После написания предыдущей статьи по языку PERM и библиотеке Casbin, возникли вопросы. Причем не у одного человека, и я хотел ответить сначала в комментарии, но понял, что объем материала выходит за рамки обычного комментария, поэтому изложу этот ответ в виде отдельной статьи.  Я долго не мог понять ту конкретную конструкцию, которая сидела в голове у вопрошающих, но в конечном итоге после уточняющих вопросов получил ответы, компиляцию которых приведу в цитате.  Надеюсь, я правильно понял эту конструкцию и в ходе обратного реверсинженеринга, мне удалось вытащить исходные данные для решения данной задачи. Для начала обойдемся без использования мультитенатности (домены), так как они усложняют задачу и соответственно понимание. Пример их использования я приводил в прошлой статье.  Сначала я опишу ту реализацию, которая сидит в голове у вопрошающего, чтобы вы тоже ее понимали, а затем мы эту реализацию трансформируем в решение с использованием Casbin. У нас имеется CMS, в которую пользователи через админпанель могут добавлять статьи. В адмике пользователь с ролью user может видеть только свои статьи. Статьи других людей он не может видеть, если только ему не присвоена роль admin или supervisor. Пользователь с ролью supervisor может видеть и редактировать все статьи, а admin имеет все те же права что и supervisor, но кроме этого, еще может и удалять любые статьи. Структура БД нашей CMS:   Содержимое таблицы пользователей — Users:
 Пользователям присвоены следующие роли:
Содержимое таблицы — Roles:   Как мы видим, Piter является администратором, а Bob — супервизором. Alice обычный пользователь, может видеть, создавать и редактировать только свои статьи. Содержимое таблицы со статьями — Articles:   Исходя из вопроса, выборка осуществляется для администратора (Piter, id=3) таким образом:   Выборка для супервизора (Bob, id=2) таким образом:   А выборка для пользователя (Alice, id=1) выглядит так:   Давайте теперь попробуем использовать другой подход к авторизации, на базе библиотеки Casbin.  Для начала давайте определимся что ресурс в подходе PERM — это не столько экземпляр сущности, сколько сама сущность.  Т.е. когда мы описываем модель авторизации, под ресурсом в нашем примере подразумевается сама сущность (таблица) Статья. А не конкретная запись из этой таблицы (с Id=1 например). Дальше необходимо уточнить, что те роли, которые используются в описании этой задачи — это не классические роли из подхода RBAC. Роли RBAC описывают те разрешения, которые можно выполнить с сущностью. Например в классическом RBAC роль user могла бы только читать статьи, роль author могла бы наследовала роль user (т.е. чтение статей), и еще могла бы редактировать и создавать новые статьи, а роль admin могла бы наследовать все предыдущие разрешения и плюс еще удалять статьи.  В описанной же нами выше задаче, по сути, все эти все роли не отличаются друг от друга. И user и supervisor и admin имеют одни и те же права, один набор разрешений — каждый носитель любой из ролей может создавать, редактировать или удалять статьи. Разница только в области видимости, user может видеть в админке только свои статьи, и соответственно редактировать их и удалять. А admin и supervisor не только свои, но еще и чужие.
И в этом заключается большой минус модели RBAC, так это статичная модель авторизации, и с ее помощью вообще невозможно выразить бизнес-правила, в которых используются атрибуты, значения которых заранее не известны и вычисляются в процессе работы.  Об этом подробно уже было рассказано в статье Подходы к контролю доступа: RBAC vs. ABAC А те роли, что мы используем (user, supervisor,admin) — это так называемые — динамические роли. Термин не официальный, и зачастую каждый подразумеваем под ним свое. Они реализуются различными способами, и описанное вопрошающим решение, которое я привел в начале статьи — один из таких подходов.  Для начала давайте определим модель политики RBAC (rbac_model.conf), ее подробное описание я привел в предыдущей статье: Далее, мы убираем таблицу ролей Roles. Роли у нас теперь будут описаны в хранилище политик. Это может быть как обычный *.csv файл, так и таблица в базе данных. Для простоты я буду использовать cvs файл rbac_policy.csv: Суть здесь такая, что мы даем роли user права на чтение, модификацию, создание и удаление статей. Затем роль supervisor наследует права роли user. А роль admin наследует права роли supervisor. Далее пользователю alice(1) мы присваиваем роль user, bob(2) у нас supervisor, а piter(3) — admin. В принципе этого достаточно, чтобы решить проблему, которую описывал автор вопроса. Этот код конечно не для продакшена, а для демонстрации. Для продакшена я советую использовать cross-cutting concern с CQRS+MediatR  Тадам! Задача решена, ответ на вопрос дан.  Теперь же пойдем еще дальше. Мы получили список статей, отобразили в админке, и попытаемся отредактировать какую-нибудь статью. И нам соответственно надо проверить, имеем ли мы права чтобы ее отредактировать, пользователь с ролью user может отредактировать только свои статьи, supervisor и admin могут отредактировать все статьи. Для этого определяем новую модель, называем ее rbac_with_abac_model.conf: Данная модель не сильно отличается от модели чтения, за исключением секции , в ней мы конструкцию r.obj == p.obj заменили на (r.sub == r.obj.OwnerId.ToString() || g(r.sub, "supervisor")). Это следует читать как r.sub (id пользователя) должен совпадать с полем r.obj.OwnerId (id владельца обновляемой записи) или r.sub должен принадлежать группе "supervisor". Поскольку группа admin наследует все права группы supervisor то и члены группы admin будут соответствовать этому правилу.  Файл с политиками остается прежним, его мы не меняем. Теперь смотрим как это выглядит в коде: Здесь стоит обратить внимание на то, что мы в метод e.Enforce передаем вторым параметром объект, который представляет из себя экземпляр класса Article. Ну и последний шаг — попытаемся удалить статью. Бизнес-правило у нас здесь такое, что пользователь с ролью user может удалить свою статью, supervisor — не имеет прав удалять чужие статьи, а admin такое право имеет.
Опишем теперь это бизнес-правило в модели политики PERM, в файле delete_model.conf: Она не сильно отличается от предыдущей политике редактирования, за тем исключением, что удалять чужие статьи мы разрешили только роли admin. Если носитель роли supervisor попытается удалить чужую статью, у него выйдет ошибка превышения полномочий.  Как и в случае с моделью, код мало чем отличается от предыдущего примера на редактирование: Надеюсь, данный пример продемонстрировал гибкость, универсальность и удобство использования библиотеки Casbin и языка PERM для построения решений по разделению доступа и авторизации.  Еще отмечу, что как сами модели политики, так и правила политики могут храниться в БД. И предусмотрена возможность фильтрации правил, если их большое множество, и это может стать узким местом в высоконагруженных приложениях.  Casbin под капотом использует библиотеку DynamicExpresso.Core для интерпретации простых выражений C# при сопоставлении правил политик с входными значениями, что позволяет эффективно использовать Casbin даже в самых сложных сценариях авторизации.  Не смотря на свою молодость, Casbin активно развивается, используется во множестве проектов, обрастает полезными инструментами и API. Такими например как UI для управления политиками.  Полностью работоспособный и самодостаточный код примера, который я использовал для написания данной статьи я разместил у себя на Github, можете скачать и поиграться, если есть интерес и желание.  Software Engineer