Система разрешений LINQ Query с 3-мя иерархиями - PullRequest
0 голосов
/ 07 февраля 2011

Это немного сложно объяснить в письменном виде, и я ценю любого из вас, кто нашел время, чтобы прочитать этот длинный вопрос.Я открыт для того, чтобы не использовать запрос LINQ для решения этой проблемы и просто выполнять циклы или рекурсию, если это работает лучше.

Я работаю над системой разрешений / авторизации, которая в некоторой степени основана на NETSqlAzMan.
Это основные понятия:

  • Разрешения - это права, которые могут быть авторизованы для двух данных «защищаемых объектов».
  • «Защищаемый» - это объект, для которого могут быть сделаны обе авторизации (представьте здесь пользователя или группу пользователей) и объекты, для которых они могут быть созданы (представьте файл или документ).Пример: User1 и File1 оба являются «защищаемыми», и User1 может быть авторизован для редактирования File1.
  • «Securables» могут иметь родительские и дочерние «securables».
  • «Разрешения» могут иметь родительские и / или дочерние разрешения.

Это более конкретный пример того, как это работает.

Защищаемые:

- Administrators  
    - Tom  
    - Mike  
- Editors  
    - John  
    - Lisa  
- Documents  
    - Document1  
    - Document2  

Разрешения:

  • Edit_Document
    • Edit_Title
    • Edit_Body
    • Change_BGColor

Приведенные выше данные хранятся в 2 таблицах: Securable и Permission с 2 промежуточными таблицами, GroupMembership (Securable иерархия) и PermissionRelationship (Permissions Hierarchy).

Существует также таблица с именем Authorization, которая содержит полномочия, которые состоят из одного Securable как «AuthorizedID», одного Securable как «SecurableID», одного Permission как «PermissionID» и одного Enum, который может бытьУнаследовать (0), Разрешить (1) или Запретить (2).

У меня есть настройка сущностей LINQ2SQL для всех таблиц, а также функция, которая возвращает всех предков данного Securable и функция, которая возвращает всех предков данного разрешения.

Я изо всех сил пытаюсь определить запрос, который должным образом проверяет все 3 иерархии для каждого разрешения, чтобы определить действующее разрешение и ближайший уровень авторизированного идентификатора, для которого установлена ​​авторизация для данного AuthorizedID и SecurableID.

Например: если администраторам предоставлено «Разрешить» в «Документах» для разрешения «Edit_Document», то запрос для данного AuthorizedID «Администраторов» и SecurableiD «Документов» должен вернуть:

  • AuthorizedID: Администратор SecurableID: Разрешение Разрешить документы: Edit_Document
  • AuthorizedID: Администраторы SecurableID: Разрешение Разрешить документы: Edit_Title
  • AuthorizedID: Администратор SecurableID: Разрешение Разрешить документы: Edit_Body
  • AuthorizedID: Администраторы SecurableID: Разрешить документы Разрешить: Change_BGColor

Если дополнительно добавлено разрешение для «Mike» на «Document1» с Deny «Change_BGColor» (Запретить отменяет любое разрешенное fдалее в цепочке), тогда результат запроса AuthorizedID «Mike» и SecurableID «Document1» будет возвращать:

  • AuthorizedID: Администраторы SecurableID: Разрешить разрешение для документов: Edit_Document
  • AuthorizedID: SecurableID администратора: разрешение для документов: Edit_Title
  • AuthorizedID: SecurableID администратора: разрешение для документов: Edit_Body
  • AuthorizedID: Майкрософт SecurableID: Document1 Запретить разрешение: Change_BGColor

Это то, что у меня есть, что хорошо обрабатывает иерархию AuthorizedID и SecurableID, но не учитывает иерархию прав доступа должным образом.Я не уверен, как принять во внимание иерархию разрешений, чтобы, если один из предков разрешения был установлен как запрещающий, тогда дочернее разрешение также показывало отрицание, и результат включает в себя AuthorizedID и SecurableID, для которых установлено запрещение.

public List<LocalPermission> GetSecurablesLocalPermissions(Guid authorizedID, Guid securableID)
    {
        var localAuthorization = (from eff in
                                       (from p in dc.Permissions
                                        let a = (from sa in dc.AllSecurablesAuthorizations(authorizedID)
                                                 where sa.PermissionID == p.PermissionID
                                                 group sa by sa.PermissionID into g
                                                 select g.OrderBy(x => x.Lvl).OrderByDescending(x => x.AuthorizationBits).First()).FirstOrDefault()
                                        select new
                                        {
                                            PermissionID = p.PermissionID,
                                            PermissionName = p.PermissionName,
                                            AuthorizationBits = a.AuthorizationBits ?? 0,
                                            SecurableID = a.SecurableID ?? Guid.Empty,
                                            SecurableName = a.SecurableName,
                                            AuthorizedName = a.AuthorizedName,
                                            AuthorizedID = a.AuthorizedID ?? Guid.Empty
                                        })
                                   join l in
                                       (from p in dc.Permissions
                                        join la in dc.Authorizations
                                           on p.PermissionID equals la.PermissionID into laJoin
                                        from localJoin in laJoin.Where(x => (x.SecurableID == securableID /*|| localJoin.SecurableID == Guid.Empty*/) && (x.AuthorizedID == authorizedID /*|| localJoin.AuthorizedID == Guid.Empty*/)).DefaultIfEmpty()
                                        select new
                                                   {
                                                       PermissionID = p.PermissionID,
                                                       PermissionName = p.PermissionName,
                                                       AuthorizationBits = localJoin == null ? 0 : localJoin.AuthorizationBits,
                                                       SecurableID = localJoin == null ? Guid.Empty : localJoin.SecurableID,
                                                       SecurableName = localJoin == null ? null : localJoin.SecurableName,
                                                       AuthorizedName = localJoin == null ? null : localJoin.AuthorizedName,
                                                       AuthorizedID = localJoin == null ? Guid.Empty : localJoin.AuthorizedID
                                                   }) on eff.PermissionID equals l.PermissionID
                                   select new LocalPermission
                                   {
                                       PermissionID = l.PermissionID,
                                       PermissionName = l.PermissionName,
                                       AuthorizationBits = l.AuthorizationBits,
                                       SecurableID = securableID,
                                       SecurableName = (from s in dc.Securables
                                                        where s.SecurableID == securableID
                                                        select s).SingleOrDefault().SecurableName,
                                       AuthorizedID = authorizedID,
                                       AuthorizedName = (from s in dc.Securables
                                                         where s.SecurableID == authorizedID
                                                         select s).SingleOrDefault().SecurableName,
                                       EffectiveAuthorizationBits = eff.AuthorizationBits == 0 ? l.AuthorizationBits : eff.AuthorizationBits
                                   }).ToList<LocalPermission>();

        return localAuthorization;
    }

1 Ответ

0 голосов
/ 09 февраля 2011

Это так противно.

Ответ: «так что если один из предков разрешения настроен на отказ, то дочернее разрешение также показывает отказ», это означает, что вам нужна рекурсия.Отношения между родителями и детьми в базе данных часто означают рекурсию.linq не поддерживает рекурсию, но собственный sql поддерживает (общие табличные выражения).

Моя стратегия состояла бы в том, чтобы вывести все разрешения в список и затем написать обычный процедурный рекурсивный код для ответа на ваш запрос (который я не полностью понял).Я рекомендую TDD в этом случае.Надеюсь, это поможет вам в правильном направлении.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...