Контроль доступа в ASP.NET MVC с использованием Enum для управления разрешениями на основе [Flags] в SQL - PullRequest
0 голосов
/ 03 ноября 2011

Этот вопрос вдохновлен этим SO вопросом, касающимся контроля доступа в ASP.NET MVC.Здесь я пытаюсь привести принятый ответ в реальное решение.

В ответе упоминается использование FileSystemSecurity в качестве источника вдохновения для управления разрешениями.Здесь я также использую перечисление с атрибутом Flags, чтобы определить ACL для всех моих объектов.Кроме того, каждый член моих объектов будет храниться в столбце в SQL.Предположим, что упрощенное сопоставление ORM для Linq2SQL, EF или nHibernate.

Редактировать: Добавлено следующее преимущество / обоснование для этого подхода

Эта модель безопасности была основана на FileSystemRights , подход .NET для управления разрешениями на уровне файлов.

Одна из основных причин, по которой мне нравится этот подход, заключается в том, что я могу легко создать сводку всех разрешений, ИЛИ объединяя все отдельные ACL.Мне также нравится, что я могу добавить DENY ACL для удаления унаследованного разрешения.

List<myObject> PermissionsList  = GetACLForObject(ItemID, UserID);
foreach (var acl in PermissionsList)
{
   // The following enum has the [FlagsAttribute] applied so the .ToString() is pretty 
   PermissionWithFlagsEnum sampleForSO = (PermissionWithFlagsEnum )acl.Permission;

   Console.Writeline ("Setting " + sampleForSO.ToString() + " permission for group: " + acl.ACLName);

   ResultingPermission = resultPermission |  acl.Permission ; 
}
public int ResultingPermission {get;set;}

/ End Edit

Затем мне пришло в голову, что я могу сравнить меньшепривилегированных пользователей по числовому значению enum из более привилегированных пользователей enum.

Я думаю, что это определение позволит быстро и легко идентифицировать пользователей в базе данных SQL, не анализируяenum на бэкэнде.(Найти непривилегированных пользователей через select users from permissions where security < DBAceessRights.DetailRead)

Вот как я определил флаги (минимальное значение имеет наименьшее разрешение)

[Flags]
public enum DBAccessRights
{
    DenyAll  =1 << 1,

    WikiMode = 1 << 4,

    AppearInListing = 1 << 8,

    DetailRead = 1 << 12,

    CreateNew = 1 << 18,

    DetailEdit = 1 << 22,

    DeleteChild = 1 << 26,

    DeleteThis = 1 << 30,

    EditPermissions = 1 << 31,

}

У меня есть таблица разрешений который соединяет идентификатор пользователя с объектом ACE.Это должно уменьшить необходимость одновременных обновлений в определенной строке.enter image description here

Вопрос

  • Это хорошая идея?

  • Кто-то сделал это?раньше (лучше чем это)?(Изменить: Это принятый ответ здесь )

  • Если это стандартный способ реализации разрешений, как он называется?

Ответы [ 2 ]

4 голосов
/ 03 ноября 2011

Я бы сказал нет.

  1. Вы предполагаете, что вам потребуется не более 32 (или 64, если вы используете bigint) привилегий.Звучит как произвольный предел для меня.Перемещение на varbinary в базе данных может преодолеть это, но тогда ваше перечисление будет на вершине (не может создавать перечисления на byte[])!И вы не сможете проводить числовые сравнения.

  2. Вы предполагаете, что привилегия 0x1 всегда будет логически меньше 0x2 и 0x80 и так далее.Это редко бывает в моем опыте.Чаще всего привилегии не зависят друг от друга (то есть: наличие привилегии «добавить пользователя» не имеет ничего общего с привилегией «загрузить изображение»): одна группа пользователей (администраторы) будет иметь первую, а другие (издатели контента) -последнее. Это означает, что ваше числовое сравнение не так полезно, как вы думали.

  3. То, что вы делаете, может принести выигрыш в производительности, но вы еще не продемонстрировали, что производительностьпроблема еще существует! Большинство моих систем привилегий работают с одной записью базы данных на пользователя для предоставления или отклонения каждой привилегии. Извлечение 100 записей не обременяет мою базу данных. Лучше было бы лучше просто кэшировать разрешения каждого пользователя между запросами, чем использовать битовую маску.

  4. Относительно производительности обновлений: как часто меняются права пользователей? Когда система установлена, они, как мне кажется, довольно статичны.

Я нашел битовые маски наиболее полезными при попытке упаковать много данных в небольшое пространство. Но часто моипункт 1 возвращается, чтобы укусить меня, когда я получаю более 64 вещей.

Обратите внимание, что я использовал эту технику при записи статистики действий пользователя (отслеживание, какие элементы пользователи находят при поиске, какие объекты они просматривают,так далее).Моей причиной было просто убедиться, что длины записей в базе данных были фиксированными и маленькими, поэтому вставки выполнялись быстро.И я не делал числовых сравнений.(И, честно говоря, я никогда не проверял, есть ли какая-либо разница между столбцом int и несколькими столбцами bit).

EDIT Базовая альтернатива (которую яиспользую): отношение M: N между пользователями и привилегиями (я называю их правами).

Users To Rights DB Diagram

(извините за эти уши Micky Mouse на моем пользователе!)

Наличие записи в UserRight указывает, что право предоставлено этому пользователю.Отсутствие указывает на отсутствие права.Этот запрос дает вам все права, назначенные пользователю.

SELECT [dbo].[User].Username, [dbo].[Right].Id, [dbo].[Right].Name
FROM [dbo].[Right]
INNER JOIN [dbo].[UserRight] ON [dbo].[Right].Id = [dbo].[UserRight].RightId
INNER JOIN [dbo].[User] ON [dbo].[User].Id = [dbo].[UserRight].UserId
WHERE [dbo].[User].Id = @pUserId

Затем в коде для подтверждения пользователь имеет право:

var grantedRights = RunTheAboveQuery(currentUser.Id);
if (grantedRights.Any(r => r.Id == requiredRight))
    // User has the right.
else
    // User does not have the right. 

Очевидно, вы можете масштабировать это, чтобы проверитьу пользователя есть несколько прав на один запрос.

Это не ограничивает искусственно, сколько привилегий поддерживает ваша система.И это не предполагает каких-либо отношений между привилегиями (поэтому вы не можете выполнять свои числовые сравнения; все выполняется IEnumerable<Right> в моей системе).И, если вы действительно заинтересованы в битовых масках, вы можете создать Dictionary<User, BitArray> в кэше!

У меня также есть концепция Role, которая предоставляет логическую группу прав для пользователей.Это просто еще один стол M: N.Это упражнение для читателя!

1 голос
/ 03 ноября 2011

В общем случае помещать несколько значений (флагов) в одно поле - плохая идея.

Если вы планируете проводить аудит этих данных, это очень плохая идея, поскольку сложно эффективно определить, какие флаги менялись от обновления к обновлению.

Проблемы, с которыми вы можете столкнуться, даже если вы не проводите аудит

  • Многие простые запросы (какими пользователями авторизация DeleteThis) не являются SARGable потому что вам нужно выполнить побитовую операцию перед сравнением.

  • Также select users from permissions where security < DBAceessRights.DetailRead может не вернуть правильные результаты, потому что AppearInListing & CreateNew больше, чем DetailRead, но не имеетDetailRead включен.Таким образом, ожидаемое вами преимущество может не получить

  • Управление параллелизмом (множественные записи в ACL) труднее, так как мутирование одного «логического значения» фактически изменяет все значения.

...