9 Replies Latest reply on Mar 12, 2008 12:31 AM by sbiwal

    Problems Accessing Realm (JBossSecurityMgrRealm) from Tomcat

    jofree

      I'm working on a custom Single Sign On solution and am having problems with JBossSecurityMgrRealm not working from within my Tomcat Valve and I can't figure out why. Here are the details:

      I'm using JBoss AS 4.0.4GA.

      I have the following code inside of a custom Tomcat Valve:

      //Context context = request.getContext();
      //Realm realm = context.getRealm();
      Realm realm = this.getContainer().getRealm();
      Principal newPrincipal = realm.authenticate(user, pass);
      


      Both variations produce the same unexpected results (authentication always fails), I traced this through the JBoss code and found that the JBossSecurityMgrRealm class has a method getSecurityContext() which always returns null when called from within my valve.

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


      The code in my custom Authenticator seems to work.

      Realm realm = context.getRealm();
      ...
      principal = realm.authenticate(username, password);
      


      The only difference that I have been able to find is that the context variable from AuthenticatorBase is set in an overridden setContainer method from ValveBase, here is AuthenticatorBase.setContainer:

       public void setContainer(Container container) {
      
       if (!(container instanceof Context))
       throw new IllegalArgumentException
       (sm.getString("authenticator.notContext"));
      
       super.setContainer(container);
       this.context = (Context) container;
      
       }
      


      When I try to mimic this behavior in my custom valve, I find that my container is not an instance of Context. The only other thing that I am doing is using the configClass attribute of the host entry in server.xml to install my custom authenticator.

      <Host name="localhost" autoDeploy="false"
      deployOnStartup="false" deployXML="false"
      configClass="com.acxiom.web.sso.config.ContextConfig">
       <Valve className="com.acxiom.web.sso.SingleSignOnValve" />
      


      public class ContextConfig extends org.apache.catalina.startup.ContextConfig {
      
       public ContextConfig() {
       super();
       try {
       Map authMap = this.getAuthenticators();
       if (authMap.size() > 0) {
       customAuthenticators = authMap;
       }
       }
       catch (Exception e) {
       //TODO Error Checking
       }
       }
      
       private Map getAuthenticators() throws Exception {
       Map cmap = new HashMap();
       ClassLoader tcl = Thread.currentThread().getContextClassLoader();
       Authenticator form = (Authenticator)tcl.loadClass("com.acxiom.web.sso.FormAuthenticator").newInstance();
      
       cmap.put("FORM", form);
      
       return cmap;
       }
      }
      


      I don't want to change any of the behavior of the Realm, only the Valve and Authenticators. Any help figuring this out would be appreciated, sorry for the verbose message, but I wanted to make sure all the relevant information was included.

      Thanks,

      Josh Freeman

        • 1. Re: Problems Accessing Realm (JBossSecurityMgrRealm) from To
          j2ee_junkie

          Josh,

          This is an interesting problem, that is not imediately obvious to me. But, I can offer some suggestions to remove potential issues. That way we can narrow the focus better.


          1 Your Valve is acting as an authenticator. As such be sure your custom authenticator(i.e. valve) extends Tomcat's AuthenticatorBase.

          2 I would try to remove the use of your ContextConfig class. See http://wiki.jboss.org/wiki/Wiki.jsp?page=ExternalizeTomcatAuthenticators on how to set your custom authenticator.


          Lets see how that works, cgriffith

          • 2. Re: Problems Accessing Realm (JBossSecurityMgrRealm) from To
            jofree

            Thanks for the quick reply.

            Actually I have two classes (following the pattern from Tomcat) an Authenticator that replaces the Form Authenticator and extends AuthenticatorBase (this is what is configured in my ContextConfig class) and a SingleSignOnValve which extends org.apache.catalina.authenticator.SingleSignOn, configured as a Valve in server.xml. I can't make SingleSignOnValve extend AuthenticatorBase because it is already extending SingleSignOn. I suppose since they both are Valves I could try to merge the two classes together?

            The problem occurs in SingleSignOnValve, and works correctly in FormAuthenticator.

            It seems that the issue is the availabiltiy/visibility of the ENC JNDI name 'java:comp/env/security'?

            Also, the reason I have the ContextConfig class is because in 4.0.4GA the JBossContextConfig class is missing. (See the JIRA issue in the Wiki post you referenced.)

            Also, it just occurred to me that I referenced two Context classes in my code snippets. So just to clarify the JBossSecurityMgrRealm code snippet uses javax.naming.Context, all other snippets reference org.apache.catalina.Context.

            Thanks again,

            Josh Freeman

            • 3. Re: Problems Accessing Realm (JBossSecurityMgrRealm) from To
              j2ee_junkie

              Josh,

              With the added detail you provided, I have a better picture. I am not suggesting that your SSO valve extend authenticator. I thought you only had that one valve that was acting a both a SSO valve, and an authenticator.

              The Context that is stored in AuthenticatorBase (as refered by context.getRealm()) may not be the same Context that is stored in the request (as used by the valve.) Is there a way you can find out what those implementations are?

              cgriffith

              • 4. Re: Problems Accessing Realm (JBossSecurityMgrRealm) from To
                jofree

                Sure, I checked and the instance of JBossSecurityMgrRealm that I get back from all of the different paths is the same instance. So no matter where or how I get reference to the realm, I am getting the same instance which is the desired behavior. However, in some cases it finds the JNDI lookup and others it doesn't (this lookup happens on every call to authenticate and isn't part of the JBossSecurityMgrRealm instance).

                The context and container from the authenticator are the same instance and are of type org.apache.catalina.core.StandardContext.

                The context from request.getContext() is also of type org.apache.catalina.core.StandardContext.

                The SingleSignOnValve's this.getContainer() method returns an object of type org.apache.catalina.core.StandardHost, and the container.getContext() method returns an object of type org.apache.catalina.core.StandardContext.

                Thanks again,

                Josh Freeman

                • 5. Re: Problems Accessing Realm (JBossSecurityMgrRealm) from To
                  j2ee_junkie

                  I going out on a limb here...

                  When executing your custom Authenticator, the Classloader is your web app's classloader. Then the realm is obtained using same classloader. So JNDI context is that of the webapp's classloader.

                  Because your custom SSO valve is set at Host level, it has a different classloader when it is executed.

                  If this is the case, you will need to see if you get the classloader from the request somehow.

                  cgriffith

                  • 6. Re: Problems Accessing Realm (JBossSecurityMgrRealm) from To
                    jofree

                    Yes, that seems to be the case. In fact, I've realized that is why you need both an Authenticator (valve) and a 'SingleSignOn' valve. The SSO valve keeps a cache at the host level's class loader, and the authenticator (web app level) accesses the SSO valve to store and retrieve entries in/from the cache. This allows SSO to operate between web application boundaries.

                    Having said that, I've solved my issue by moving the code into the authenticator and making calls back to the SSO valve as appropriate.

                    I still believe that the JBossSecurityMgrRealm should work from either context, and think that it is odd that the same instance of the class would provide differing behaviors, as it is accessible and completely logical to call from either context.

                    Thanks for all of your help j2ee_junkie.

                    Josh Freeman

                    • 7. Re: Problems Accessing Realm (JBossSecurityMgrRealm) from To
                      j2ee_junkie

                      Josh,

                      I am glad you are on the track to fixing your problem. I do want to comment on your last statement. When an web-application is being deployed, a private JNDI context is constructed by deployer for that web-app. It would be a bad idea to have web-apps share one JNDI context. That said, JBoss stores a reference to the security service needed by that web-app in that JNDI context. This is what allows each web-app to link into JBoss' security layer specific for that application. It is unreasonable to expect JBossSecurityMgrRealm to work outside of an applications JNDI context.

                      later, cgriffith

                      • 8. Re: Problems Accessing Realm (JBossSecurityMgrRealm) from To
                        sbiwal

                        Hi Josh,

                        I am facing a similar issue as you did sometime back ie. the JBossSecurityMgrRealm instance that is provided to me in my custom valve has a null securityCtx. I am still a newbee to Jboss and JAAS. I could not understand your last post as to which code did you move to the authenticator ? In my case I have only created a custom valve - NO custom authenticator.

                        Can you please help me with this as I have been struggling with this issue for a week now.

                        Thanks,
                        Swati

                        • 9. Re: Problems Accessing Realm (JBossSecurityMgrRealm) from To
                          sbiwal

                          Hi Josh,

                          Some more information on my problem. I am just implementing a basic custom valve (and not a SSO valve). I am attaching the code for my valve below. Isn't this similar to the authenticator that you talk about in your last post ? If so then the Realm object that I get should have the JNDI namespace correctly set ?

                          /*
                           * JBoss, Home of Professional Open Source.
                           * Copyright 2006, Red Hat Middleware LLC, and individual contributors
                           * as indicated by the @author tags. See the copyright.txt file in the
                           * distribution for a full listing of individual contributors.
                           *
                           * This is free software; you can redistribute it and/or modify it
                           * under the terms of the GNU Lesser General Public License as
                           * published by the Free Software Foundation; either version 2.1 of
                           * the License, or (at your option) any later version.
                           *
                           * This software is distributed in the hope that it will be useful,
                           * but WITHOUT ANY WARRANTY; without even the implied warranty of
                           * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
                           * Lesser General Public License for more details.
                           *
                           * You should have received a copy of the GNU Lesser General Public
                           * License along with this software; if not, write to the Free
                           * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
                           * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
                           */
                          package org.jboss.web.tomcat.security;
                          
                          import java.io.IOException;
                          import java.security.Principal;
                          import java.security.acl.Group;
                          import java.util.ArrayList;
                          import java.util.List;
                          
                          import javax.security.auth.Subject;
                          import javax.servlet.ServletException;
                          
                          import org.apache.catalina.connector.Request;
                          import org.apache.catalina.connector.Response;
                          import org.apache.catalina.Context;
                          import org.apache.catalina.Session;
                          
                          import org.apache.catalina.realm.GenericPrincipal;
                          import org.apache.catalina.valves.ValveBase;
                          import org.jboss.logging.Logger;
                          import org.jboss.security.SecurityAssociation;
                          import org.jboss.security.SimpleGroup;
                          
                          //import com.sun.security.auth.UserPrincipal;
                          
                          //import com.sun.security.auth.UserPrincipal;
                          
                          /** A valve that provides information on the jaas login exception seen in the
                           SecurityAssociation exception data. The useExceptionAsMsg flag indicates if
                           the exception message should be set as the http response message. The
                           exceptionHeader attribute if set is the header name that should be populated
                           with the exception message.
                          
                           @author Scott.Stark@jboss.org
                           @version $Revision: 57206 $
                           */
                          public class BasicAuthValve
                           extends ValveBase
                          {
                           private static Logger log = Logger.getLogger(BasicAuthValve.class);
                           private static boolean trace = log.isTraceEnabled();
                          
                           /** Should the exception message be used as the request status message */
                           private boolean useExceptionAsMsg = false;
                           /** A flag indicating if the auth exception thread local should be cleared */
                           private boolean clearAuthException = true;
                           /** The name of the reply header to use to return the exception message */
                           private String exceptionHeader = null;
                          
                           public boolean isUseExceptionAsMsg()
                           {
                           return useExceptionAsMsg;
                           }
                           public void setUseExceptionAsMsg(boolean useExceptionAsMsg)
                           {
                           this.useExceptionAsMsg = useExceptionAsMsg;
                           }
                          
                           public String getExceptionHeader()
                           {
                           return exceptionHeader;
                           }
                           public void setExceptionHeader(String exceptionHeader)
                           {
                           this.exceptionHeader = exceptionHeader;
                           }
                          
                           public void invoke(Request request, Response response)
                           throws IOException, ServletException
                           {
                           // TODO Auto-generated method stub
                           List roles = new ArrayList();
                           roles.add("Authenticated");
                           roles.add("User");
                           roles.add("Admin");
                           roles.add("CustomRole");
                          
                           String password = "user";
                           String username = "user";
                          
                           username = request.getRemoteUser();
                           if (username != null) {
                           Principal p = this.getContainer().getRealm().authenticate(username, (String)null);
                           request.setAuthType("FORM");
                           request.setUserPrincipal(new GenericPrincipal(request.getContext().getRealm(), username, password, roles));
                           }
                          
                           this.getNext().invoke(request, response);
                           if (request.getAttribute("org.jboss.portal.logout") != null) {
                           request.getSession().invalidate();
                           }
                           }
                          
                          }