Где проверить авторизацию для команды?

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

Например, установка клиента как предпочтительного включает в себя:

  • MarkAsPreferred действие контроллера (может быть Winforms или что-то еще);
  • SetCustomerAsPreferredCommand;
  • SetCustomerAsPreferredCommandHandler;
  • Customer.MarkAsPreferred() (домен);

Я определил 3 места для проверки авторизации:

  • пользовательский интерфейс для отображения целей (пользователь не должен видеть ссылку/кнопку, если у него нет доступа к ней);
  • действие контроллера, чтобы подтвердить, что пользователь имеет право называть эту команду; команды считаются всегда успешными (в отношении проверки, но я тоже принимаю авторизацию), и у нас есть возможность сообщить пользователю об отсутствии доступа;
  • внутри команды непосредственно перед вызовом логики домена;

SomeView.cshtml

if (authorizationService.Authorize("MarkCustomerAsPreferred))
{
    // show link
}

CustomerController

[HttpPost]
public ActionResult MarkAsPreferred(Guid id)
{
    if (!authorizationService.Authorize("MarkCustomerAsPreferred))
    {
        return RedirectToAction("Unauthorized");
    }

    var MarkCustomerAsPreferredCommand { Id = id };
    ...
}

MarkCustomerAsPreferredCommandHandler

public void Handle(MarkCustomerAsPreferredCommand command)
{
    if (!authorizationService.Authorize("MarkCustomerAsPreferred"))
    {
        throw new Exception("...");
    }

    customer.MarkAsPreferred();
}

Мой вопрос: нужна ли мне проверка авторизации в 3-х местах или я просто переоценен?

Я искал по всему Интернету, но не смог найти ни одного примера или ссылки об этом.

Edit

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

Я нашел этот пост в блоге, который объясняет именно эту концепцию.

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

Ответ 1

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

class AuthorizationHandler : IHandle<SetCustomerAsPreferred> {

    IHandle<SetCustomerAsPreferred> innerHandler;

    public AuthorizationHandler(IHandle<SetCustomerAsPreferred> handler)
    {
        innerHandler = handler;
    }

    public void Handle(SetCustomerAsPreferred command) 
    {
        if (/* not authorized */)
            throw ...
        innerHandler.Handle(command);
    }

}

class SetCustomerAsPreferredCommandHandler : IHandle<SetCustomerAsPreferred> {

    public void Handle(SetCustomerAsPreferred command) 
    {
        // do the work
    }

}

Ответ 2

Хороший пользовательский интерфейс должен иметь эту проверку в представлении, поэтому пользователь не будет щелкать по ошибке. Я считаю проверку контроллера "реальной", потому что там создается команда. Если у пользователя нет прав, она не должна создавать (или даже достигать этого действия) команду.

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