Контроль доступа в доменно-ориентированном дизайне

Я читал о DDD и контроле доступа и обнаружил некоторое противоречие между двумя следующими мнениями:

  • «вопросы безопасности следует решать вне домена»
  • "требования к управлению доступом зависят от домена"

Я ищу лучшие практики по этому поводу. Итак, где мне разместить логику управления доступом с помощью доменно-ориентированного дизайна и как мне ее реализовать?

(Чтобы быть более конкретным, DDD + CQRS + ES.)

Я думаю, что это должно быть где-то рядом с бизнес-логикой, например, пользовательская история может быть примерно такой:

Пользователь может редактировать свой профиль, отправив имя пользователя, список увлечений, резюме и т. Д.

На основе пользовательской истории мы реализуем модель предметной области и услуги, например:

UserService
    editProfile(EditUserProfileCommand command)
        User user = userRepository.getOneById(command.id)
        user.changeName(command.name)
        user.changeHobbies(command.hobbies)
        user.changeCV(command.cv)

UserRepository
    User getOneById(id)

User
    changeName(String name)
    changeHobbies(String[] hobbies)
    changeCV(String cv)

Это нормально, но где же HIS profile часть истории?

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

deny all, but if subject.id = resource.owner.id then grant access

Но где мы должны обеспечить соблюдение этого правила и как мы должны его реализовать?


person inf3rno    schedule 05.05.2014    source источник
comment
Включение идентификатора пользователя в команду (command.id) вносит неоднозначность. Лучше удалить идентификатор пользователя из команды и передать пользователя, взятого из контекста авторизации, вместе с командой.   -  person Lightman    schedule 21.02.2016
comment
@Lightman Спасибо за ваш вклад. Честно говоря, я понятия не имею, почему этот вопрос и ответ получают такие высокие баллы. Может быть, кому-нибудь стоит написать статью или пример приложения о том, как это должно быть сделано правильно. Недавно я снова начал читать о DDD, и действительно сложно найти хорошие статьи. У некоторых из них есть очевидные проблемы с дизайном. Я предполагаю, что это происходит, когда каждый может писать в теме, даже такие люди, как я, которые не полностью ее понимают.   -  person inf3rno    schedule 14.01.2020


Ответы (1)


Так где же мне разместить логику контроля доступа?

Согласно этому: https://softwareengineering.stackexchange.com/a/71883/65755 точка применения политики должна быть прямо перед вызовом UserService.editProfile().

Я пришел к такому же выводу: этого не может быть в пользовательском интерфейсе, потому что в нескольких пользовательских интерфейсах мы будем иметь повторение кода. Это должно быть до создания доменных событий, потому что они указывали, что мы уже что-то сделали в системе. Таким образом, мы можем ограничить доступ к объектам домена или к службам, которые используют эти объекты домена. По CQRS у нас нет необходимости иметь объекты домена по модели чтения, только сервисы, поэтому мы должны ограничить доступ к сервисам, если нам нужно общее решение. Мы могли бы помещать решения о доступе в начало каждой операции службы, но это было бы grant all, deny x антипаттерном безопасности.

Как мне это реализовать?

Это зависит от того, какая модель управления доступом подходит для домена, поэтому это зависит от истории пользователя. Принимая решение о доступе, мы обычно отправляем запрос на доступ и ждем ответного разрешения. Запрос на доступ обычно состоит из следующих частей: тема, ресурс, операция, среда. Таким образом, субъекту требуется разрешение на выполнение операции с ресурсом в среде. Сначала мы идентифицируем субъект, затем аутентифицируем его, а после этого идет авторизация, где мы проверяем, соответствует ли запрос доступа нашей политике доступа. Каждая модель контроля доступа работает одинаково. Ofc. им может не хватать некоторых из этих шагов, но это не имеет значения ...

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

  • При управлении доступом на основе идентичности (IBAC) у нас есть хранилище идентичности - разрешений (список управления доступом, список возможностей, матрица управления доступом). Так, например, с помощью списка управления доступом мы храним список пользователей или групп, у которых могут быть разрешения.

    UserService
        @AccessControlList[inf3rno]
        editProfile(EditUserProfileCommand command)
    
  • При управлении доступом на основе решетки (LBAC) субъект имеет уровень допуска, ресурс имеет требуемый уровень допуска, и мы проверяем, какой уровень выше ...

    @posseses[level=5]
    inf3rno
    
    UserService
        @requires(level>=3)
        editProfile(EditUserProfileCommand command)
    
  • С помощью управления доступом на основе ролей (RBAC) мы определяем роли субъектов и предоставляем разрешения тем субъектам, чьи действия являются фактической ролью.

    @roles[admin]
    inf3rno
    
    UserService
        @requires(role=admin)
        editProfile(EditUserProfileCommand command)
    
  • Посредством управления доступом на основе атрибутов (ABAC) мы определяем атрибуты субъекта, ресурса и среды и на их основе составляем наши политики.

    @attributes[roles=[admin]]
    inf3rno
    
    UserService
        @policy(subject.role=admin or resource.owner.id = subject.id)
        editProfile(EditUserProfileCommand command)
        @attribute(owner)
        Subject getOwner(EditUserProfileCommand command)
    
  • С помощью управления доступом на основе политик (PBAC) мы не назначаем наши политики ничему другому, они автономны.

    @attributes[roles=[admin]]
    inf3rno
    
    UserService
        editProfile(EditUserProfileCommand command)
        deleteProfile(DeleteUserProfileCommand command)
        @attribute(owner)
        Subject getOwner(EditUserProfileCommand command)
    
    @permission(UserService.editProfile, UserService.deleteProfile)
    @criteria(subject.role=admin or resource.owner.id = subject.id)
    WriteUserServicePolicy
    
  • Используя управление доступом с адаптацией к рискам (RAdAC), мы основываем свое решение на профиле относительного риска субъекта и уровне риска операции. Я думаю, это нельзя описать правилами. Я не уверен в реализации, возможно, это то, что stackoverflow использует своей системой баллов.

  • При управлении доступом на основе авторизации (ZBAC) мы не выполняем идентификацию и аутентификацию, вместо этого мы назначаем разрешения для факторов идентификации. Например, если кто-то отправляет токен, он может получить доступ к услуге. В остальном все аналогично предыдущим решениям. Например, с ABAC:

    @attributes[roles=[editor]]
    token:2683fraicfv8a2zuisbkcaac
    
    ArticleService
        @policy(subject.role=editor)
        editArticle(EditArticleCommand command)
    

    Таким образом, любой, кто знает токен 2683fraicfv8a2zuisbkcaac, может пользоваться сервисом.

и так далее...

Есть много других моделей, и выбор наилучшего всегда зависит от потребностей вашего клиента.

Итак, чтобы подвести итог

- "security concerns should be handled outside the domain"
- "access control requirements are domain specific"

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

изменить через 2 года 5 сентября 2016 г.

Поскольку я ответил на свой вопрос как новичок в DDD, я прочитал Реализация Домен-ориентированный дизайн от Вона Вернона. Это была интересная книга по теме. Вот цитата из него:

Это составляет новый ограниченный контекст - контекст идентификации и доступа - и будет использоваться другими ограниченными контекстами с помощью стандартных методов интеграции DDD. Для потребляющих контекстов контекст идентификации и доступа является универсальным субдоменом. Продукт будет называться IdOvation.

Так что, по мнению Вернона, возможно, лучшее решение перенести контроль доступа на общий поддомен.

person inf3rno    schedule 06.05.2014
comment
Что касается редактирования, легко переместить на основе ролей, ACL или разрешений в общий BC, но не столько для авторизации на основе правил. - person plalx; 15.08.2017
comment
Одна вещь, которую я не понимаю в этих системах ABAC, которые имеют проверки типа @policy(subject.role=admin or resource.owner.id = subject.id), - это то, как обрабатывается управление версиями, потому что код в политике - это песочница из кода в службе. Владелец мог измениться к тому времени, когда код в реальном методе службы запросил ресурс, но, может быть, мне не хватает какой-то магии? - person Jordan; 27.11.2019
comment
@Jordan Не стесняйтесь редактировать или изменять его по своему усмотрению. Насколько я помню, я неделю читал об ABAC, но никогда им не пользовался. Насколько я помню, это своего рода контроль доступа на основе политик или правил, хотя он и называется на основе атрибутов. Возможно, я что-то совершенно неправильно понял, когда писал этот ответ 5 лет назад. Я должен был оставить только правку после двухлетней части, но люди уже проголосовали за мои ответы, я думаю, поэтому я оставил это там. Однако это будет не первый раз, когда люди проголосуют за неправильный ответ ... - person inf3rno; 27.11.2019
comment
@ inf3rno, это очень подробный и хорошо продуманный ответ, и в целом он похож на аналогичные примеры, которые я видел. Мне просто любопытно, есть ли канонический способ управления версиями одной системы ABAC. - person Jordan; 27.11.2019
comment
@Jordan Простите, я не знаю. - person inf3rno; 27.11.2019
comment
@Jordan Это действительно важно? Будет ли иметь значение, если разрешение отозвано за 1 мс до того, как пользователь выполнит действие. - person plalx; 02.12.2019
comment
@plalx Да, во многих случаях вы нарушаете инвариант и можете оказаться в недопустимом / неожиданном состоянии. - person Jordan; 02.12.2019
comment
@Jordan Не думаю, что когда-либо сталкивался с подобным случаем. Записи журнала или порядок событий могут выглядеть странно, например PermissionRevoked, затем ActionPerformed, но, в конечном итоге, если это не происходит часто, большинство предприятий будут в порядке с этой IMO. У вас всегда может быть возможная согласованность, которая проверяет журнал событий, чтобы найти такие сценарии и выполнить компенсирующие действия? - person plalx; 03.12.2019
comment
@plalx Думаю, мне придется подумать об этом еще раз. В целом мне нравится идея конечной согласованности, но я обычно использую оптимистичную модель параллелизма или, по крайней мере, проверяю, находится ли объект в допустимом состоянии для рассматриваемого действия / поведения. Ваша точка зрения кажется разумной, но она противоречит моему инстинкту :) Вы, вероятно, правы, что это верно в большинстве случаев, но я уверен, что есть некоторые области, где это было бы неприемлемо. - person Jordan; 03.12.2019
comment
@Jordan Да, конечно, но в конечном итоге, когда вы начинаете думать об этом, это часто не имеет значения. Например, руководство находится на собрании, оно только что приняло решение об увольнении сотрудника, и права доступа должны быть отозваны. Решение уже принято, но в системе его еще никто не применял. Уже есть условия гонки в реальном мире. В большинстве случаев при применении правил авторизации не имеет значения, кто щелкнул бы первым. Права доступа часто даже кешируются во многих системах на определенный период времени. - person plalx; 03.12.2019
comment
Вы должны быть осторожны, чтобы не принимать решения о бизнес-состоянии, основанные на несогласованных правах доступа, например, если только владелец объекта может изменить его, то несомненно, что если мы находимся в этой операции, текущий пользователь владеет юридическим лицом. Это объединяет состояние бизнес-модели с состоянием модели доступа. - person plalx; 03.12.2019
comment
@Jordan Ну, вы могли бы сделать что-то подобное, что делает mongodb. Вы начинаете с ресурса с номером версии v1. Вы вносите изменения и отправляете запрос на обновление ресурса и офс. вы добавляете v1 к запросу. Тем временем кто-то обновил ресурс, получивший номер версии v2. Поэтому, когда mongodb получит ваш запрос с v1, он отклонит его, потому что ресурс находится в v2. В вашем случае вы можете прикрепить версию к команде перед первой проверкой безопасности, и когда версия изменится перед обработкой команды и созданием событий домена, вы можете проверить ее снова. - person inf3rno; 03.12.2019
comment
@Jordan Детализация - интересная тема здесь. Похоже, я добавил контроль доступа к службам приложений, поэтому я предполагаю, что у каждой службы приложения должна быть своя собственная версия. Другой вариант: каждый выпуск получает версию политики. Таким образом, при обновлении версии можно остановить обработку команд и повторить попытку с новыми политиками. Может быть, это частичное решение. Он не решает сценариев, в которых политика одинакова, но, например, владелец меняется во время обработки команды от предыдущего владельца. Я думаю, что для этих сценариев вам нужна конечная согласованность, как написано в plalx. - person inf3rno; 03.12.2019
comment
Но опять же, я не специалист. :П - person inf3rno; 03.12.2019
comment
Привет @ inf3rno, у меня есть вопрос о том, как можно было бы использовать поддомен управления идентификацией и доступом (IAM), если бы он был разделен на его собственный общий поддомен. Не могли бы вы уточнить, представляет ли следующий путь вызова рекомендуемое использование предоставленного вами поддомена IAM? пользователь делает запрос - ›обработчик HTTP-запроса (контроллер приложения) преобразует запрос в объект DTO и вызывает useCase -› прецедент обрабатывает логику домена, проверяет usersRepository.exists (user), если существует, проверяет разрешения через IAMrepository.getPermission (user) (a читать модель), и только потом выполняет единицу работы, если разрешения действительны? - person Kyle Fong; 03.07.2021
comment
@KyleFong Звучит нормально, но я не эксперт в этой теме. Я даже не понимаю, как я получил столько голосов, наверное, никто другой не является экспертом. : D - person inf3rno; 03.07.2021