How to check whether a user has access to an aggregate/object

Hi everyone, I’m looking for a solution on authorization a user based on current state of an aggregate. I found this topic Security and Serialization in Axon - #9 by Allard. According to Mr.Allard suggestion, it’s best practice to use interceptor to intercept a request before dispatch to the aggregate. In my scenario, I need to have state of an aggregate to check whether a user has access or being the owner of an aggregate/object. my code may looks like this.

public class TopicAggregate {
  private String ownerId;
 
  @CommandHandler
  public void handle(DeleteTopicCommand command) {
     if (!ownerId.equals(command.getUserId())) {
        throw new AccessDeniedException();
     }
  }
}

using interceptor I won’t be able to have access to the state of an aggregate. one thing that I can think of is that to rely on query model and implement interceptor like this.

public class AuthorizationDispatchInterceptor implements MessageHandlerInterceptor<CommandMessage<?>> {
    private TopicRepository topicRepository;

    @Override
    public Object handle(UnitOfWork<? extends CommandMessage<?>> unitOfWork, InterceptorChain interceptorChain) throws Exception {
        CommandMessage<?> command = unitOfWork.getMessage();
        String userId = Optional.ofNullable(command.getMetaData().get("userId"))
                                .map(uId -> (String) uId)
                                .orElseThrow(IllegalCommandException::new);
        String ownerId = topicRepository.findById(command..getMetaData().get("topicId"));
        if (ownerId.equals(userId)) {
            return interceptorChain.proceed();
        }
        throw new AccessDeniedException();
    }
}

but that’s definitely bad practice right? since there’s the eventual consistency thing. I don’t want to put authorization logic in my aggregate either since it should be handle in separate layer. so what’s the best practice to solve this problem

I think, in this case, it’s perfectly fine to have the check in the command handler. Perhaps the confusion comes from the fact you see this as a separate access/security layer. I see it as an aggregate invariant/rule that says, “only the owner can make this change”. IMHO it is no different than “user can’t withdraw more than the total amount.” for example. In other words, if you need the aggregate state to make a decision, you should make that decision in the aggregate, even if the decision is about something that would normally be in the security/access domain.

I fully agree with Milen here!
And, you can combine this fact with Allard’s interceptor suggestion.

The interceptor interfaces you’ve shared are components registered on the dispatcher and handler infrastructure of the framework. So, these do not provide access to the handler (the aggregate, in this case).

However, you can use an annotated interceptor too, as described here in the Reference Guide. There’s a generic @MessageHandlerInterceptor, but also a command-specific @CommandHandlerInterceptor annotation. You can place these annotations on methods inside the Aggregate.

Based on the sample you gave, I think you can make an annotated-interceptor like this:

class TopicAggregate {

    private String ownerId;
    // omitting additional state for simplicity...

    @MessageHandlerInterceptor
    public void handle(@MetaDataValue("userId") String userId) {
        if (userId == null || userId.equals(ownerId) {
            throw new AccessDeniedException();
        } 
    }

    // omitting command and event sourcing handlers for simplicity...
}

The null-check is necessary here, as the @MetaDataValue annotation resolves a parameter that may be null if it is not present in the MetaData.
On any note, I’d wager the above should work, @Michale_Scofield!
So, please give it a try, and let us know if it fulfills the purpose sufficiently. :slight_smile:

1 Like