Spring Security in Axon

Hi Allard et all.

This is a follow-up on the thread Security and Serialization in Axon but with more focus on Spring Security. I read some info on the group, specially this and this posts, and also issue AXON-281.

So here’s a couple of question:

  • regarding AXON-281, what is the status of it? Is it only for v3.0?

  • you said in one of the threads

If possible, I would annotate the commands themselves and use an interceptor to validate commands against the roles of the principal sending them.

Why is that? I understand that allows to validate at a early stage, but what about security concerns like the “man-in-the-middle”? If the command contains it’s own ACL, that can be changed in transit thus surpassing the end-point security.

Also related, the use of @MetaData to hold Id’s and Acl’s couldn’t also have this problems?

  • you also said

On my own projects, I use a specific command gateway instance for internal components (e.g. sagas) that attaches a special authentication token to commands.

Can you elaborate a little on that?

Ans now to my own concerns. At the moment (and this is already the production design) I’m using the AsynchronousCommandBus and the AsyncAnnotatedSagaManager. The intention here is to use only the Spring SecurityContextHolder to propagate the authentication. We have (for now) two interceptors:

 AuthenticationInterceptor implements CommandDispatchInterceptor
 AuthorizationInterceptor implements CommandHandlerInterceptor

Is no problem to propagate the Context between the (and further on the line to the Aggregate) by using the MODE_INHERITABLETHREADLOCAL instead of the default one. The problem is after the Aggregate. After a event is raised, a Saga is invoked, that in turn it sends some commands to the bus. It is in the Saga that the authentication is lost.

The obvious solution will be, I think, to catch the thread that is started by the AsyncAnnotatedSagaManager and set the authentication on the new thread as well. If this is indeed the solution, where should this be made?

The rest is not so bad until now :slight_smile: I have a loosely based implementation of the Human-Task specification (one aggregate and a couple of sagas and listeners) that looks stable enough and will be now used by other parties, hopefully without much fuss.

It’s quite difficult to troubleshoot and debug this kind of designs, if there are any “rules of thumb” or “good practices” I would love to hear it.

I’ll be following your webinar today, so I’ll hear you soon… :slight_smile:

BTW, I also saw this post regarding a Axon Event Store Server, is there any news on it?

Thanks.

After wasting some time on it I now think it’s impossible to do it the way I wanted to. So, picking on what you said:

On my own projects, I use a specific command gateway instance for internal components (e.g. sagas) that attaches a special authentication token to commands.

I got the idea of just attaching a uuid to the command metadata and at some point add it to the event and at some point setting the SecurityContextHolder.

However, the only places I found I where I wanted to put that code are:

AsyncSagaEventProcessor::onEvent - Can't extend, it's final class

One more thing, I notice I can do all the Saga security by extending SagaMethodMessageHandler since I have there all the info I needed (the Saga to invoke and the message metada) but I don’t know where this is used/configured. Any help will be great.

`
public void invoke(Object target, EventMessage message) {
if (!isHandlerAvailable()) {
return;
}
try {
handlerMethod.invoke(target, message);
} catch (IllegalAccessException e) {

`

Cheers.

Hi Antonio,

since your saga probably extends AbstractAnnotatedSaga, you can simply override the handle method there and do your ThreadLocal stuff around a super.handle call.

One thing I’d like to point out is that event handling isn’t -really- done in the context of a user action. It’s the systems response to activity of a specific user. Will you ever want to refuse to handle an event because a user doesn’t have specific right? The decisions have been made, and security concerns were part of that decision.

Regarding the annotations for security on commands, I don’t see how that allows for a man in the middle attack. The command objects are part of your own application. They are constructed based on serialized data, but the annotations are part of the static class data.

Cheers,

Allard

Hi, thanks for your reply. Please note my comments below.

Hi Antonio,

since your saga probably extends AbstractAnnotatedSaga, you can simply override the handle method there and do your ThreadLocal stuff around a super.handle call.

Hmm, that’s what I wanted to do, but that method is final…


public final void handle([EventMessage](http://www.axonframework.org/apidocs/2.4/org/axonframework/domain/EventMessage.html) event)

This is crucial for us, because the events in our case must have Authentication and Acountability, and must propagate it to Commands that are sent from the Events.

One thing I’d like to point out is that event handling isn’t -really- done in the context of a user action. It’s the systems response to activity of a specific user. Will you ever want to refuse to handle an event because a user doesn’t have specific right? The decisions have been made, and security concerns were part of that decision.

Yes, that was my opinion as well, that only commands should be secured and not events. However that applies to Authorization (can the current user do this or not?) while events, being run “on behalf” or “by initiative” of a user, must be aware if that user is who he claims to be, so it should be able to perform Authenticationm(and Acountability)

Regarding the annotations for security on commands, I don’t see how that allows for a man in the middle attack. The command objects are part of your own application. They are constructed based on serialized data, but the annotations are part of the static class data.

Hmmm, I see your point, I have to investigate that further.

But the crucial point here for us at the moment is preciselly the first one, how to access metadata to do Authentication before the Saga handler methods, that would be ideally done in the AbstractAnnotatedSaga::handle, was it not for being final…

Thanks again for your support.

Cheers.

Hi Antonio,

authentication is something you also only need to do on the command side. If you need to know in which (user) context a specific event was raised, then you can use a meta data field to contain the id of that user. For Event Handlers, that’s just a “given”. They don’t need to (again) validate that information.

Checkout the AuditingInterceptor to find out how you can attach meta data to all outgoing events.

I agree that the handle method being final is unfortunate. However, I am not sure the reason why you would want to use the method is the right one.

Cheers,

Allard

I didn’t explain myself correctly, we are already doing that, attach meta data to all outgoing events (raised by the aggregate, when they exit the aggregate). The problem is further down the line. Let me show in the code:

The event, with the auth id in the metada is handled on a saga, but on the handler itself I don’t have access to the metada:

@StartSaga()

@SagaEventHandler(associationProperty = "aggregateId")
public void handle(ProcessCreatedEvent event) {
    aggregateId = event.getAggregateId();

I think you need to check section 6.2.2 of the reference guide. You can easily access the events meta data abd add it to an outgoing command.

Cheers,

Allard

I don’t get exactly what you mean, and I don;t have my code here to test. Are you saying that I should wrap my Saga in a#### AnnotationEventListenerAdapter and override it's handle method?

`
@Override
public void handle(EventMessage event) {
String auth = event.getMetaData().get(“AUTH”);
SecurityContextHolder.getContext().setAuthentication(auth);
invoker.invokeHandlerMethod(event);
}

`

And since the command will be invoked on the same thread, just extend theCommandGateway to get the auth from the context and add it to the Command Metada?

I’ll try that tomorrow, just in case :slight_smile:

Cheers.

Hi António,

What Allard means is that you can easily inject the event’s metadata into the @SagaEventHandler method and add it to the command message:

@StartSaga
@SagaEventHandler(associationProperty = “aggregateId”)
public void handle(ProcessCreatedEvent event, MetaData metaData) {
aggregateId = event.getAggregateId();
commandGateway.send(new CreateTaskCommand(aggregateId), metaData);
}

Regards,
Rene

Hi thanks for that. I did knew I could get the metadata as a param, but we don’t want to do that because we want our programmers to be aware of “business-only” aspects of the sagas. And that correlation is more a “infrastructure” thing.

I didn’t knew that it was so simple to add the metadata to the command, however, and that can be very useful, yes. I’ll try with that tomorrow.

Thanks for your help.

You’ll have to define your own CommandGateway interface for that (though Axon creates the implementation for you).

Alternatively you can pass a command message to the gateway.

Regarding the correlation data, you can register a CorrelationDataProvider with the saga manager. The metadata selected by that provider (based on the metadata in the event) will then be transferred to commands dispatched from the saga.

YAY, that works!!! I just used the CorrelationDataProvider and didn’t even add to implement nothing in the CommandGateway since we has already a SecurityInterceptor where I just added a couple of lines (well, I’m assuming the Interceptor runs on the same thread of the CommandGateway, but if not we just change the gateway).

I often feel so stupid for looking for complicated solutions when in fact there is a solution that is… simple!!! But I guess that comes with the territory…

Many many thanks Allard and René.

Cheers.