cancel
Showing results for 
Search instead for 
Did you mean: 

Discussion on Re-authentication for long running transaction

ryu
Champ in-the-making
Champ in-the-making
Hi all,

Here I want to discuss about re-authentication for long running transaction in a situation as follows:

In our project, we use Activiti process as a central orchestration to call our EJBs. From my knowledge of Activiti, for handling long running transaction, the process is divided into small transactions. For example, when the process enters a UserTask, all current process's variables are commited into the database, the current transaction ends and the process goes into a wait state. When a user finishes his form, a new transaction is started again.

Our designer has different EJBs and an authentication is performed before invoking any of them. The problem is the authentication information of EJB is hold in a thread. But when your transaction ends, and you start a new transaction (new thread) after that, you will loose your authentication information. In another words, when Activiti starts a new transaction after a UserTask, how can we call an EJB with the authentication information of the previous transaction.

I'd love to hear your ideas.

Thank you very much
Regards
13 REPLIES 13

ancoron
Champ in-the-making
Champ in-the-making
Did you ever happen to secure your EJB's using either standard configuration or annotations? The problem is simple: an EJB is secured and certain methods only accessible to authenticated users that belong to a certain group/role. And exactly here is the problem: the user-to-roles mapping, which I would have to transport as well and somehow "set" them along with the user itself before invoking such a secured EJB.
Yes and no. What we did in an early stage is to get rid of the 'dogma' that at the ejb level, individual users should be authenticated. That is where we authenticated the calling application (if it was a remote call). The user (and his roles) is passed on in a standardized way (SAML token), not via methods, but via threadlocal (a strong concept! appservers use it themselves all the time). But something like http://docs.jboss.org/seam/3/security/latest/reference/en-US/html/ is also an option

Yes, I'm used to ThreadLocals which tend to break in a JavaEE environment (Thread-reuse, proprietary pooling-internals, different handling of security/transaction transport over threads, asynchronous EJB calls, …). Some time ago I already implemented a ThreadPool that takes over a SecurityContext (a class with InheritableThreadLocal information in GlassFish) from the calling Thread but makes sure that this information is not stored when a thread returns into the pool and so doesn't use old information when getting re-used by a new task. However, this works well if you know absolutely everything about how the container is going to interact/use ThreadLocals and if you can identify all of them, which is quite a no-go because it can change at any time and you cannot validate all corner-cases by yourself.

Nevertheless, I fear that I have to stick to those secured EJB's as they are not only internal but also get accessed from other external systems and/or other higher-level components (e.g. web-services) that use their own security definition but map to a certain subset of the roles defined by the EJB's. So I don't have influence at that level.

This would involve digging into the application servers code and find a way to set them programmatically. The GlassFish guys provide a simple class to "fake-authenticate" a user, however, they clearly state that this doesn't involve any real authentication and hence, no user/role mapping coming from e.g. a database or an external system.
Well, not realy. It is not difficult (assuming ejb 3.0) to create your own annotations. This works cross appserver and the annotations read the threadlocal variable with the user details.
Hm, might be an option. However, then again I'm offloading at least all the authorization issue into the application code (although hidden by annotations) to some degree which I would like to avoid, since that's the purpose of standards like JAAS or JACC, aso. I would really like to let the container do those things in a very defined way (security-realms, login-modules, …).

Yes, it's simple. However, it's not so simple in my case and simply wouldn't work.
I there are existing systems out of your control then I can imagine. If there are existing systems within your control, I'd seriously think of adapting those 😉
Exactly, I cannot control them, so I have to deal with what I get… somehow.

I've looked a bit deeper and with the SecurityContext in GlassFish I think I was on the right track. It seems that the only thing I really need is to get all of its data filled correctly. This data means: realm and subject, where the subject would have to contain the username and all the required groups assigned to it. If this is the only thing I have to do than it's completely fine, because I can retrieve the list of groups for a user from an external system. Does that sound like a solution? Of course there is a lot of potential in there to break/misuse stuff. However, there's also the notion of a ClientSecurityContext, which might be more appropriate. However, if I go that way and it works, then I effectively authenticate (from a container point of view) without using authentication at all, so in the end, I'm abusing container-internals and completely break the chain of trust (again, from a container point of view - not so for the application, because it is trusted). But again, this only works within the container and not so when using external systems and also will probably result in problems in a clustered environment.

ronald_van_kuij
Champ on-the-rise
Champ on-the-rise
This would involve digging into the application servers code and find a way to set them programmatically. The GlassFish guys provide a simple class to "fake-authenticate" a user, however, they clearly state that this doesn't involve any real authentication and hence, no user/role mapping coming from e.g. a database or an external system.
Well, not realy. It is not difficult (assuming ejb 3.0) to create your own annotations. This works cross appserver and the annotations read the threadlocal variable with the user details.
Hm, might be an option. However, then again I'm offloading at least all the authorization issue into the application code (although hidden by annotations) to some degree which I would like to avoid, since that's the purpose of standards like JAAS or JACC, aso. I would really like to let the container do those things in a very defined way (security-realms, login-modules, …).
Yep, I know the 'standards'. I've never been able to use them in a comfortable way. For my other proposal, you say 'into the application code'… I do not see it that way. It's not more than a jar that needs to be included and you can work with 'credentials' etc… Yes, you have to add annotation, but that would be needed anyway. And yes, they would not be the credentials obtained from the container, but if that is a real big major objective, then good luck 🙂 We tried to do it in a kind of container agnostic way (but in the container), and failed. This is precisely the reason frameworks like spring security (acegi) and seam security are becomming so popular.

I've looked a bit deeper and with the SecurityContext in GlassFish I think I was on the right track. It seems that the only thing I really need is to get all of its data filled correctly. This data means: realm and subject, where the subject would have to contain the username and all the required groups assigned to it. If this is the only thing I have to do than it's completely fine, because I can retrieve the list of groups for a user from an external system. Does that sound like a solution?
Just this? Hmmmm where and what are the credentials? Sounds like the 'fake authentication' you mentioned earlier.

Of course there is a lot of potential in there to break/misuse stuff. However, there's also the notion of a ClientSecurityContext, which might be more appropriate. However, if I go that way and it works, then I effectively authenticate (from a container point of view) without using authentication at all, so in the end, I'm abusing container-internals and completely break the chain of trust (again, from a container point of view - not so for the application, because it is trusted). But again, this only works within the container and not so when using external systems and also will probably result in problems in a clustered environment.

Yep…. all kinds of similar things we ran into as well… and decided not to proceed any further (you make things more complex without any real gain).

But… I won't hold you back 🙂

ancoron
Champ in-the-making
Champ in-the-making
Hehe… I've got it working.

The key thing is the implementation for Group inside the Subject. If that is exactly (or a subclass of) "org.glassfish.security.common.Group" - in Maven artifact "org.glassfish-common:common-util" - than the JACC policy can succeed and hence some existing @RolesAllowed annotation is being validated. Another key is, of course, the SecurityContext, which is the thing that Glassfish uses to access Subject, Principal (caller) and Groups for Permission checks.

So in my test project using the embedded Glassfish I didn't have to execute something like "LoginContext.login()", which even the ProgrammaticLogin class from Glassfish itself enforces to use.

So I don't have to have any access to credentials of any kind to execute an EJB as a different user.

Here's some code of my test EJB:

@Resource
private EJBContext context;

@RolesAllowed("testGroup")
@Override
public String sayHello(String name) {
    return "Hello " + name + " from " + context.getCallerPrincipal().getName() + "!";
}

…and here is my test code (updated my github repo already):

final String name = "bob";

SudoAction<String> action = new GlassfishNoLoginSudoAction<String>() {

    @Override
    public String run() throws Exception {
        SecuredEJB instance = (SecuredEJB) container.getContext().lookup("java:global/sudo/glassfish/SecuredEJB");

        return instance.sayHello(name);
    }

    @Override
    public String getRealm() {
        return "file";
    }

    @Override
    public Principal getCallerPrincipal() {
        return new MyPrincipal("alice", "somewhere");
    }

    @Override
    public Group[] getGroups() {
        Group[] groups = new Group[1];

        groups[0] = new MyGroup("testGroup");

        return groups;
    }
};

SudoService sudo = new SudoServiceGlassFish();
String result = sudo.sudo(action);

System.out.println("SUDO (no-login) invocation OK: " + result);

…as you can see I've already abstracted most of the internals here. And the result:


[#|…|JACC: new ProtectionDomain added to cache|#]

[#|…|JACC: returning cached ProtectionDomain - CodeSource: ((file:/classes/classes <no signer certificates>))
    PrincipalSet: org.ancoron.sudo.glassfish.test.MyPrincipal@1f52f43b testGroup|#]

[#|…|JACC: Access Control Decision Result: true EJBMethodPermission (Name) = SecuredEJBImpl
    (Action) = sayHello,Local,java.lang.String (Caller) = null|#]



SUDO (no-login) invocation OK: Hello bob from alice!

🙂

Of course, this experiment could be seen as compromising application server security. However, here I still just trust the application as it still has to deliver the Principal/Groups to be used for this "sudo" stuff. So how the application actually gets the list of Groups of some user is completely up to the application developer (my first approach using a real login doesn't need this as the list of roles/groups is populated at login time by the LoginModules - however, for this you need private credentials).

ancoron
Champ in-the-making
Champ in-the-making
Principal or authentication details are safe when they stay in EJB context or Security-context.

Well, what container are you running?

As Ronald has already pointed out (and tried) there is no container-agnostic way to automatically (re-)authenticate, as those details are non-standard, and this is for a reason (we lengthly discussed that here already). In the companies I've worked with so far most of the time long running transactions are either not authenticated at all (only basic user information stored along the way for auditing/logging only) or a complete custom application level security "framework" has been developed (which almost always causes problems).

My current point of view here is that you really don't need re-authentication. Most of the time you only need authorization, which I covered (at least for GlassFish 3.x) in my PoC "sudo" project using only principal + groups. What you really would have to store is a unique user identifier for your application or something like this. Then you can use a custom ActivityBehavior (or similar) to fetch required data (for the Principal + Groups) from elsewhere - so, on demand - and finally make up the authorization for the current Thread.

This seems to work quite well for GlassFish, but there is no guarantee that it will be as easy for other containers. Also you will have to fully understand how the container interacts with authentication/authorization data internally in respect to EJB sessions, threads, transactions, …

However, as also pointed out before, if you are going to "re-authenticate" upon some UserTask action then why not use the authenticated user that completed the task? If you really need to execute something "in behalf" of the user that e.g. initiated the process instance, it pretty much sounds like what I was covering with my "sudo" stuff.

In addition, remember that "long running" may also mean "picked up by a different cluster node/JVM instance" or "picked up after app server restart" and of course always involves some level of serialization. So something has to be stored somewhere to allow situations like this. And there seriously is not a single way to do these kind of things.

Anyhow, I hope this information could help you a bit.

Ancoron