My initial experiment with JBoss seems to suggest that the principal object
returned by EJBContext.getCallerPrincipal is shared between all the EJB clients that have been authenticated with the same user id and password and that as long as JBoss is caching the authentication information (default timed cache of JaasSecurityManager, I suppose), the server authentication process (going through the stack of server JAAS login modules) is not executed again (when authentication information matches a cached one).
This is causing me a few concerns:
1) I would expect that two physical users, authenticating from two different machines using the same user name and password will lead to two complete authentication (going through the server JAAS login modules). Agree, it might not make sense in all cases, but it just seems "weird" to me that a real authentication is not always performed in such case.
2) Without even going that far, the same user stopping and restarting his application and authenticating again might not go through the server configured JAAS login modules again if its authentication information is cached. So, for example, let's say that the user role memberships are changed, he cannot simply logout/login to take the changes into account, he has to wait for the cached authentication information to expire.
I guess, what I am really looking for is a way to make sure that each time a client is calling loginContext.login(), the server will go through all the configured JAAS login modules to perform a full authentication. After that, as long as the client is working under the same security context (no call to login again), I have no problem (of course) with the fact that authentication is not performed for each EJB call.
I also would like that Principal objects returned by EJBContext.getCallerPrincipal are not shared between clients using the same user name and password. Basically I'd like a new principal object for every call to loginContext.login().
I am not certain of the best way to do that.
Should I create my own client login module that will generate some kind of unique number used as par of the credential to make each login unique?
Do I need to implement my own authentication cache for the JaasSecurityManager?
Do I need to go as far as creating my own security interceptors?
Any pointers will help.
So, disable caching.
Thanks for the prompt answer.
I thought that disabling caching means that authentication will be done for every EJB request (and not just between two calls to loginContext.login).
Is it not the case?
I see. Thanks for the Wiki page link.
I am still concern to rely on the client code to do the proper flushing (in my case, I have a remote EJB application).
I guess I could flush the cache before any new call to loginContext.login, but this will still not solve my problem of not sharing the server side principal object between two clients using the same credentials (I am using a custom principal class to keep extra information that should not be shared between two clients using the same credentials).
If I write my own client custom login module, could I generate a unique number at login time that JBoss could consider as part of the user credential?
I could then take advantage of all the authentication caching capabilities and still have unique "credentials" and principal object for each call to loginContext.login.
Does that make sense?
No, because the cache key is the principal name. Unless the client can provide a unqiue key the login module cannot attempt to unique it as this information will not be available in subsequent requests. The client has to be able to differentiate between uses of the same principal.
I am not sure I understand what you mean.
For example, if I want to write a login module to have user name, password and domain name (for windows), I probably need to write a custom login module for both the client and server side.
In such case, I am hoping that the same user name and password but with a different domain name will be considered as two different users (two separate authentication, nothing shared between the two). I not, it seems to be quite a security hole.
If this is correct, I can probably replace the domain name by client generated unique id that will uniquely identify each login request.
The login info is a Principal and an opaque credential. Unless the Principal differentiates between two uses of the same login name, there is no difference. Its up to you to make the Principal meaningful.
I believe that I have a working solution (tests seems to be conclusive).
Let me know if this makes senses.
1) Create a custom principal class holding user name and an extra "id" attribute.
2) Create a custom login module for the client. This custom login module is requesting only user name and password, and is generating a new unique id string for each new "login". It then creates an instance of the custom principal and put it in the share state.
3) The JAAS configuration on the client is composed of my custom login module first, then the JBoss client login module with password-stacking on in order to reuse the custom principal created with my custom login module.
It seems to work fine and it seems to be confirmed when I looked at the code of the TimedCachePolicy. The hashcode of the principal sent from the client is the real key of the authorization cache. So, provided that I define the proper hashCode method taking into account the uniquely generated id, that should do it.
Yes, that seems fine. This would not take much of a customization of the ClientLoginModule to allow for building a custom principal with info dervied from the login module config and callback handlers to push this level of support to the default jboss implementation. If you want to create a patch for this I can look at incorporating it.
I never created a patch for JBoss before, but I will give it a try.
I also would like the solution to be complete and work when using a web client going through a servlet.
Here is what I did so far:
1) Create a simple servlet that is deployed under the same JAAS security domain as my EJBs and using BASIC authentication.
2) With the default bundled tomcat configuration, it seems that the identity is automatically propagated to the EJBs (thanks to org.jboss.web.tomcat.security.SecurityAssociationValve).
3) But I am facing the same problem that I initially have with my EJB client: if two users are login through the web browser using the same user name and password, although they will be seen as two separate HTTP session, they will share the same principal object in the EJB layers.
Contrary to the EJB client case, I cannot have a custom client login module to create a custom principal. And the web UI is not going to do anything like that for me either.
Ideally, I'd like to be able to "add" the HTTP session id as part of the principal to make it unique for each client authentication.
But I am not very clear on how I can do such thing (because of my misunderstanding of the overall flow between tomcat and JBoss).
Do I need to create my own org.jboss.web.tomcat.security.SecurityAssociationValve in order to do that?
Is this valve invoked before the JAAS authentication is done?
Who is actually calling the JAAS authentication mechanism? JBossSecurityMgrRealm? If yes, I did not see it configured in any of the tomcat configuration file.
So your understanding of not being able to create a custom principal from the web tier is false. Its exactly the same as the ejb case, so explain why there is an apparent difference.
You are correct, I am confused.....
Let's say that I have two custom login modules:
- CustomClientLoginModule: used on the EJB client side to create the custom principal.
- CustomServerLoginModule: used on the EJB server side to perform my custom authentication.
In an EJB client/server mode, CustomClientLoginModule is used on the client JAAS configuration (as well as the JBoss ClientLoginModule) and CustomServerLoginModule is used on the server side (configured in conf/login-config.xml for example).
Now, I am putting a servlet in front of my EJBs. The servlet is configured to use BASIC authentication.
The first question is what should be the JAAS configuration to use.
I would like the "real" authentication to be done by the web container (so I can check for roles in my servlet code). So the JAAS configuration of my servlet (security-domain in jboss-web.xml) should definitely use my CustomServerLoginModule (I think) and nothing else (or at least I do not think so).
Granted, my CustomServerLoginModule can definitely create a custom principal than can be returned by the EJBContext.getCallerPrincipal.
But it is done "too late" as this custom principal is not the one used by the security domain credential cache (it cannot be used to add a unique id that will make two principals with same user name and password different).
I guess I am confused as where the code of my CustomClientLoginModule should go. I do not think it should be part of the servlet JAAS configuration, but may be I am wrong on that one. Still, it seems weird to stack in the same JAAS configuration my CustomClientLoginModule and CustomServerLoginModule. If this is what I have to do, it seems that it is still going to be too late (not the principal used as a key in the security domain cache).
And what about the ClientLoginModule? It should probably not be used either as what it does for an EJB client is replaced by the org.jboss.web.tomcat.security.SecurityAssociationValve, if I am not mistaken.
I am even more confused when I read some of the security post. It seems that if I want to use the FORM based authentication (instead of BASIC), then things are different. I will be the one coding the call to the LoginContext.login and it seems that I will have to use the ClientLoginModule somewhere....
I did a few more tests and I think I can try to define my configuration and question a little better:
- I have one JAAS security domain using, for example, the LDAP login module and nothing else (not ClientLoginModule or anything like that, the SecurityAssociationValve is the one taking care of propagating the credentials).
- Both the EJBs and the servlet are configured to used this JAAS security domain.
- The servlet is also configured to use BASIC authentication.
- Somehow, first time I am accessing the servlet, I give my user name and password, and someone (not sure who at this point) is calling the security domain JAAS login modules to do the authentication. I suppose that this same "someone" is adding the principal to the security domain credential cache.
- Then the SecurityAssociationValve is called and makes sure that the authenticated principal is associated with the current security context.
- When calling an EJB, credentials in the security context are found and authentication is not performed again as the principal is found in the domain credential cache.
If this is the way it is actually working, I will first be curious to know who is the "someone" calling the JAAS module (so I can look at the code and understand better)?
If I replace the LDAP login module by my custom login module, I can create a custom principal and have it returned by the EJBContext.getCallerPrincipal, but I have two problems:
1) This custom principal is not the one returned by HttpServletRequest.getUserPrincipal and I am not sure to know why.
2) This principal is not the one used as the key of the security domain cache (and this is this one that I want ot change under the cover to make it unique even if user name and password are the same).
I might have found one of the answer to my questions.
I am currently using JBoss 3.2.3 (but I am looking at JBoss 3.2.6 source code, which might explain some of my confusion).
Anyway, in JBoss 3.2.6, it seems that there is a custom principal valve that is supposed to be used so the principal created from my JAAS login modules is the one returned by HttpServletRequest.getUserPrincipal.
Is this what I need to use to have this working?
It is still not solving my principal uniqueness problem when same username and password are used, but I am still working on it .....