1

Here are the domain classes :

class AuthorAR {
    private $authorId;
}

class BookAR {
    private $bookId;
    // book owner
    private $authorId;
    private $title;

    public function changeTitle($title) {
        $this->title = $title;
    }
}

// This will be in the domain layer to make explicit the finding of the book
interface BookRepository {
    public function findByBookId($bookId);
    public function findForAuthorIdByBookId($authorId, $bookId);
}  

Here is the Dao class used for authorization outside of the domain :

class AuthorizationDao {
    public function findBookOfIdForAuthorId($bookId, $authorId) {}
}  

Here are the 2 aproaches that I've seen in some places and don't know which is better, which one is considered a good practice (this is just a naive example, the main question is where to put this type of authorization) :

// Aproach 1 : call the repository with the method made explicit in the domain,
// in order to check if the book with a specific author exists
class ChangeBookTitleCommandHander {
    public function handle($command) {
        $book = $bookRepository->findForAuthorIdByBookId($command->authorId, $command->bookId );

        if($book === NULL) {
            throw new CommandHandlingFailedException();
        }
    }
}

// Aproach 2 use an authorization service inside the controller to check if a user  
// has access to the specific book resource in order to change it's title
class Controller {
    public function changeTitleAction() {
        // This will use the authorizationDao->findBookOfIdForAuthorId($bookId, $authorId) to allow
        // access for changing that resource
        // @throws UnauthorizeAccessException
        $authorizationService->authorizeCommand($changeBookTitleCommand);
    }
}

So how to design this type of permission checking (authorization)? How to design the verification if a particular author is the owner of the book before allowing the BookAR to change it's state (in the case of CQRS where no querying permission is needed, just state change permission) ?

Tudor
  • 1,033
  • 1
  • 11
  • 26

1 Answers1

3

Short answer:

You should keep business logic separate from authorization logic. Why? It makes it:

  • easier to maintain
  • easier to evolve and update
  • easier to run audit reports of what a user can do in the system.

Long answer:

I have already provided some answers on Stack Overflow around this topic:

In these answers I introduce the concept of attribute-based access control (ABAC) and externalized authorization management. The point is that:

  • You should avoid implementing custom code to achieve authorization.
  • You should cleanly separate the business logic from the authorization logic.

Existing authorization models (RBAC...) are too user-centric and will not let you do what you are looking for. You need a relationship between the book's owner and the user trying to act on the book.

Steps in ABAC. ABAC uses attributes (of the user, the object, the action, and the context) to define authorization policies in a technology-agnostic way such as:

  • A user can edit the metadata of a book if and only if the book.owner==user.id

NIST recently published a report on ABAC which I recommend you check out. XACML, the eXtensible Access Control Markup Language, is the de-facto implementation of ABAC. XACML defines:

  • an architecture,
  • a policy language, and
  • a request/response scheme.

You can find more resources on XACML on the OASIS website as well as on this YouTube channel.

Community
  • 1
  • 1
David Brossard
  • 12,223
  • 6
  • 42
  • 72
  • I'm not into authorization patterns that make you leak business logic in a layer outside of the domain. As i am using DDD in a CQRS context, most of these authorization policies would be implemented in the domain model : Author->createAuthor->generateAuthorIdFromUser(){ if(!User.isActive() throw DomainException ... )} . Most of the authorization rules are actually business rules. The actual authorization is the one that allows a user to access a resource, if it is the owner or permission was granted by an admin. – Tudor Jun 18 '14 at 12:33
  • Another exampe would be a shopping cart. There might be a business rule that states that noone could add more than 10 items to a cart, but anybody could add items to cart. This a limitation expressed by the business same as the User.isActive() domain description. So basically anyone can access the addItemToCart() command, but if the cart has already 10 items in it, it should not be possible to fullfill this command (so it should'nt even be available in the UI). As for CQRS this should be expressed in the domain, and your type of auth in the read/query side of the application (resource access). – Tudor Jun 18 '14 at 12:33
  • Also I agree that ABAC is better than RBAC, but ofcourse this depends on the business/application needs. I'm not convinced about ABAC as it might leak some business rules out of the domain model (DDD). ABAC seems to take out most of the business ruless from the domain layer (DDD/CQRS). I think ABAC is suited for the query side of the application, not the write/command side where most of the rules, if not all, will be in the domain layer. – Tudor Jun 18 '14 at 12:34
  • P.S. Another note : if ShoppingCart has more than 10 products is in invalid state (domain business rules reject the addItemCommand), if User.isNotActive() domain will reject the creation of a new author from a person(user) that has not finished a payment (that isActive domain description) . These all are domain business rules not authorization rules . These rules allow or reject certain commands to modify the state of the domain. If I read correctly with ABAC these rules would be in the Ahtorization layer, outside the domain, which seems to contradict the DDD rules. – Tudor Jun 18 '14 at 12:47
  • I agree with all your comments. They all boil down to defining what a business rule is and defining what an authorization rule is. Not everything should be expressed as authorization policies. Your cart example is very good. The fact you can only have 10 items is definitely not an authorization rule and should stay w/in your business logic. – David Brossard Jun 18 '14 at 12:55
  • Anyway +1, first time i read about ABAC and seems better than RBAC. But regarding where to put that rule in my question , ABAC here doesn't help, I still don't know if that should be business rule or authorization rule (a book might have more authors). I could even add something like changeTitle(Author byAuthor, string title) to keep the logic in the domain and even create and store domain events : authorChangedBookTitleEvent . Anyway i think ABAC gives more control than RBAC +1. – Tudor Jun 18 '14 at 13:01
  • Thanks! THe line between business logic and authorization logic is a fine one at times indeed. Just think about the sensitivity of the action and whether you want people to be held accountable. If it is the case, then you are likely dealing with authorization. Otherwise you're likely dealing with business logic. – David Brossard Jun 18 '14 at 15:04