Search code examples
jakarta-eeauthenticationejbjaas

How can I change user principal during an EJB/MDB invocation?


I have an application in which some operations are performed by MDB. These MDB all use a @RunAs(SYSTEM) annotation to mark them as system elements.

One of these MDB has to run some code which is protecetd through @RolesAllowed(WORKSPACE), which the SYSTEM role doesn't have, obviously, but which the user (a human being, mind you) that started the process has.

So, my question is quite simple : is there any way (through asynchronous invocation, as an example) to have my MDB to change its principal to be my user instead of SYSTEM ?


Solution

  • Like Mike Braun answer suggest, this is not possible according to JavaEE specifications.

    And that is unfortunate. But, what is a little less unfortunate is that there is some code to do that kind of things (application-server specific), hidden in that application server implementation of @RunAs. In Glassfish, that particular code is in the com.sun.enterprise.security.auth.login.LoginContextDriver class, and particularly in its LoginContextDriver#loginPrincipal method.

    So, to have one part of code using a specific principal, I defined an interface

    public interface Sudoer {
        public <Result> Result sudo(String user, SudoOperation<Result> operation);
    }
    

    which I implemented for Glassfish as so :

    public class GlassfishSudoer implements Sudoer {
        @Override
        public <Result> Result sudo(String user, SudoOperation<Result> operation) {
            try {
                LoginContextDriver.loginPrincipal(user, "autocat");
                return operation.perform();
            } catch (Exception e) {
                throw new UnableToSudoException(e);
            } finally {
                LoginContextDriver.logout();
            }
        }
    }
    

    And when using it, the part which wants to have some code "sudoed" only has to provide an implementation of the SudoOperation, like

        component.sudo(userLogin, new SudoOperation<Void>() {
            public Void perform() {
                /* do some sudoed code */
                return null;
            }
        });
    

    Advantage of this method is that, provided the given application server has some code to handle @RunAs, you can use that code to implement your own sudoer (I'm thinking about extracting that into a sudo-ejb library ...).