Django-guardian: user.has_perm("perm", obj) berperilaku tidak terduga

Dibuat pada 3 Sep 2011  ·  28Komentar  ·  Sumber: django-guardian/django-guardian

Jika saya menggunakan metode user.has_perm("perm") standar, maka itu akan mengembalikan True saja, jika pengguna memiliki izin global "perm" .
Dan jika user.has_perm("perm", obj) digunakan, itu akan mengembalikan True jika pengguna memiliki izin untuk mengakses objek khusus ini.
Tetapi itu akan mengembalikan False , bahkan jika pengguna memiliki izin global "perm" , yang cukup tidak terduga bagi saya, karena saya berasumsi, bahwa memiliki izin global harus memberikan akses pengguna ke semua objek. Apakah saya benar?

Enhancement

Komentar yang paling membantu

Hai, bisakah Anda menempelkan setelan _AUTHENTICATION_BACKENDS_ Anda?

Karena Anda telah membaca dokumentasi Django, Anda mengetahui bahwa urutan dari backend yang ditentukan adalah penting.

Saya kira Anda memiliki sesuatu seperti berikut di aplikasi Anda:

AUTHENTICATION_BACKENDS = (
    'guardian.backends.ObjectPermissionBackend',
    'django.contrib.auth.backends.ModelBackend',
)

Atau ini:

from django.conf import global_settings
AUTHENTICATION_BACKENDS = (
    'guardian.backends.ObjectPermissionBackend',
) + global_settings.AUTHENTICATION_BACKENDS

Pastikan saja backend default ditentukan terlebih dahulu.

Bisakah Anda mengonfirmasi bahwa ini masalahnya? Jika tidak, harap tambahkan beberapa informasi lagi (mungkin Anda juga menggunakan beberapa backend lain, atau patch monyet aplikasi lain yang menggunakan metode _User.has_perm_?).

Semua 28 komentar

Saya menggali lebih sedikit dan menemukan, bahwa setiap backend izin harus bekerja secara independen, jadi seharusnya tidak ada situasi seperti yang saya jelaskan di atas. Tetapi untuk beberapa alasan memanggil user.has_perm yang menyediakan instance objek, terlihat hanya memeriksa izin tingkat objek, dan melewati pemeriksaan izin global. Saya tidak tahu, apa alasan perilaku itu. Saya menggunakan Django 1.2.5.

Hai, bisakah Anda menempelkan setelan _AUTHENTICATION_BACKENDS_ Anda?

Karena Anda telah membaca dokumentasi Django, Anda mengetahui bahwa urutan dari backend yang ditentukan adalah penting.

Saya kira Anda memiliki sesuatu seperti berikut di aplikasi Anda:

AUTHENTICATION_BACKENDS = (
    'guardian.backends.ObjectPermissionBackend',
    'django.contrib.auth.backends.ModelBackend',
)

Atau ini:

from django.conf import global_settings
AUTHENTICATION_BACKENDS = (
    'guardian.backends.ObjectPermissionBackend',
) + global_settings.AUTHENTICATION_BACKENDS

Pastikan saja backend default ditentukan terlebih dahulu.

Bisakah Anda mengonfirmasi bahwa ini masalahnya? Jika tidak, harap tambahkan beberapa informasi lagi (mungkin Anda juga menggunakan beberapa backend lain, atau patch monyet aplikasi lain yang menggunakan metode _User.has_perm_?).

Oke, sepertinya aku terlalu lelah. Urutan seharusnya tidak mempengaruhi hasil _has_perm_. Jadi harap tambahkan informasi lebih lanjut tentang pengaturan aplikasi yang Anda gunakan. Selain itu, Anda dapat memastikan bahwa wali berfungsi dengan baik dengan menjalankan rangkaian pengujian (_python manage.py test guardian_).

Oh, ok, saya sudah membaca masalah Anda sekali lagi. _auth.ModelBackend_ default TIDAK mendukung _supports_object_permissions_ (atribut itu _False_). Menurut dokumen Django, mereka akan menambahkan dukungan itu untuk backend default dari 1.4.

Jadi, untuk situasi Anda, perilaku benar-benar benar dan diharapkan. Backend default dihilangkan begitu saja.

Anda harus memeriksa izin global sebelum memeriksa izin tingkat objek di aplikasi Anda. Itu solusi paling sederhana yang bisa saya pikirkan.

Ditutup sebagai _invalid_, namun jika Anda ingin membukanya kembali dengan komentar baru - jangan ragu untuk melakukannya!

Oke, sepertinya Anda benar. Tetapi tidak memeriksa izin global membawa beberapa kesulitan serius bagi saya. Misalnya dalam guardian.decorators.permission_required , yang, seperti yang saya asumsikan, harus memperluas fungsionalitas django.contrib.auth.decorators.permission_required biasa dengan pemeriksaan izin tingkat objek tambahan.
Masalahnya adalah, saya memiliki aplikasi yang berfungsi yang menggunakan izin global, dan saya ingin menambahkan beberapa izin tingkat objek tambahan ke dalamnya, jadi saya telah mengubah dekorator permission_required default ke tingkat objek dari Django-guardian, tetapi maka pengguna telah kehilangan hak akses mereka berdasarkan izin global.

Sepertinya perilaku yang tidak terduga bagi saya, bisakah Anda setidaknya menambahkan kasus ini ke dokumentasi, karena tidak jelas tanpa melihat ke dalam kode.

Bagi saya, ini adalah cacat desain, yang mengakibatkan pemeriksaan ekstra di seluruh kode di mana saya perlu memverifikasi izin baik secara global maupun per-objek.

@coagulant : tidak akan fleksibel untuk sebenarnya _extend_ dekorator dari auth.django. Aplikasi ini ingin mengimplementasikan _object permissions_, bukan memperluas yang asli. Yaitu bagaimana jika Anda ingin menggunakan beberapa aplikasi lain yang hanya menggunakan izin tingkat wali dan objek? Bagaimana jika Anda ingin menggunakan izin global di admin saja, dan izin tingkat objek di aplikasi yang diberikan kepada pengguna biasa? Bagaimana jika aplikasi memerlukan izin global dan izin tingkat objek agar pengguna dapat melakukan beberapa tindakan pada objek? Ada banyak kasus, wali tidak akan menutupi semuanya.

Di sisi lain, saya dapat mengakui bahwa kasus penggunaan khusus ini, mungkin lebih umum daripada yang lain. Bagi siapa pun yang tertarik dengan ini - harap ajukan masalah lain yang menentukan persyaratan. Saya percaya ini dapat dengan mudah dicapai tanpa ketidakcocokan kata balik - yaitu dengan pengaturan konfigurasi baru.

Akan berguna bagi saya, jika hanya ada dekorator yang membutuhkan izin_yang ditambahkan, yang memeriksa izin tingkat objek dan izin global. Itu akan mencakup masalah saya.

@Dzejkob , dapatkah Anda memeriksa komit terakhir dan mengatakan apakah ini cukup (saya telah menambahkan tanda untuk menerima izin global). Juga, beri tahu saya jika memperluas docstring di dekorator itu sendiri sudah cukup, atau haruskah saya menambahkan lebih banyak contoh/dokumen yang lebih deskriptif?

Catatan: komit ada di cabang baru: _feature/add-accept_global_perms-flag-for-decorators_

@lukaszb Ya, saya telah menginstal Guardian versi cabang baru ke dalam proyek saya, menambahkan parameter di permission_required decorators accept_global_perms=True , dan tampaknya berfungsi sebagaimana mestinya. Jadi terima kasih telah membuat fitur ini. Saya pikir itu ide yang baik untuk menggabungkannya ke dalam bagasi.
Tentang dokumen, mereka cukup jelas bagi saya, jadi saya pikir itu cukup.

Ini sudah lama diperbaiki.

Halo, saya menemukan masalah ini saat saya mencoba jango-guardian dan saya menemukan perilaku ini bermasalah/sangat membingungkan.

Saya menambahkan izin global (sebut saja view_user ) ke pengguna (sebut saja joe ) dan kemudian memeriksa apakah Joe dapat melihat pengguna tertentu (sebut saja other_user ) dan secara mengejutkan ia mengembalikan False .

joe = User.objects.get(username="joe")
other_user = User.objects.get(username="other_user")
assign_perm("myapp.view_user", joe)
joe.has_perm("myapp.view_user") # True as expected
joe.has_perm("myapp.view_user", other_user) # False, whaaaat?

Sekarang, saya tidak menggunakan dekorator permission_required secara eksplisit karena set tampilan saya "secara otomatis" memeriksa izin karena REST_FRAMEWORK/DEFAULT_PERMISSION_CLASSES dan AUTHENTICATION_BACKENDS saya di settings.py . Bagaimana saya memberitahu wali untuk berperilaku seperti yang diharapkan?

(Maaf jika saya melewatkan sesuatu yang sangat bodoh, ini semacam hari 2/3 di Djangoland)

Maaf atas kebisingan hari ini, bahkan lebih menarik/membingungkan bahwa guardian.shortcuts.get_objects_for_user() menghormati izin auth/global secara default ( accept_global_perms ).

accept_global_perms: if True takes global permissions into account. 
[...]
Default is True.

Meskipun, masalah ini ditutup demi yang lain, ada banyak wawasan di sini, jadi saya menulis di sini untuk membangkitkan kembali percakapan. Zen dari Python berkata:

Harus ada satu --dan sebaiknya hanya satu-- cara yang jelas untuk melakukannya.

Dan bagi saya itu adalah _fallback ke global ketika lokal (tingkat objek) tidak ditentukan_. Urutan tidak mengubah hasil (sejak Django returns False if obj is not None ), tetapi dapat mengubah kinerja.

Saya setuju bahwa memeriksa ulang seluruh kode bukanlah desain yang baik, dan dalam beberapa hal juga bertentangan dengan prinsip KERING. Namun, fungsi implementasi wali yang tersedia dalam backend default Django juga tidak KERING. Django itu mengizinkan banyak backend, dan memeriksa satu per satu secara berurutan menunjukkan bahwa backend seharusnya bermain bersama, bukan menggantikan satu sama lain. Jika ini benar, maka Django menolak untuk memeriksa global ketika obj is not None adalah _salah_. Jika Django mengabaikan obj , itu bisa menjadi fallback global untuk pemeriksa objek.

Saya pikir kita harus membuka tiket dengan Django, menanyakan apakah, dan bagaimana, backend otorisasi bermain satu sama lain, dan kemudian pergi dari sana.

Dengan itu, saya pikir Django mengubah perilakunya sangat tidak mungkin (walaupun saya masih berpikir kita harus bertanya), jadi status quo menunjukkan bahwa, wali perlu berevolusi untuk dapat memberikan keduanya tergantung pada mana yang diinginkan (yang mana mungkin diatur baik melalui pengaturan atau argumen ke fungsi). Tetapi perilaku default yang menurut saya harus menjadi _local fallback ke global ketika false_ di seluruh papan.

Saya lebih suka jika Django lebih suka sistem izin tri-negara: Benar, Salah, dan Tidak Ada. Dalam hal ini, pemeriksa lokal dapat _mengganti_ global melalui False ; dan melalui None setiap backend dapat mengatakan: "Saya tidak tahu, tanyakan baris berikutnya". Dalam kasus itu, Django akan berhenti memeriksa setelah mendapatkan True or False di salah satu backend, dan berhenti membuang daya pemrosesan.

Ini akan memberi setiap backend lebih banyak kekuatan: dapat memberikan jawaban yang pasti, atau merujuk ke yang lain.

@doganmeh Django mengimplementasikan sistem izin tri-negara (setidaknya pada 1.10). Pilihannya adalah True, None, dan meningkatkan PermissionDenied. Melakukan yang belakangan akan menyebabkan Django berhenti memeriksa dan mengembalikan False.

Mengenai masalah yang dihadapi, saya yakin ini adalah masalah Contrib.Auth. Itu adalah backend yang berhubungan dengan 'izin global'. Masalahnya adalah mereka telah membuat konvensi tidak langsung yang has_perm dengan obj=None hanya memeriksa 'izin tabel' dan has_perm dengan obj hanya memeriksa 'izin baris'. Mereka tidak mungkin mengubahnya, tetapi _harus_ terbuka untuk memperluas API mereka guna mendukung pemeriksaan keduanya.

(Setidaknya saya berharap mereka akan terbuka untuk menambahkan perilaku untuk memeriksa keduanya. Tampaknya cacat yang jelas dalam sistem mereka.)

Apa saran Anda untuk API 'periksa keduanya'? Yang terbaik yang bisa saya pikirkan adalah seorang kwarg.

Saya sedang berbicara tentang sistem tristate eksplisit di mana akan ada NullBooleanField di tabel Izin. Benar, jika Anda menganggap kurangnya True (atau kurangnya izin) sebagai None , itu dapat dianggap sebagai tristate. Dalam hal ini izin yang tidak ditentukan oleh wali harus dianggap sebagai None , dan keputusan harus didelegasikan ke global. Jika saya punya pilihan, saya akan mengambil desain eksplisit.

@airstandley Saya kira maksud Anda tambahan kwarg ke user.has_perm seperti:

def has_perm(self, perm, obj=None, fallback=False)

Saya pikir itu akan membantu. Dalam kasus ketika fallback=True , guardian akan memanggil Django backend dan mengembalikan global. Jika saya punya pilihan, saya lebih suka fallback ke Django ditangani secara alami oleh kerangka kerja, bukan dengan membajak internalnya.

@doganmeh Maaf saya salah bicara. Itu seharusnya membaca "Benar, Salah , dan meningkatkan Izin Ditolak". Bukan:

Opsinya adalah True, None, dan meningkatkan PermissionDenied

Nanti adalah bagaimana saya memikirkan pengembalian di kepala saya ...

Saya pikir saya mungkin juga salah mengerti apa yang Anda maksud dengan

Saya lebih suka jika Django lebih menyukai sistem izin tri-negara

Untuk memperjelas, Anda berbicara tentang implementasi Model Izin secara khusus dan bukan sistem secara keseluruhan?

Maksud saya adalah bahwa sistem backend auth memungkinkan jalan pintas yang tepat yang Anda jelaskan (Setidaknya untuk has_perm , has_module_perms api). Ini eksplisit, jika tidak langsung:
Setiap backend dapat membuat keputusan itu sendiri (mengembalikan True atau PermissionDenied), atau mendelegasikan keputusan ke rantai berikutnya yang didukung (mengembalikan False). Itu adalah keputusan khusus implementasi, dan bukan kualitas sistem itu sendiri.

ObjectPermissionBackend eksplisit akan menjadi masalah untuk proyek saya, jadi saya akan memilih untuk tidak eksplisit. ("Kejelasan" membuat integrasi dengan backend pemeriksaan izin lain menjadi rumit.) Saya menduga bahwa, seperti Anda, orang lain lebih suka itu eksplisit. Jadi memiliki 'eksplisititas' sebagai pengaturan masuk akal bagi saya.

@doganmeh
Jadi tentang kwarg.

Pertama saya terus khawatir kita tidak berada di halaman yang sama tentang bagaimana perilaku sistem backend auth dimaksudkan untuk bekerja. Jadi untuk menjadi jelas:
Pemahaman saya adalah bahwa tujuannya adalah agar backend bekerja bersama. Jika sebuah aplikasi ingin menggunakan sistem Izin auth (yaitu 'izin global', meskipun secara teknis itu akan menjadi 'izin tabel' tetapi kentang, kentang) itu akan menginstal ModelBackend auth di AUTH_BACKENDS. Jika aplikasi itu ingin menggunakan sistem ObjectPermission wali (yaitu 'izin baris'), itu akan menginstal ObjectPermissionBackend wali di AUTH_BACKENDS.
ModelBackend akan menangani izin global.
ObjectPermissionBackend akan menangani izin objek.
jika pengguna hanya memiliki izin objek, ModelBackend tidak akan pernah mengembalikan True untuk has_perm . Jika pengguna hanya memiliki izin global ObjectPermissionBackend tidak akan pernah mengembalikan True untuk has_perm . Dalam kedua kasus, panggilan ke user.has_perm(perm, obj) harus tetap bernilai true, karena pengguna memang memiliki izin untuk _one_ dari backend yang diinstal. (Ini adalah masalah yang kami alami karena ModelBackend gagal di akun ini)

Ok jadi mengingat semua itu.

Di dunia yang ideal di mana kompatibilitas tidak menjadi masalah, saya juga lebih suka perubahan sederhana untuk ModelBackend contrib.auth. ModelBackend.has_perm(user_obj, perm, obj=None) harus mengembalikan True jika pengguna memiliki 'izin global' yang ditentukan oleh perm; terlepas dari apakah obj adalah None atau tidak.

Namun kompatibilitas adalah masalah, itulah sebabnya saya bertanya apakah Anda memiliki solusi untuk masalah tersebut.

Satu-satunya solusi _backwards compatible_ yang dapat saya pikirkan adalah menambahkan kwarg ke API atau menambahkan pengaturan AUTH.

Jadi kwargs:
Sebut saja obj_shortcut karena itulah yang terbaik yang bisa saya pikirkan. object_shortcut akan default ke True. Jika object_shortcut adalah True maka backend harus berperilaku seperti yang dilakukan ModelBackend sekarang: mereka harus _only_ memeriksa izin 'tabel/global' jika obj adalah Tidak Ada, jika tidak, mereka harus _only_ memeriksa izin 'baris/objek'. Namun jika object_shortcut adalah False maka backend harus berperilaku seperti yang kita inginkan: mereka akan memeriksa _both_ global dan izin objek ketika objek bukan None. Kemudian add-on seperti Guardian selalu dapat menyediakan mixin dengan metode has_perm dengan object_shortcut=False sebagai default. user.has_perm(perm, obj, object_shortcut=False) akan mengembalikan True dengan benar jika Izin yang diwakili oleh perm ditetapkan ke Pengguna, sementara panggilan lawas ke user.has_perm(perm, obj) akan terus mengembalikan False.

Pengaturan akan memiliki hasil yang sama tetapi pada dasarnya akan menghasilkan peralihan di ModelBackend._get_permissions .

if not user_obj.is_active or user_obj.is_anonymous or obj is not None:
    return set()

akan menjadi sesuatu seperti:

if not user_obj.is_active or user_obj.is_anonymous or (obj is not None and legacy_behaviour_setting_is _on):
    return set()

Tidak yakin mana yang lebih baik. Saya pikir pengaturan lebih bersih, tetapi saya yakin pengembang Django akan lebih memenuhi syarat untuk menentukan itu.

Intinya adalah masalah itu mungkin untuk diperbaiki, dan saya sangat yakin bahwa masalahnya adalah bug Django, bukan Guardian.

@airstandley Anda benar, False atau None tidak masalah. Keduanya berarti saya tidak tahu . Akhirnya jika tidak ada yang tahu, izin tidak diberikan.

Saya biasanya pada halaman yang sama dengan Anda, meskipun Django memaksa backend untuk berperilaku satu atau lain cara tampaknya menurunkan kelonggaran. Saya pikir itu harus memiliki mekanisme untuk memungkinkan setiap backend mendapatkan informasi yang mereka butuhkan untuk menyelesaikan pekerjaan mereka. Saya bersama Anda pada argumen kata kunci, tetapi tidak hanya memaksa satu atau lain cara.

Percakapan ini sepertinya membuang-buang waktu, tetapi itu membantu saya mengklarifikasi pada diri sendiri bahwa tidak ada izin yang diberikan pada wali bukan penolakan, itu hanya kurangnya informasi.

Bagaimanapun, saya membuat permintaan tarik #546. Silakan periksa dan beri tahu saya pendapat Anda.

Saya membuat tiket dengan Django: https://code.djangoproject.com/ticket/29012 , dan bertanya-tanya apa yang akan mereka katakan.

Sepertinya ada beberapa tiket lain dengan Django: https://code.djangoproject.com/ticket/20218

Aku menutup milikku.

@doganmeh
Saya suka arah yang Anda tuju dengan #546. Jika itu akan membantu, saya harus punya waktu untuk memeriksa sisa unit dan menulis beberapa tes untuk fallback ini akhir pekan ini.

Pada garis singgung. Komentar Anda tentang "Django memaksa backend" untuk berperilaku dengan cara tertentu membingungkan saya, tetapi juga memberi saya pemikiran. Backend dapat berperilaku sesuka mereka; kekhawatiran awal saya tentang ObjectPermissionBackend Guardian berasal dari dokumentasi yang menyarankan itu dirancang untuk berjalan bersama ModelBackend Auth. Guardian dapat menawarkan beberapa backend, satu dirancang untuk bekerja dengan ModelBackend, dan satu dirancang untuk bekerja sendiri. (IE yang baru saja memeriksa tabel UserObjectPermission/GroupObjectPermission Guardian, yang lain memeriksa tabel UserObjectPermission/GroupObjectPermission dan tabel Izin Auth.)
Secara pribadi saya lebih suka pendekatan saat ini dengan pengaturan dan kwargs. Saya pikir kelemahan utama dari pendekatan backend ganda adalah menjadi tidak jelas bagaimana jalan pintas dan fungsi kenyamanan harus berperilaku.

Melihat kiriman ke milis Django dev. Saya berharap mereka menerima itu, atau setuju untuk mengubah perilaku dengan cara yang tidak sesuai. Batasan saat ini pada API hanya kikuk.

Bisakah Anda mendukungnya di sana? 😊

Dikirim dari Mail untuk Windows 10

Dari: airstandley
Dikirim: Jumat, 12 Januari 2018 12:06
Kepada: django-guardian/django-guardian
Cc: Mehmet Dogan; Menyebutkan
Subjek: Re: [django-guardian/django-guardian] user.has_perm("perm", obj)berperilaku tidak terduga (#49)

Melihat kiriman ke milis Django dev. Saya berharap mereka menerima itu, atau setuju untuk mengubah perilaku dengan cara yang tidak sesuai. Batasan saat ini pada API hanya kikuk.

Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub, atau matikan utasnya.

Setelah menangani masalah ini selama sekitar satu minggu sekarang, saya menjadi lebih yakin bahwa setiap backend hanya perlu melakukan satu hal, yaitu izin objek untuk Guardian. Agar itu terjadi, Django harus memperlakukan obj s dengan lebih baik.

Ada tambalan yang saya kirim ke Django untuk itu: https://github.com/django/django/pull/9581 (tolong beri komentar jika Anda bisa). Yang membuatnya masuk, kita dapat membersihkan pengambilan izin model di mana pun ada di Guardian, dan cukup melakukan panggilan ke backend default.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

Allan-Nava picture Allan-Nava  ·  35Komentar

g-as picture g-as  ·  10Komentar

lukaszb picture lukaszb  ·  14Komentar

brianmay picture brianmay  ·  16Komentar

ad-m picture ad-m  ·  13Komentar