Comments 16
Кто вам мешает понаделывать экстеншинов к контексту которые возвращают IQueryable?
Context.GetPermittedUsers();
Context.GetPermittedOrders(BusinessRole.Manager);
Все аккуратненько, в одном месте без дополнительных абстракций, глобальности, сайд эффектов и легко трекается решарпером.
Context.GetPermittedUsers()
. Как проконтролировать, что другой разработчик по ошибке не вызовет Context.Users
? В варианте с дополнительным интерфейсом можно вообще не подключать EF
к web-проекту и работать только через слой бизнес-логики. Еще на extension'ы не повесить декораторы.Не усложняйте себе и другим жизнь. Все должно быть явно и легко трекаться средствами разработки.
Global filters, еще та палка о двух концах. Мило, но бесполезно. Отрубить фильтр может каждый, а вот оттрекать это нереально.
Что вы имеете в виду, когда говорите «оттрекать»?
DbSet
напрямую — фильтров нет. Используете абстракцию — фильтры есть. Если используете глобальные фильтры DbContext.Set<T>()
— с фильтрами, DbContext.Set<T>().IgnoreQueryFilters()
— без.Для меня это выглядит как: мы вам даем сомнительную возможность отфильтровать гарантировано, но оставили лазейку. И кто-то таки выстрелит себе в ногу.
Как насчет нарезать доступ контролировано, я про свой сампл Context.GetPermittedOrders(BusinessRole.Manager)? Ваше же решение режет энтити на корню.
Имея большой опыт разработки, дам простой совет: чем проще, тем лучше. И в поддержке и в выявлении багов. Как раз разбираюсь с одним багом, который неявно вытекает из-за использования сомнительного решения по трансформациии дерева выражений перед отправкой его Query Provider. До сих пор теряюсь в догадках — зачем! Чем меньше динамики, тем приложение стабильнее.
Я в подобных случаях использую спецификации
queryable.Where(spec)
.Вообще, ограничения доступа которые вы описали в статье, это больше похоже на бизнесовые правила и им место скорее в спецификациях, а не на глобальном уровне. Применяя их на глобальном уровне вы можете столкнутся с ситуацией когда вам нужно получить данные не применяя эти правила.
А что бы не забывать применить спецификации, давайте им понятные названия и объединяйте их в большие. Например, в вашем случае, можно создать спецификации ClientOrganizationsSpec
и StaffingAgencyOrganizationsSpec
и объединить в общую UserOrganizationsSpec
.
Остальные правила которые вы описываете в Where
, так же можно описать через спецификации и в результате в контроллере вы будете передавать 1-3 спецификации.
На мой взгляд, проще ориентироваться в наборе спецификаций имеющих четкое название и четкое назначение чем в куче условий в Where
. Тем более что со спецификациями вы уже имели дело. Кстати говоря, как ваше впечатление от них спустя год?
Вообще, ограничения доступа которые вы описали в статье, это больше похоже на бизнесовые правила и им место скорее в спецификациях, а не на глобальном уровне. Применяя их на глобальном уровне вы можете столкнутся с ситуацией когда вам нужно получить данные не применяя эти правила.Да и тогда нужно будет обращаться к другому контексту, который возвращает не «отфильтрованный»
IQueryable
. Кроме того этот случай решается «супер-пользователем», который видит все и может логиниться от имени кого угодно. Де-факто такой пользователь почти всегда появляется:)На самом деле ничего не мешает использовать спецификации внутри таких фильтров. Я просто не стал усложнять код в примере.Да, но если эти данные вообще не предназначены для данного пользователя зачем ему вообще иметь возможность их видеть? Неявность имеет не только плохую, но и хорошую сторону. В некоторых случаях хорошо, что прикладной разработчик не заботится о доступе. Снижает нагрузку на мозг.
На мой взгляд, проще ориентироваться в наборе спецификаций имеющих четкое название и четкое назначение чем в куче условий в Where. Тем более что со спецификациями вы уже имели дело.
Кстати говоря, как ваше впечатление от них спустя год?Короткий ответ — отлично. Если хотите длинный ответ, то <a href=«www.youtube.com/watch?v=DD3w66Ff8Ms&t=948s.
Кроме того этот случай решается «супер-пользователем»
На мой взгляд, "супер-пользователь" это серьезный удар по безопасности.
В своей практике я не припомню случаев необходимости в таких пользователях.
- Персональный пользователь с правами администратора – Да
- Временный пользователь для функциональных тестов – Да
- Пользователь с ограниченными правами для ботов – Да
Но не супер-пользователь.
Да, но если эти данные вообще не предназначены для данного пользователя зачем ему вообще иметь возможность их видеть?
Моё предложение в том, что бы использовать базовую спецификацию проверяющую права и расширять её при необходимости. В этом случае вы в явном виде запрашиваете данные конкретного пользователя. То есть, это не спецификация проверчющая права доступа, а спецификация возвращающая пользовательские данные, которая инкапсулирует проверку прав доступа. Это немного другой взгляд.
Конечно можно применять фильтры и на глобальном уровне в неявном виде. Иногда я тоже так делаю, но лично я все равно придерживаюсь мнения, что явное лучше неявного.
Короткий ответ — отлично. Если хотите длинный ответ, то вот он.
Спасибо за доклад. Было интересно послушать.
В своей практике я не припомню случаев необходимости в таких пользователях.
Техподдержка Azure или YouTrack, например, чудесным образом имеет доступ к моим данным в облаке и помогает решать проблемы, когда что-то не работает;)
Доступ к данным в многопользовательских приложениях