Это немного сложно объяснить в письменном виде, и я ценю любого из вас, кто нашел время, чтобы прочитать этот длинный вопрос.Я открыт для того, чтобы не использовать запрос 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;
}