dev@glassfish.java.net

Re: How to "sudo" in a JAAS/JavaEE world?

From: Michael Hollatz <michael.hollatz_at_profitbricks.com>
Date: Wed, 16 Mar 2011 00:47:07 +0100

Hi devs,

I've spent quite some time to dig through the code but I think I've got
it, at least a first test shows up with the correct caller principal and
even groups/roles can be used at the target bean (verified using
security annotations) and I don't have to use JNDI lookups (injected EJB
works just fine).

The key was using the SecurityContext class, but let's step through is
one after another:


1.) Create a subject (here, I just use password credentials for a
password login module for simplicity):

Set<Object> credsPrivate = new HashSet<Object>();
PasswordCredential pwd = new PasswordCredential(
        user,
        passwd.toCharArray(),
        "myCustomJDBCRealm");
credsPrivate.add(pwd);

// note the "final" here...
final Subject s = new Subject(false,
        new HashSet<Principal>(),
        new HashSet<Object>(),
        credsPrivate);


2.) Issue a real JAAS login to get the subject filled with groups:

LoginContext lc = new LoginContext("myCustomJDBCRealm", s);
lc.login();


3.) Store the current SecurityContext:

final SecurityContext secCtx = SecurityContext.getCurrent();

try {


4.) Create a new SecurityContext based on our own:

    final SecurityContext newCtx =
            AccessController.doPrivileged(
            new PrivilegedAction<SecurityContext>() {
        public SecurityContext run()
        {
            return new SecurityContext(user, s);
        }
    });


5.) Set the new one as current (ThreadLocal):

    SecurityContext.setCurrent(newCtx);


6.) Invoke anything you want to execute as the desired user:

    return myBean.getName();


7.) Reset the current SecurityContext to proceed as usual:

} finally {
    SecurityContext.setCurrent(secCtx);
}



So, devs, what do you think about this solution? Although I've tested it
inside a web application accessed through a Servlet and two EJBs (the
"sudo" code residing inside a EJB itself) I don't know if this approach
would work anywhere.

I already took a look at ProgrammaticLogin, but as it states "...it
bypasses the standard J2EE mechanisms..." I thought, I'd choose another
path that is closer to what is used by the real JavaEE mechanisms.

So, I'd like to get feedback from you.


Cheers,

        Kane


On 03/15/2011 05:01 PM, Michael Hollatz wrote:
> Hi *,
>
> I have a simple problem:
>
> I have the requirement to run code by the means of a specific user. This
> has to work with all aspects, as code I then have to invoke as that
> specific user depends on code like 'EJBContext.getCallerPrincipal' to
> return the current running user (the one I "sudo" to).
>
> For a test I just made up two EJBs, "BeanA" calling "BeanB", but before
> that I do something like this:
>
> Set<Object> credsPrivate = new HashSet<Object>();
> PasswordCredential pwd = new PasswordCredential(
> user,
> passwd.toCharArray(),
> "myCustomJDBCRealm");
> credsPrivate.add(pwd);
>
> final Subject s = new Subject(false,
> new HashSet<Principal>(),
> new HashSet<Object>(),
> credsPrivate);
> LoginContext lc = new LoginContext("myCustomJDBCRealm", s);
> lc.login();
>
> The problem is, that this works pretty well, but when entering the
> "BeanB" the getCallerPrincipal always returns "ANONYMOUS", although in
> the same thread.
>
> Here is some log output:
>
> FINE: Login module initialized: class
> com.something.security.realm.CustomJDBCLoginModule
> FINEST: JDBC login succeeded for: bob groups:[Test Group]
> FINE: JAAS login complete.
> FINE: JAAS authentication committed.
>
> INFO: Called BeanB with 'ANONYMOUS'...
>
> FINE: JAAS logout for: Subject:
> Principal: bob
> Principal: Test Group
> Principal: com.something.security.impl.IdentityPrincipalImpl_at_c2e62fba
> Private Credential: Realm=myCustomJDBCRealm Username=bob
> Password=######## TargetName =
>
>
> So I would like to know if there is a way to accomplish that.
>
> Please help.
>
>
> Cheers,
>
> kane