Doorgaan naar hoofdcontent

Your own security annotation

So, I'm on a small project which has some multi-tenancy. Simply put; user A can see the bananas on his trees, and user B can see the bananas on his own trees... but they can't see each others.
But it's restfull, so ideally, you'd call something like /tree/{tree-id}/bananas
And since we know who's executing the call (since it's authenticated), we can verify that it's user A calling us, and then check which trees he can see.
If he's trying to be sneaky, and does a restcall with a treeId of B, a security violation should occur.

Okay, so how do we do that?
Well, the application has a controller for that, and we'd want to secure it there.
So, assume we have the following code:
public List<Banana> getBananasOfTree(String tree)
Since it's supposed to be annotated, we'd use something like this:
@RequestMapping("/tree/{tree-id}/bananas")
@Secured // or some other requirement 
public List<Banana> getBananasOfTree(@PathVariable("tree-id") String tree)

In spring, we have several options. The first one I tried was a HandlerInterceptor.
It's an interceptor which executes just before the method is called, in spring's handlerchain. We can make it intercept on specific annotations, so... we just add an annotation, and we're groovy.
Well, almost. The annotation needs to be  on the method, so we can't put the annotation on the pathvariable. That's okay, we can reference it, right? Well, not quite. We can't bind by name, since the parametername is not available at runtime, it's stripped away (the signature of the method is (String arg0, User arg1)) That's annoying, so we'd have to do it either by type, or by parameter index, so our (method)annotation should have an index signifying which element we'd want to check. It's getting uglier by the second.

And sadly, that's not all. The HandlerInterceptor is executed before the actual parameter resolution. Since it's a webrequest, the actual binding of the tree-id parameter hasn't occured yet. And it's not something which is easily achieved in Spring to (redo) it's binding to get the actual value.
#sosad.

So, we'll have to resort to plain AOP.
How? Well, we define a pointcut, like this:execution(public * * (.., @MyAnnotation (*), ..))
(in other words, on the execution of a public method which contains MyAnnotation on a parameter)

That allows us both to have an annotation to trigger the aspect, and we can use that same annotation to signify the parameter which should be checked.

Great!
Of course, it's not done yet.
What happens in the aspect is a lot of reflection... The JoinPoint signifies the method, from the method we can retrieve the actual method (i.e. public List<Banana> getBananaOfTrees(...)) and then we still need to figure out which argument we want to have, so we loop over all parameters, find the first one having our nice annotation, save it's parameterindex, and from the actual arguments which we retrieve from the JoinPoint, we get the argument which contains the tree-id. And then, only then, can we verify if the currently logged in user (which can be retrieved through the securitycontext) is allowed to see the tree in question. *sigh*

Of course, the securitycontext doesn't hold the actual user... it only holds the name or principal. So we'll have to do a database lookup for it.

Yeah, that's kinda sucky. So... what do we need from the user? Well, we need his tree-id. That could be put on the security context easily. So once we do that, we'll have a 'moderately' clean solution.

And there was much rejoicing...

Reacties

  1. Also, with all that reflection jazz going on, on *every* call to that tree method... it may become slow. Something to consider...

    BeantwoordenVerwijderen

Een reactie posten

Populaire posts van deze blog

Spring's conditional annotation with properties

Spring has a nice @Conditional annotation, to have the option to have beans be available in the context depending a specific condition (Of course, this can also be realized by using @Configuration objects, but that's a different post). Ideally, we'd have the option to have a condition evaluate to true or false depending on a property. Sadly, Spring does not support that out of the box. Googling and looking around gives a partial solution, but the complete one here, so we won't forget: /** * Components annotated with ConditionalOnProperty will be registered in the spring context depending on the value of a * property defined in the propertiesBeanName properties Bean. */ @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Conditional(OnPropertyCondition.class) public @interface ConditionalOnProperty { /** * The name of the property. If not found, it will evaluate to false. */ String value(); /** * if the p...

On SSL certificate generation

 So, more stuff I always forget... how to properly generate SSL certs. Well, easiest is with openssl (of course) Something like: openssl req -new -newkey rsa:2048 -nodes  -sha256 -subj "/C=NL/ST=Utrecht/L=Utrecht/O=Cooperatieve Rabobank U.A./OU=RASS Groep ICT/CN=my-common-name.host.nl" -keyout somename-prod.key -out somename-prod.csr   That can get you a certificate sign request (csr) and the appropriate key.   Of course, you want to then import those keys into a keystore. The trick to doing that is to convert it to a pkcs12 format where it can have the certificate and the key combined.   openssl pkcs12 -export -inkey somename-prod.key -in somename-prod.rabobank.nl.crt -out somename-prod.p12  Note that the crt is the signed certificate, acquired through getting the csr generated above approved..   This p12 file you can import using something like KeyStore Explorer.   After that, you can also append the root certificates of the original cert, to en...

Using spring's @transactional to only roll back when you really want to

In spring you can use the @Transactional annotation to marcate public methods as transactions. Any exception thrown in such an exception causes a rollback... Any exception? No, spring only does so on runtime exceptions. Checked exceptions are allowed and do not result in a rollback. And even then, you can allow some transactions to rollback, or not, with the proper properties for the transactions. For examle, @Transactional(rollbackFor="MyCheckedException.class") will rollback for a specific checked exception, and similarly, you can use @Transactional(noRollbackFor="MyUncheckedException.class") for unchecked exceptions. Of course, you might want to make it a little simpler on yourself. This article on stackoverflow shows us a different way: we can use our own transaction handler by overriding spring's. Let's have a sample interface which defines whether an exception should perform a rollback: interface ConfigurableRollback shouldRollbackOnE...