I am attempting to implement java security in an application that is 
architected as follows:

We have a web tier running in a jboss instance. This web tier has no 
datasources available to it. The only way it has access to data is through a 
business tier, a set of applications running in another jboss instance. For 
simplicity, we will refer to the two instances as web-1 and service-1.

To complicate things further in our system, we require three bits of 
information to uniquely identify a user. The loginid, the password and the 
domain. In our system, we enforce unique loginids in each domain, but they all 
live relatively harmoniously in the same database tables. The loginid in and of 
itself will not necessarily be unique, but coupled with the domain, we could 
have a primary key, although the real primary key of the user table is a userid 
column, which is what we REALLY want as our principal once we get into the 
business tier. (We allow our customers to choose their own loginid, but the 
userid is system generated)

Ok, so having read Chapter 8 of the admin guide 23 times, I understand that 
there will be two parts of the JAAS login process.

I first attempted to use the ClientLoginModule that JBoss provides to marshall 
the login credentials from the web layer to the service layer. We are 
instantiating the LoginContext with "other", which is mapped to 
ClientLoginModule in login-config.xml. (The service layer ejbs are all part of 
the same security-domain, which we'll refer to as "jwdomain")

Here is my login-config.xml snippet:



  |     <application-policy name="jwdomain">
  |        <authentication>
  |           <login-module code = "my.package.MyLoginModule" flag = "required">
  |              <module-option 
name="unauthenticatedIdentity">guest</module-option>
  |              <module-option 
name="password-stacking">useFirstPass</module-option>
  |              <module-option name="multi-threaded">true</module-option>
  |           </login-module>
  |           <login-module code="org.jboss.security.ClientLoginModule" 
flag="required">
  |              <module-option 
name="password-stacking">useFirstPass</module-option>
  |              <module-option name="multi-threaded">true</module-option>
  |                   </login-module>
  |        </authentication>
  |     </application-policy>
  |     <application-policy name = "other">
  |        <authentication>
  |           <login-module code="org.jboss.security.ClientLoginModule" 
flag="required">
  |              <module-option 
name="unauthenticatedIdentity">guest</module-option>
  |              <module-option name="multi-threaded">true</module-option>
  |                   </login-module>
  |        </authentication>
  |     </application-policy>
  | 

And a snippet from my login action in the web layer:
(Since unauthenticated I can't yet look up the userid, I get creative and 
construct a unique "username" from loginid, a string of text that we hope 
nobody ever uses as a loginid or domain, and the actual domain strings.)

ProcessLoginAction.java snippet:


  | CallbackHandler cbHandler = new UsernamePasswordHandler
  |         (new 
StringBuffer(loginid).append("UNIQUETOKEN").append(domain).toString(), 
password.toCharArray());
  | LoginContext loginContext;
  | try {
  |         loginContext = new LoginContext("other",cbHandler);
  |         loginContext.login();
  | } catch (LoginException e) {
  |         throw new ApplicationException(e);
  | }
  | 

Stepping through the executing code, things seem to progress nicely. My 
ProcessLoginAction class instantiates the "other" LoginContext, and by the time 
the commit() method is called, we can see that the Subject is populated with 
Principal and Credentials, as expected from what is passed in via the 
CallbackHandler.

After the LoginContext.login() succeeds, we invoke the create() methot on an 
EJB that has a) been set to unchecked method-permission and b) security-domain 
of "jwdomain" (See jboss.xml and ejb-jar.xml snippets below) and the server 
side of the JAAS login proceeds as planned, as we step through MyLoginModule. 
By the time we've gone through the login() and commit() method, we've done 
actual authentication on the credentials marshalled to the service login 
invocation via the client side login. So far, so good.

MyLoginModule.java:


  | public class MyLoginModule extends AbstractServerLoginModule {
  |         private Principal csIdentity;
  |         public boolean login() throws LoginException {
  |                 String userid = null;
  |                 Callback[] callbacks = new Callback[2];
  |                 callbacks[0] = new NameCallback("loginid+domain should have 
been provided by web layer");
  |                 callbacks[1] = new PasswordCallback("Password should have 
been provided by web layer",false);
  |                 String ltd = null;
  |                 String password = null;
  |                 try {
  |                         callbackHandler.handle(callbacks);
  |                         ltd = ((NameCallback)callbacks[0]).getName();
  |                         if (((PasswordCallback)callbacks[1]).getPassword() 
!= null) {
  |                                 password = new 
String(((PasswordCallback)callbacks[1]).getPassword());
  |                         }
  |                 } catch (Exception e) {
  |                         throw new LoginException(e.getMessage());
  |                 }
  |                 try {
  |                 if (ltd != null && password != null) {
  |                                 String loginid = null;
  |                                 String domain = null;
  |                                 String token = "UNIQUETOKEN";
  |                                 String[] loginiddomain = ltd.split(token);
  |                                 if ( loginiddomain.length == 2 ) {
  |                                         loginid = loginiddomain[0];
  |                                         domain = loginiddomain[1];
  |                                 }
  |                     userid = getUserid(loginid,domain);
  |                     if(userid == null ) {
  |                         throw new LoginException("Unable to determine 
unique identifier for "+loginid+"/"+assoc+".");
  |                     }
  |                     String expectedPassword = getPassword(userid);
  |                     if(password != null && 
!password.equals(expectedPassword)) {
  |                         throw new LoginException("Passwords do not match.");
  |                     }
  |                                 csIdentity = createIdentity(userid);
  |                                 loginOk = true;
  |                 }
  |                 } catch (Exception e) {
  |                         throw new LoginException(e.getMessage());
  |                 }
  |                 return super.login();
  |         }
  |     protected String getUserid(String loginid, String association) {
  |         //implementation details not needed
  |     }
  |     protected String getPassword(String userid) {
  |         //implementation details not needed
  |     }
  |         public Principal getIdentity() {
  |                 return csIdentity;
  |         }
  |         protected Group[] getRoleSets() throws LoginException {
  |                 Group roles = new SimpleGroup("Roles");
  |                 //actual implementation to follow after successful test
  |                 Principal role = new SimplePrincipal("foorole");
  |                 roles.addMember(csIdentity);
  |                 roles.addMember(role);
  |                 Group cp = new SimpleGroup("CallerPrincipal");
  |                 cp.addMember(csIdentity);
  |                 return new Group[]{roles,cp};
  |         }
  | }
  | 

The server side login() succeeds, stepping through the above code shows 
success, but the subsequent EJB create() fails with:


  | Insufficient method permissions, principal=myLoginidUNIQUETOKENmyDomain, 
ejbName=AccountManager, method=create, interface=HOME, requiredRoles=[], 
principalRoles=null
  | 

Now this failure happened while the ProcessLoginAction attempted to lookup and 
call a method on an EJB in the service layer, secured by the "jwdomain" as 
follows:


  | <jboss>
  |    <security-domain>java:/jaas/jwdomain</security-domain>
  |    <enterprise-beans>
  |       <session>
  |          <ejb-name>AccountManager</ejb-name>
  |          <jndi-name>AccountManagerManager</jndi-name>
  |          <local-jndi-name>AccountManagerManagerLocal</local-jndi-name>
  |       </session>
  | ...
  | </jboss>
  | 
But ALL of my session bean methods are flagged as unchecked! (for now)

  | <ejb-jar >
  | 
  |    <description>AccountManager to retrieve info from service 
tier</description>
  |    <enterprise-beans>
  |       <session >
  |          <ejb-name>AccountManager</ejb-name>
  |                  ...
  |          <session-type>Stateless</session-type>
  |          <transaction-type>Bean</transaction-type>
  |          <security-identity>
  |             <use-caller-identity />
  |          </security-identity>
  |       </session>
  | ...
  |    <method-permission >
  |       <unchecked/>
  |       <method >
  |          <ejb-name>AccountManager</ejb-name>
  |          <method-intf>Remote</method-intf>
  |          <method-name>create</method-name>
  |          <method-params>
  |          </method-params>
  |       </method>
  |    </method-permission>
  | ...
  | </ejb-jar >
  | 

So, I thought that maybe there's something awry between requiredRoles=[] and 
principalRoles=null.... that maybe AT LEAST ONE role is expected. So I try to 
force this to happen... I try it two ways:

The first way, I changed my login-config.xml as follows:


  |     <application-policy name = "other">
  |        <authentication>
  |           <login-module code="org.jboss.security.auth.spi.RunAsLoginModule" 
flag="required">
  |              <module-option name="roleName">foo-admin</module-option>
  |           </login-module>
  |           <login-module code="org.jboss.security.ClientLoginModule" 
flag="required">
  |              <module-option 
name="unauthenticatedIdentity">guest</module-option>
  |              <module-option name="multi-threaded">true</module-option>
  |                   </login-module>
  |        </authentication>
  |     </application-policy>
  | 

That didn't help me at all. So, I changed ClientLoginModule to a custom client 
side login module that extends UsernamePasswordLoginModule so that I could 
specify a dummy role via the getRoleSets() method, overriding the 
validatePasswords() method as well so that it would always return true (after 
all, it's a client side login module that doesn't _really_ provide any 
meaningful authentication... the sole purpose of this login module is to 
marshall the Subject Principal and credentials to the service layer where the 
real business is transacted. So, I implement a getRoleSets() method as follows:


  | public class MyClientLoginModule extends UsernamePasswordLoginModule {
  |         String password = "";
  |         public String getUsersPassword() { return password; }
  |         protected boolean validatePassword(String inputPassword, String 
expectedPassword) { return true; }
  |         protected Group[] getRoleSets() throws LoginException {
  |                 Principal identity = super.getIdentity();
  |                 Group roles = new SimpleGroup("Roles");
  |                 Principal role = new SimplePrincipal("foorole");
  |                 roles.addMember(identity);
  |                 roles.addMember(role);
  |                 return new Group[]{roles};
  |         }
  | }
  | 

Straight up, right? When I execute this code, it all _looks_ good, but still no 
dice, same error. Maybe I'm missing something in the way 
Subject<->Principal<->Principals<->Groups<->Roles all _really_ work together. 
It seems that if I can just get a bogus role associated with my client side 
Subject then maybe the EJB create() would work? 

Then again, maybe I'm just barking up the wrong tree, because what I really 
want for my principal is not that bastardized concatenization of loginid, 
unique token and domain, but the actual system primary key user id, that which 
the server side MyLoginModule identity identifies once a LoginModule is 
executing in a place where the customer provided loginid/domain/password can be 
properly authenticated and turned into a proper Principal (rather 
CallerPrincipal, what I am ultimately after)

If anything glaringly obvious jumps off the page, please enlgihten me, as I 
simply cannot see the forest through the trees.

Thanks in advance,
-david


View the original post : 
http://www.jboss.com/index.html?module=bb&op=viewtopic&p=3972803#3972803

Reply to the post : 
http://www.jboss.com/index.html?module=bb&op=posting&mode=reply&p=3972803
_______________________________________________
jboss-user mailing list
jboss-user@lists.jboss.org
https://lists.jboss.org/mailman/listinfo/jboss-user

Reply via email to