5 Replies Latest reply on Dec 7, 2005 9:19 AM by jimbrady

    Apache authentication with JBoss authorisation

    Tim Penhey Newbie

      Here is the situation.

      The company that I work for has an apache instance (somewhere, maybe more than one) that handles authentication. I want to be able to have secure webapps and EJBs that have role based authorisation.

      Firstly:
      Solaris
      Sun JDK 1.4.0
      JBoss 3.0.3, w/ Tomcat 4.0.4

      I have managed to get this working, but have one question.

      Here is how it works:

      A Ajp13Connector is defined in the tomcat4-service.xml file. The war file that I create has a jboss-web.xml file in the WEB-INF directory with a <security-domain> tag to point to the right policy in the login-config.xml. The web.xml for the war defines a basic auth-method.

      The first problem was the catalina definition of authenticated. Basically if there is a principal for the request context then the user is authenticated. The Ajp13Connector creates an Ajp13Principal with the username that apache has authenticated. Now when the tomcat container then asked the Realm if the authenticated user had a particular role, the Realm (JBossSecurityMgrRealm) then said that the user was not authenticated.

      After a week of reading catalina and jboss source code, this is what I have come up with.

      Unfortunately the JaasSecurityManager that comes with JBoss is not friendly to classes that want to derive from it, so I had to make a copy of the source code, change the name and stick it in another package. Basically all I wanted to do was to add a bit in the doesUserHaveRole method that will try to log in the principal if the Subject is null. In order for this to work, I also created another LoginModule.

      The login module that I wanted in general was the DatabaseServerLoginModule so we could store our usernames and roles in a database. So I created another class that derived from that one, and called it ApacheDatabaseServerLoginModule. In the initialize method, a copy of the principal is stored in a private variable using the SecurityAssociationCallback to get the principal. When the login method is called, a check is made to see if the principal is implemented using org.apache.ajp.tomcat4.Ajp13Principal, and if it is the function returns true, and if not, it calls the derived login.

      Effectively what happens is the following steps:
      1) Incomming connection from apache with a set principal
      2) Tomcat believes that the user is already authenticated.
      3) Asks the security manager to see if the principal has said role
      4) Security manager has no set subject, and checks to see if principal is an Ajp13Principal. If it is, it calls isValid to initiate login.
      5) Login method on derived apache login module is called, and the principal is an Ajp13 one so success.
      6) ApacheSecurityManager updates the cache with the DomainInfo and sets the active subject on the SecurityAssociation.
      7) Roles are now checked properly.

      Now, my question is...

      The next time a request is made by the user, the active subject on the SecurityAssociation is not set, so it goes through the isValid function again. This time it is caught by the validateCache method, and goes on from there. Why is the active subject not set?

      Also as a not the the author of the JaasSecurityManager, can we have the private methods as protected please? This would have meant that I could have just derived from the class instead of replaced it.

      Tim

        • 1. Re: Apache authentication with JBoss authorisation
          jimbrady Newbie

          Hi Tim,
          This is fairly old but I have exactly the same issue and wondered if there had been any progress in new Releases of JBOSS to make this easier. Also I would be very interested in your JAAS authentication using the existing principal.
          My apache front-end allows a particular servlet to be called upon authentication so maybe it is possible to use this to trigger the authentication as an alternative to fiddling with the JaasSecurityManager.
          Thanks Jim Brady

          • 2. Re: Apache authentication with JBoss authorisation
            Scott Stark Master

            Create a feature request describing the current integration point definicies. Method that are private are those that have not been sufficiently vetted as public integration points we are willing to support.

            http://jira.jboss.com/jira/browse/JBAS

            • 3. Re: Apache authentication with JBoss authorisation
              jimbrady Newbie

              I have confirmed this is still an issue - but it seems to me the correct place to fix it is in org.jboss.web.tomcat.security.SecurityAssociationValve or org.jboss.web.tomcat.security.JBossSecurityManagerRealm. The Valve assumes that if a UserPrincipal is set it will be JBossGeneralPrincipal. What happens if it is not? If some external process has set up another Principal here we are in trouble - even if JAAS principles are set up subsequently.

              The following will throw a CastException:

              if (caller == null || (caller instanceof JBossGenericPrincipal) == false)
              {
              // Look to the session for the active caller security context
              if (session != null)
              {
              principal =
              (JBossGenericPrincipal) session.getPrincipal();
              }
              }
              else
              {
              // Use the request principal as the caller identity
              principal = (JBossGenericPrincipal) caller;
              }

              The problem here of course is that we are assuming that JBOSS will drive the authentication - couldn't it be possible to make this selectable and reverse the causation if necessary.

              • 4. Re: Apache authentication with JBoss authorisation
                Scott Stark Master

                Anything is possible, but I need to see why a specific principal is needed. We already support custom principals from the jaas login module level.

                • 5. Re: Apache authentication with JBoss authorisation
                  jimbrady Newbie

                  In case this helps anybody I got this working now.

                  Our site has a Valve that checks for valid cookie and if none is found redirects to a standard authentication screen. The session has a variable added storing information related to the principal and cookie and the request is populated with a Principal containing the same information.

                  My realm then is invoked whenever a role is to be checked. It performs JAAS authorisation based on the authenticated user (in my case using a different user key). I really only needed to replace the hasRole method, but the getSecurityContext method was private so I had to copy it.

                  The code is as follows:

                  /**
                  * An extension of the JBossSecurityMgrRealm that links JBOSS JAAS with Tomcat.
                  * This extension calls the Authorisation process of the JaasSecurityManager using
                  * a principal passed from the Tomcat sign-on via org.apache.catalina.valves.GdAuthValve.
                  *
                  * @author Jim Brady
                  * @version $Revision: 1 $
                  */
                  public class DbprintJBossSecurityMgrRealm extends JBossSecurityMgrRealm
                  implements Realm {
                  static Logger log = Logger.getLogger(DbprintJBossSecurityMgrRealm.class);

                  /**
                  * Extension of JBoss JAAS Security Manager for Tomcat that
                  * adds a JAAS Authorisation call based on the UserPrincipal coming from
                  * a Tomcat Valve. This reverses the normal causation in JBOSS.
                  *
                  * Returns true if the specified user Principal
                  * has the specified security role, within the context of this
                  * Realm; otherwise return false. This will be
                  * true when an associated role Principal can be found whose
                  * getName method returns a String equalling the
                  * specified role.
                  *
                  * @param principal
                  * Principal for whom the role is to be checked
                  * @param role
                  * Security role to be checked
                  */
                  public boolean hasRole(Principal principal, String role) {

                  if ((principal == null) || (role == null)) {
                  return false;
                  }
                  if (principal instanceof JBossGenericPrincipal) {
                  return super.hasRole(principal, role);
                  } else {

                  // non-jaas signon - we need to trigger a jaas signon here

                  log.trace("Got principal of type " + principal.getClass().getName());
                  log.trace("Got principal of name " + principal.getName());
                  if (principal instanceof LDAPPrincipal) {
                  String mailuserid = (String) ((Vector) ((LDAPPrincipal) principal)
                  .getProperty("mailuserid")).elementAt(0);
                  log.trace("mailuserid " + mailuserid);
                  Principal cachePrincipal = authenticate(mailuserid
                  .toLowerCase(), "");
                  return super.hasRole(cachePrincipal, role);
                  } else {
                  Principal cachePrincipal = authenticate(principal.getName()
                  .toLowerCase(), "");
                  return super.hasRole(cachePrincipal, role);
                  }

                  }

                  }

                  /**
                  *
                  * @return JAAS Context
                  */
                  private javax.naming.Context getSecurityContext() {
                  javax.naming.Context securityCtx = null;
                  // Get the JBoss security manager from the ENC context
                  try {
                  InitialContext iniCtx = new InitialContext();
                  securityCtx = (javax.naming.Context) iniCtx
                  .lookup("java:comp/env/security");
                  } catch (NamingException e) {
                  // Apparently there is no security context?
                  }
                  return securityCtx;
                  }

                  }