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