Если я использую стандартный метод user.has_perm("perm")
, он вернет только True
, если у пользователя есть глобальное разрешение "perm"
.
И если используется user.has_perm("perm", obj)
, он вернет True
, если у пользователя есть разрешение на доступ к этому конкретному объекту.
Но он вернет False
, даже если у пользователя есть глобальное разрешение "perm"
, что довольно неожиданно для меня, потому что я предполагаю, что наличие глобального разрешения должно давать пользователю доступ ко всем объектам. Я прав?
Я еще немного поковырялся и выяснил, что каждый бэкенд разрешений должен работать независимо, так что ситуации, описанной выше, быть не должно. Но по какой-то причине вызов user.has_perm
, предоставляющий экземпляр объекта, проверяет только разрешения на уровне объекта и пропускает глобальную проверку разрешений. Я понятия не имею, в чем причина такого поведения. Я использую Джанго 1.2.5.
Здравствуйте, не могли бы вы вставить настройку _AUTHENTICATION_BACKENDS_?
Поскольку вы уже читали документацию Django, вы знаете, что порядок указанных бэкэндов важен.
Я полагаю, у вас есть что-то вроде следующего в вашем приложении:
AUTHENTICATION_BACKENDS = (
'guardian.backends.ObjectPermissionBackend',
'django.contrib.auth.backends.ModelBackend',
)
Или это:
from django.conf import global_settings
AUTHENTICATION_BACKENDS = (
'guardian.backends.ObjectPermissionBackend',
) + global_settings.AUTHENTICATION_BACKENDS
Просто убедитесь, что сначала указан бэкэнд по умолчанию.
Вы можете подтвердить, что это проблема? Если нет, добавьте дополнительную информацию (может быть, вы также используете какие-то другие бэкенды или какие-то другие патчи для обезьян приложений, которые используют метод _User.has_perm_?).
Ладно, кажется, я слишком устал. Порядок не должен влиять на результат _has_perm_. Пожалуйста, добавьте больше информации об используемых вами настройках приложения. Кроме того, вы можете убедиться, что Guardian работает правильно, запустив набор тестов (_python manage.py test guardian_).
О, хорошо, я прочитал ваш вопрос еще раз. По умолчанию _auth.ModelBackend_ НЕ поддерживает _supports_object_permissions_ (этот атрибут имеет значение _False_). Согласно документам Django, они добавят эту поддержку бэкенда по умолчанию из версии 1.4.
Итак, для вашей ситуации поведение абсолютно правильное и ожидаемое. Бэкэнд по умолчанию просто опущен.
Вы должны проверить глобальные разрешения, прежде чем проверять разрешения на уровне объекта в своем приложении. Это самое простое решение, которое я могу придумать.
Закрытие как _invalid_, однако, если вы хотите снова открыть его с новым комментарием - не стесняйтесь делать это!
Хорошо, похоже, ты прав. Но отсутствие проверки глобальных разрешений создает для меня серьезные трудности. Например в guardian.decorators.permission_required
, что, как я предполагал, должно расширять функциональность обычного django.contrib.auth.decorators.permission_required
с дополнительной проверкой разрешений на уровне объекта.
Проблема в том, что у меня есть работающее приложение, которое использует глобальные разрешения, и я хотел добавить к нему некоторые дополнительные разрешения на уровне объекта, поэтому я изменил декораторы permission_required
по умолчанию на уровень объекта из django-guardian, но затем пользователи потеряли свои права доступа на основе глобальных разрешений.
Для меня это выглядит неожиданным поведением, не могли бы вы хотя бы добавить этот случай в документацию, потому что это неочевидно без изучения кода.
Что касается меня, это недостаток дизайна, приводящий к дополнительной проверке всего кода, где мне нужно проверить разрешение как глобально, так и для каждого объекта.
@Коагулянт : было бы не очень гибко на самом деле _расширять_ декораторы из авторизации Джанго. Это приложение намерено реализовать _разрешения объекта_, а не расширять исходное. То есть, что, если вы хотите использовать какое-то другое приложение, которое использует только разрешения уровня опекуна и объекта? Что, если вы хотите использовать глобальные разрешения только для администратора и разрешения на уровне объекта в приложениях, предоставленные обычным пользователям? Что, если приложению потребуется как глобальное разрешение, так и разрешение на уровне объекта, чтобы пользователь мог выполнять какие-либо действия с объектом? Случаев много, опекун не охватил бы их все.
С другой стороны, я могу признать, что этот конкретный вариант использования может быть более распространенным, чем другие. Для всех, кто заинтересован в этом - пожалуйста, отправьте другой вопрос с указанием требований. Я считаю, что это может быть легко достигнуто без несовместимости обратных слов - т.е. с новой настройкой конфигурации.
Мне было бы полезно, если бы был добавлен еще один декоратор permission_required, который проверяет как разрешения уровня объекта, так и глобальные. Это покроет мою проблему.
@Dzejkob , не могли бы вы проверить последний коммит и сказать, будет ли этого достаточно (я добавил флаг для принятия глобальных разрешений). Кроме того, дайте мне знать, достаточно ли расширения строки документации в самом декораторе, или мне следует добавить больше примеров/более описательных документов?
Примечание: коммит находится в новой ветке: _feature/add-accept_global_perms-flag-for-decorators_
@lukaszb Да, я установил новую ветку Guardian в свой проект, добавил параметр в декораторы $ permission_required
accept_global_perms=True
, и, похоже, он работает как надо. Так что спасибо за создание этой функции. Я думаю, что это хорошая идея, чтобы слить его в багажник.
Насчет документов они мне вполне понятны, так что думаю хватит.
Это было исправлено давно.
Здравствуйте, я обнаружил эту проблему, когда пробовал jango-guardian, и обнаружил, что это поведение ошибочно/очень запутанно.
Я добавил глобальное разрешение (назовем его view_user
) пользователю (назовем его joe
), а затем проверил, может ли Джо просматривать определенного пользователя (назовем его other_user
) и неожиданно он вернул 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?
Теперь я не использую декоратор permission_required
явно, поскольку мои наборы представлений «автоматически» проверяют разрешения из-за моих REST_FRAMEWORK/DEFAULT_PERMISSION_CLASSES
и AUTHENTICATION_BACKENDS
в settings.py
. Как мне сказать опекуну, чтобы он вел себя так, как ожидалось?
(Извините, если я пропустил что-то действительно глупое, это своего рода день 2/3 в djangoland)
Извините за шум сегодня, еще более интересно/запутанно то, что guardian.shortcuts.get_objects_for_user()
по умолчанию соблюдает авторизационные/глобальные разрешения ( accept_global_perms
).
accept_global_perms: if True takes global permissions into account.
[...]
Default is True.
Хотя этот вопрос был закрыт в пользу другого, здесь есть много инсайтов, поэтому я пишу сюда, чтобы возобновить разговор. Дзен Питона говорит:
Должен быть один — и желательно только один — очевидный способ сделать это.
И для меня это _откат к глобальному, когда локальный (уровень объекта) не указан_. Порядок не меняет результат (начиная с Django returns False if obj is not None
), но может изменить производительность.
Я согласен с тем, что двойные проверки по всему коду — это нехороший дизайн, и в некотором смысле это противоречит принципу DRY. Тем не менее, функция реализации Guardian, доступная в бэкэнде Django по умолчанию, также не является DRY. То, что Django допускает использование нескольких бэкэндов и проверяет их по порядку, указывает на то, что бэкенды должны работать вместе, а не заменять друг друга. Если это правильно, то Django отказывается проверять глобальные переменные, когда obj is not None
_неправильно_. Если бы Django проигнорировал obj
, это могло бы стать глобальным запасным вариантом для средств проверки объектов.
Я думаю, мы должны открыть тикет с Django, спросив, взаимодействуют ли серверы авторизации друг с другом, и если да, то как, а затем исходить из этого.
При этом я думаю, что изменение поведения Django маловероятно (хотя я все еще думаю, что мы должны спросить об этом), поэтому статус-кво указывает на то, что Guardian должен развиваться, чтобы быть в состоянии обеспечить и то, и другое в зависимости от того, какой из них желателен (какой). может быть установлен либо через настройки, либо через аргумент функции). Но поведение по умолчанию, я думаю, должно быть _local резервным к глобальному, когда false_ по всем направлениям.
Мне бы очень понравилось, если бы Django предпочел систему разрешений с тремя состояниями: True, False и None. В этом случае локальные чекеры могут _переопределить_ глобальные переменные через False
; и через None
каждый бэкенд мог сказать: «Я этого не знаю, спросите следующего в очереди». В этом случае Django прекратит проверку после получения True or False
в одном из бэкендов и перестанет тратить вычислительную мощность впустую.
Это дало бы каждому бэкенду больше возможностей: он мог бы дать окончательный ответ или сослаться на других.
@doganmeh Django реализует систему разрешений с тремя состояниями (по крайней мере, с версии 1.10). Возможные варианты: True, None и повышение PermissionDenied. Выполнение последнего приведет к тому, что Django прекратит проверку и вернет False.
Что касается рассматриваемой проблемы, я считаю, что это проблема Contrib.Auth. Это серверная часть, которая имеет дело с «глобальными разрешениями». Проблема в том, что они установили косвенное соглашение, согласно которому has_perm с obj=None проверяет только «разрешения таблицы», а has_perm с obj проверяет только «разрешения строк». Они вряд ли изменят это, но _должны_ быть открыты для расширения своего API, чтобы поддерживать проверку обоих.
(По крайней мере, я надеюсь, что они будут готовы добавить поведение для проверки обоих. Это кажется явным недостатком в их системе.)
Каковы ваши предложения по API «проверить оба»? Лучшее, о чем я могу думать, это kwarg.
Я говорил о явной системе с тремя состояниями, где в таблице разрешений будет NullBooleanField. Правда, если вы принимаете отсутствие True
(или отсутствие разрешения) как None
, это можно рассматривать как три состояния. В этом случае разрешения, не указанные опекуном, должны быть приняты как None
, а решение должно быть делегировано глобалу. Если бы у меня был выбор, я бы выбрал явный дизайн.
@airstandley Я думаю, вы имеете в виду добавление kwarg к user.has_perm
, например:
def has_perm(self, perm, obj=None, fallback=False)
Я думаю, это поможет. В случае, когда fallback=True
, опекун вызовет бэкэнд Django и вернет файл global. Однако, если бы у меня был выбор, я бы предпочел, чтобы откат к Django естественным образом обрабатывался фреймворком, а не взламывал его внутренности.
@doganmeh Извините, я оговорился. Это должно было читаться «True, False и повышение PermissionDenied». Нет:
Возможные варианты: True, None и поднятие PermissionDenied.
Позже я думаю о возврате в своей голове...
Я думаю, что я, возможно, также неправильно понял, что вы имели в виду под
Я бы предпочел, чтобы Джанго предпочитал систему разрешений с тремя состояниями.
Чтобы уточнить, вы говорили о реализации модели разрешений конкретно, а не о системе в целом?
Моя точка зрения заключалась в том, что бэкэнд-система аутентификации допускает точное сокращение, которое вы описали (по крайней мере, для has_perm
, has_module_perms
API). Это явно, если не прямо:
Любой сервер может либо сам принять решение (вернуть True или PermissionDenied), либо делегировать решение следующему серверу в цепочке (вернуть False). Это решение зависит от реализации, а не от качества самой системы.
Явный ObjectPermissionBackend был бы проблемой для моего проекта, поэтому я бы предпочел не указывать его явно. («Явность» делает интеграцию с другими механизмами проверки разрешений громоздкой.) Я подозреваю, что, как и вы, другие предпочли бы, чтобы она была явной. Поэтому наличие «явности» в качестве настройки имеет для меня смысл.
@доганмех
Итак, о кварге.
Во-первых, я продолжаю беспокоиться, что мы не согласны с тем, как должно работать поведение серверной системы аутентификации. Итак, чтобы было ясно:
Насколько я понимаю, намерение состояло в том, чтобы серверные части работали вместе. Если приложение хочет использовать систему разрешений авторизации (то есть «глобальные разрешения», хотя технически это будут «разрешения для таблиц», но картошка, картошка), оно установит auth ModelBackend в AUTH_BACKENDS. Если бы это приложение хотело использовать систему ObjectPermission опекуна (т. е. «разрешение на строку»), оно не использовало бы ObjectPermissionBackend опекуна в AUTH_BACKENDS.
ModelBackend будет обрабатывать глобальное разрешение.
ObjectPermissionBackend будет обрабатывать разрешения объекта.
если бы у пользователя было только разрешение на объект, ModelBackend никогда бы не вернул True для has_perm
. Если бы у пользователя было только глобальное разрешение, ObjectPermissionBackend никогда бы не вернул True для has_perm
. В обоих случаях вызов user.has_perm(perm, obj) должен по-прежнему возвращать значение true, поскольку у пользователя есть разрешение для _one_ установленных бэкендов. (Это мы столкнулись с проблемой из-за сбоя ModelBackend в этой учетной записи)
Хорошо, учитывая все это.
В идеальном мире, где совместимость не является проблемой, я бы тоже предпочел простое изменение ModelBackend contrib.auth. ModelBackend.has_perm(user_obj, perm, obj=None)
должен возвращать True, если у пользователя есть «глобальное разрешение», указанное в perm; независимо от того, является ли obj None или нет.
Однако совместимость является проблемой, поэтому я спрашиваю, есть ли у вас решение этой проблемы.
Единственные _обратно совместимые_ решения, которые я могу придумать, это либо добавить kwarg в API, либо добавить параметр AUTH.
Итак, варги:
Давайте назовем это obj_shortcut
, потому что это лучшее, что я могу придумать. object_shortcut
по умолчанию будет иметь значение True. Если object_shortcut
равно True, то бэкенды должны вести себя так же, как сейчас делает ModelBackend: они должны _только_ проверять разрешения "таблица/глобальные", если obj имеет значение None, в противном случае они должны _только_ проверять разрешения "строка/объект". Однако, если object_shortcut
имеет значение False, серверные части должны вести себя так, как мы оба предпочли бы: они будут проверять _и глобальные, и объектные разрешения, когда объект не равен None. Тогда надстройки, такие как Guardian, всегда могут предоставить миксин с методом has_perm
с object_shortcut=False
по умолчанию. user.has_perm(perm, obj, object_shortcut=False)
будет правильно возвращать True, если Разрешение, представленное perm, было назначено Пользователю, в то время как устаревшие вызовы user.has_perm(perm, obj)
будут по-прежнему возвращать False.
Настройка будет иметь тот же результат, но в основном приведет к переключению в ModelBackend._get_permissions
.
if not user_obj.is_active or user_obj.is_anonymous or obj is not None:
return set()
станет чем-то вроде:
if not user_obj.is_active or user_obj.is_anonymous or (obj is not None and legacy_behaviour_setting_is _on):
return set()
Не уверен, что лучше. Я думаю, что настройка чище, но я уверен, что разработчики Django будут более квалифицированы, чтобы определить это.
Дело в том, что проблему можно исправить, и я твердо верю, что проблема связана с ошибкой Django, а не с Guardian.
@airstandley Вы правы, False
или None
не имеет значения. Они оба означают , что я не знаю . В конце концов, если никто не знает, разрешение не дается.
Я, как правило, на одной странице с вами, хотя django, заставляющий бэкэнды вести себя так или иначе, похоже, ухудшает слабосвязанность. Я думаю, что у него должны быть механизмы, позволяющие каждому бэкэнду получать информацию, необходимую им для выполнения своей работы. Я с вами по аргументу(ам) ключевого слова, но не просто форсирую так или иначе.
Этот разговор кажется пустой тратой времени, но он помог мне уяснить себе, что отсутствие разрешения у опекуна — это не отказ, а просто отсутствие информации.
В любом случае, я сделал запрос на включение # 546. Пожалуйста, проверьте это и дайте мне знать, что вы думаете.
Я создал тикет с помощью django: https://code.djangoproject.com/ticket/29012 , и мне интересно, что они скажут.
Кажется, были и другие билеты с django: https://code.djangoproject.com/ticket/20218
Я закрыл свой.
@доганмех
Мне нравится направление, в котором вы идете с # 546. Если это поможет, у меня будет время просмотреть остальные модули и написать несколько тестов для этих запасных вариантов в эти выходные.
По касательной. Ваш комментарий о том, что «Django заставляет бэкэнды вести себя определенным образом», смущает меня, но также заставил меня задуматься. Бэкенды могут вести себя так, как им хочется; мое первоначальное беспокойство по поводу ObjectPermissionBackend от Guardian связано с документацией, предполагающей, что он был разработан для работы вместе с ModelBackend от Auth. Guardian может предложить несколько бэкендов, один из которых предназначен для работы с ModelBackend, а другой — для работы в одиночку. (IE, который просто проверяет таблицы UserObjectPermission/GroupObjectPermission Guardian, другой проверяет обе таблицы UserObjectPermission/GroupObjectPermission и таблицу разрешений Auth.)
Лично я предпочитаю текущий подход с настройкой и kwargs. Я думаю, что основным недостатком подхода с несколькими серверными частями будет то, что становится неясным, как должны вести себя ярлыки и удобные функции.
Видел сообщение в списке рассылки разработчиков Django. Я надеюсь, что они примут это или согласятся изменить поведение несовместимым образом. Текущее ограничение на API просто неуклюже.
Можете ли вы поддержать его там? 😊
Отправлено из почты для Windows 10
Откуда: Airstandley
Отправлено: пятница, 12 января 2018 г., 12:06.
Кому: джанго-хранитель/джанго-хранитель
Копия: Мехмет Доган; Упомянуть
Тема: Re: [django-guardian/django-guardian] user.has_perm("perm", obj) ведет себя неожиданно (#49)
Видел сообщение в списке рассылки разработчиков Django. Я надеюсь, что они примут это или согласятся изменить поведение несовместимым образом. Текущее ограничение на API просто неуклюже.
—
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub или отключите ветку.
Поработав над этой проблемой около недели, я пришел к более твердому убеждению, что каждый бэкэнд должен делать только одну вещь, а именно права доступа к объектам для Guardian. Для этого Django должен обращаться с obj
более аккуратно.
Для этого я отправил Django патч: https://github.com/django/django/pull/9581 (пожалуйста, прокомментируйте, если можете). Те, которые это сделают, мы можем очистить получение разрешений модели, где бы они ни находились в Guardian, и просто сделать вызов к серверной части по умолчанию.
Самый полезный комментарий
Здравствуйте, не могли бы вы вставить настройку _AUTHENTICATION_BACKENDS_?
Поскольку вы уже читали документацию Django, вы знаете, что порядок указанных бэкэндов важен.
Я полагаю, у вас есть что-то вроде следующего в вашем приложении:
Или это:
Просто убедитесь, что сначала указан бэкэнд по умолчанию.
Вы можете подтвердить, что это проблема? Если нет, добавьте дополнительную информацию (может быть, вы также используете какие-то другие бэкенды или какие-то другие патчи для обезьян приложений, которые используют метод _User.has_perm_?).