DDD CQRS - авторизация для запроса и для каждой команды

15

Резюме

Должна ли авторизация в CQRS / DDD реализовываться для каждой команды / запроса или нет?

Я впервые разрабатываю онлайн-приложение, использующее более или менее строго шаблон DDD CQRS. Я столкнулся с некоторой проблемой, которую я не могу понять.

Приложение, которое я создаю, представляет собой приложение главной книги, которое позволяет людям создавать книги, а также позволяет другим людям просматривать / редактировать / удалять их, например сотрудников. Создатель бухгалтерской книги должен иметь возможность редактировать права доступа к созданной бухгалтерской книге. Могли даже поменять владельца. Домен имеет две совокупности TLedger и TUser .

Я прочитал много постов с ключевым словом DDD / CQRS, касающихся безопасности, авторизации и т. Д. Большинство из них заявили, что авторизация была общим субдоменом , если только не создавалось приложение безопасности.

В этом случае основной домен - это, безусловно, учетная область, заинтересованная в транзакциях, балансировании и счетах. Но также требуется возможность управлять мелкозернистым доступом к бухгалтерским книгам. Мне интересно, как спроектировать это в терминах DDD / CQRS.

В учебниках DDD повсюду говорится, что команды являются частью вездесущего языка. Они значимы. Это конкретные действия, которые представляют «реальную вещь».

Поскольку все эти команды и запросы являются действительными действиями, которые пользователи будут выполнять в «реальной жизни», должна ли реализация авторизации сочетаться со всеми этими «командами» и «запросами»? Пользователь будет иметь полномочия для выполнения TLedger.addTransaction (), но не TLedger.removeTransaction (), например. Или пользователю будет разрешено выполнить запрос "getSummaries ()", но не "getTransactions ()".

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

Или, в отрыве, именованные «разрешения» будут зарегистрированы для пользователя. Разрешения, которые затем будут отображаться для конкретных команд. Например, разрешение «ManageTransactions» позволит пользователю выполнять «AddTransaction ()», «RemoveTransaction ()» и т. Д.

  1. Разрешение сопоставления пользователя -> регистр -> команда / запрос

  2. Разрешение сопоставления пользователя -> регистр -> разрешение -> команда / запрос

Это первая часть вопроса. Или вкратце, следует ли осуществлять авторизацию в CQRS / DDD для каждой команды или для запроса? Или авторизация должна быть отделена от команд?

Во-вторых, в отношении авторизации на основе разрешений. Пользователь должен иметь возможность управлять разрешениями в своих книгах или в тех книгах, которыми ему разрешено управлять.

  1. Команды управления авторизацией происходит в Главной книге

Я думал о добавлении событий / команд / обработчиков в агрегат Ledger , таких как grantPermission (), revokePermission () и т. Д. В этом случае применение этих правил будет происходить в обработчиках команд. Но для этого необходимо, чтобы все команды включали идентификатор пользователя, который выполнил эту команду. Затем я бы проверил в TLedger, существует ли разрешение для этого пользователя на выполнение этой команды.

Например :

class TLedger{ 
    function addTransactionCmdHandler(cmd){
        if (!this.permissions.exist(user, 'addTransaction')
            throw new Error('Not Authorized');
    }
}
  1. Команды управления авторизацией в Пользователе

Другой способ - включить разрешения в TUser. TUser будет иметь набор разрешений. Затем в обработчиках команд TLedger я получал пользователя и проверял, есть ли у него разрешение на выполнение команды. Но это потребовало бы от меня получения агрегата TUser для каждой команды TLedger.

class TAddTransactionCmdHandler(cmd) {
    this.userRepository.find(cmd.userId)
    .then(function(user){
        if (!user.can(cmd)){
            throw new Error('Not authorized');
        }
        return this.ledgerRepository.find(cmd.ledgerId);
    })
    .then(function(ledger){
        ledger.addTransaction(cmd);
    })

}
  1. Еще один домен с сервисом

Другая возможность - полностью смоделировать другой домен авторизации. Этот домен будет интересоваться правами доступа, авторизацией и т. Д. Субдомен учета будет использовать службу для доступа к этому домену авторизации в форме AuthorizationService.isAuthorized(user, command).

class TAddTransactionCmdHandler(cmd) {
    authService.isAuthorized(cmd)
    .then(function(authorized){
        if (!authorized) throw new Error('Not authorized');
        return this.ledgerRepository.find(cmd.ledgerId)
    })
    .then(function(){
        ledger.addTransaction(cmd);
    })

}

Какое решение будет наиболее "DDD / CQRS"?

Людовик С
источник
1
Отличный вопрос - я пытался решить подобные проблемы, и ни одна литература, кажется, не рассматривает это напрямую. Меня немного смутила вторая половина вашего вопроса. Это звучало так, как будто вы задались вопросом о том, куда поместить управление разрешениями (добавление или удаление разрешений), но показанные примеры предназначены для добавления транзакции, поэтому похоже, что вторая половина задает вопрос «как мне запрашивать разрешения». Можете ли вы уточнить эту часть, пожалуйста?
Emragins
Каждая транзакция может иметь политику выполнения. Каждый пользователь должен принадлежать к одной или нескольким группам, каждая группа будет иметь профиль доступа, определяющий, какие транзакции разрешены. Во время выполнения перед выполнением транзакции политика сверяется с агрегированными профилями для исполняющего пользователя. Конечно, это легче сказать, чем сделать.
NoChance

Ответы:

5

По первому вопросу я боролся с чем-то похожим. Я все больше склоняюсь к трехфазной схеме авторизации:

1) Разрешение на уровне команды / запрос «делает этот пользователь когда - либо разрешения на выполнение этой команды?» В приложении MVC это, вероятно, может быть обработано на уровне контроллера, но я выбираю общий предварительный обработчик, который будет запрашивать хранилище разрешений на основе текущего пользователя и выполняемой команды.

2) Авторизация внутри службы приложений «имеет ли этот пользователь» когда-либо * разрешение на доступ к этой сущности? »В моем случае это, вероятно, окажется неявной проверкой просто с помощью фильтров в хранилище - в моем домене это в основном это TenantId с немного большей детализацией OrganizationId.

3) Авторизация, которая зависит от временных свойств ваших объектов (таких как Статус), будет обрабатываться внутри домена. (Пример: «Только некоторые люди могут изменять закрытую бухгалтерскую книгу.») Я предпочитаю размещать это в домене, потому что это сильно зависит от домена и бизнес-логики, и мне не очень удобно показывать это в других местах.

Я хотел бы услышать ответы других на эту идею - разорвите ее на куски, если хотите (просто предоставьте несколько альтернатив, если вы это сделаете :))

emragins
источник
Я думаю, что у вас есть веские аргументы относительно разных «слоев» авторизации. В системе, над которой я работал, были разные типы пользователей - зарегистрированные пользователи и сотрудники. Разрешения обработчика команды / запроса провели базовую проверку типа пользователя. Если это был персонал, это всегда проходило. Если это был зарегистрированный пользователь, то он был разрешен только при соблюдении определенных условий (например, разрешений для совокупности).
Магнус
0

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

pnschofield
источник