Save This Page
Home » JBoss-5.1.0 » org » jboss » security » auth » spi » [javadoc | source]
    1   /*
    2   * JBoss, Home of Professional Open Source
    3   * Copyright 2005, JBoss Inc., and individual contributors as indicated
    4   * by the @authors tag. See the copyright.txt in the distribution for a
    5   * full listing of individual contributors.
    6   *
    7   * This is free software; you can redistribute it and/or modify it
    8   * under the terms of the GNU Lesser General Public License as
    9   * published by the Free Software Foundation; either version 2.1 of
   10   * the License, or (at your option) any later version.
   11   *
   12   * This software is distributed in the hope that it will be useful,
   13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   15   * Lesser General Public License for more details.
   16   *
   17   * You should have received a copy of the GNU Lesser General Public
   18   * License along with this software; if not, write to the Free
   19   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
   20   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
   21   */
   22   package org.jboss.security.auth.spi;
   23   
   24   
   25   import java.lang.reflect.Constructor;
   26   import java.security.Principal;
   27   import java.security.acl.Group;
   28   import java.util.Enumeration;
   29   import java.util.Iterator;
   30   import java.util.Map;
   31   import java.util.Set;
   32   
   33   import javax.security.auth.Subject;
   34   import javax.security.auth.callback.CallbackHandler;
   35   import javax.security.auth.login.LoginException;
   36   import javax.security.auth.spi.LoginModule;
   37   
   38   import org.jboss.logging.Logger;
   39   import org.jboss.security.NestableGroup;
   40   import org.jboss.security.SecurityConstants;
   41   import org.jboss.security.SimpleGroup;
   42   import org.jboss.security.SimplePrincipal;
   43   
   44   /**
   45    * This class implements the common functionality required for a JAAS
   46    * server side LoginModule and implements the JBossSX standard Subject usage
   47    * pattern of storing identities and roles. Subclass this module to create your
   48    * own custom LoginModule and override the login(), getRoleSets() and getIdentity()
   49    * methods.
   50    * <p>
   51    * You may also wish to override
   52    * <pre>
   53    *    public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options)
   54    * </pre>
   55    * In which case the first line of your initialize() method should be:
   56    * <pre>
   57    *    super.initialize(subject, callbackHandler, sharedState, options);
   58    * </pre>
   59    * <p>
   60    * You may also wish to override
   61    * <pre>
   62    *    public boolean login() throws LoginException
   63    * </pre>
   64    * In which case the last line of your login() method should be
   65    * <pre>
   66    *    return super.login();
   67    * </pre>
   68    *
   69    *@author <a href="edward.kenworthy@crispgroup.co.uk">Edward Kenworthy</a>, 12th Dec 2000
   70    *@author Scott.Stark@jboss.org
   71    *@version $Revision: 86122 $
   72    */
   73   public abstract class AbstractServerLoginModule implements LoginModule
   74   {
   75      protected Subject subject;
   76      protected CallbackHandler callbackHandler;
   77      @SuppressWarnings("unchecked")
   78      protected Map sharedState;
   79      @SuppressWarnings("unchecked")
   80      protected Map options;
   81      protected Logger log;
   82      /** Flag indicating if the shared credential should be used */
   83      protected boolean useFirstPass;
   84      /** Flag indicating if the login phase succeeded. Subclasses that override
   85       the login method must set this to true on successful completion of login
   86       */
   87      protected boolean loginOk;
   88      /** An optional custom Principal class implementation */
   89      protected String principalClassName;
   90      /** the principal to use when a null username and password are seen */
   91      protected Principal unauthenticatedIdentity;
   92   
   93   //--- Begin LoginModule interface methods
   94      /** Initialize the login module. This stores the subject, callbackHandler
   95       * and sharedState and options for the login session. Subclasses should override
   96       * if they need to process their own options. A call to super.initialize(...)
   97       * must be made in the case of an override.
   98       * <p>
   99       * @option password-stacking: If this is set to "useFirstPass", the login
  100       * identity will be taken from the <code>javax.security.auth.login.name</code>
  101       * value of the sharedState map, and the proof of identity from the
  102       * <code>javax.security.auth.login.password</code> value of the sharedState
  103       * map.
  104       * @option principalClass: A Principal implementation that support a ctor
  105       *   taking a String argument for the princpal name.
  106       * @option unauthenticatedIdentity: the name of the principal to asssign
  107       * and authenticate when a null username and password are seen.
  108       * 
  109       * @param subject the Subject to update after a successful login.
  110       * @param callbackHandler the CallbackHandler that will be used to obtain the
  111       *    the user identity and credentials.
  112       * @param sharedState a Map shared between all configured login module instances
  113       * @param options the parameters passed to the login module.
  114       */
  115      public void initialize(Subject subject, CallbackHandler callbackHandler,
  116         Map<String,?> sharedState, Map<String,?> options)
  117      {
  118         this.subject = subject;
  119         this.callbackHandler = callbackHandler;
  120         this.sharedState = sharedState;
  121         this.options = options;
  122         log = Logger.getLogger(getClass());
  123         log.trace("initialize");
  124   
  125         //log securityDomain, if set.
  126         log.trace("Security domain: " + 
  127   		(String)options.get(SecurityConstants.SECURITY_DOMAIN_OPTION));
  128   
  129         /* Check for password sharing options. Any non-null value for
  130            password_stacking sets useFirstPass as this module has no way to
  131            validate any shared password.
  132         */
  133         String passwordStacking = (String) options.get("password-stacking");
  134         if( passwordStacking != null && passwordStacking.equalsIgnoreCase("useFirstPass") )
  135            useFirstPass = true;
  136   
  137         // Check for a custom Principal implementation
  138         principalClassName = (String) options.get("principalClass");
  139   
  140         // Check for unauthenticatedIdentity option.
  141         String name = (String) options.get("unauthenticatedIdentity");
  142         if( name != null )
  143         {
  144            try
  145            {
  146               unauthenticatedIdentity = createIdentity(name);
  147               log.trace("Saw unauthenticatedIdentity="+name);
  148            }
  149            catch(Exception e)
  150            {
  151               log.warn("Failed to create custom unauthenticatedIdentity", e);
  152            }
  153         }
  154      }
  155   
  156      /** Looks for javax.security.auth.login.name and javax.security.auth.login.password
  157       values in the sharedState map if the useFirstPass option was true and returns
  158       true if they exist. If they do not or are null this method returns false.
  159   
  160       Note that subclasses that override the login method must set the loginOk
  161       ivar to true if the login succeeds in order for the commit phase to
  162       populate the Subject. This implementation sets loginOk to true if the
  163       login() method returns true, otherwise, it sets loginOk to false.
  164       */
  165      public boolean login() throws LoginException
  166      {
  167         log.trace("login");
  168         loginOk = false;
  169         // If useFirstPass is true, look for the shared password
  170         if( useFirstPass == true )
  171         {
  172            try
  173            {
  174               Object identity = sharedState.get("javax.security.auth.login.name");
  175               Object credential = sharedState.get("javax.security.auth.login.password");
  176               if( identity != null && credential != null )
  177               {
  178                  loginOk = true;
  179                  return true;
  180               }
  181               // Else, fall through and perform the login
  182            }
  183            catch(Exception e)
  184            {   // Dump the exception and continue
  185               log.error("login failed", e);
  186            }
  187         }
  188         return false;
  189      }
  190   
  191      /** Method to commit the authentication process (phase 2). If the login
  192       method completed successfully as indicated by loginOk == true, this
  193       method adds the getIdentity() value to the subject getPrincipals() Set.
  194       It also adds the members of each Group returned by getRoleSets()
  195       to the subject getPrincipals() Set.
  196       
  197       @see javax.security.auth.Subject;
  198       @see java.security.acl.Group;
  199       @return true always.
  200       */
  201      public boolean commit() throws LoginException
  202      {
  203         log.trace("commit, loginOk="+loginOk);
  204         if( loginOk == false )
  205            return false;
  206   
  207         Set<Principal> principals = subject.getPrincipals();
  208         Principal identity = getIdentity();
  209         principals.add(identity);
  210         Group[] roleSets = getRoleSets();
  211         for(int g = 0; g < roleSets.length; g ++)
  212         {
  213            Group group = roleSets[g];
  214            String name = group.getName();
  215            Group subjectGroup = createGroup(name, principals);
  216            if( subjectGroup instanceof NestableGroup )
  217            {
  218               /* A NestableGroup only allows Groups to be added to it so we
  219               need to add a SimpleGroup to subjectRoles to contain the roles
  220               */
  221               SimpleGroup tmp = new SimpleGroup("Roles");
  222               subjectGroup.addMember(tmp);
  223               subjectGroup = tmp;
  224            }
  225            // Copy the group members to the Subject group
  226            Enumeration<? extends Principal> members = group.members();
  227            while( members.hasMoreElements() )
  228            {
  229               Principal role = (Principal) members.nextElement();
  230               subjectGroup.addMember(role);
  231            }
  232         }
  233         return true;
  234      }
  235   
  236      /** Method to abort the authentication process (phase 2).
  237       @return true alaways
  238       */
  239      public boolean abort() throws LoginException
  240      {
  241         log.trace("abort");
  242         return true;
  243      }
  244      
  245      /** Remove the user identity and roles added to the Subject during commit.
  246       @return true always.
  247       */
  248      public boolean logout() throws LoginException
  249      {
  250         log.trace("logout");
  251         // Remove the user identity
  252         Principal identity = getIdentity();
  253         Set<Principal> principals = subject.getPrincipals();
  254         principals.remove(identity);
  255         // Remove any added Groups...
  256         return true;
  257      }
  258      //--- End LoginModule interface methods
  259      
  260      // --- Protected methods
  261      
  262      /** Overriden by subclasses to return the Principal that corresponds to
  263       the user primary identity.
  264       */
  265      abstract protected Principal getIdentity();
  266      /** Overriden by subclasses to return the Groups that correspond to the
  267       to the role sets assigned to the user. Subclasses should create at
  268       least a Group named "Roles" that contains the roles assigned to the user.
  269       A second common group is "CallerPrincipal" that provides the application
  270       identity of the user rather than the security domain identity.
  271       @return Group[] containing the sets of roles
  272       */
  273      abstract protected Group[] getRoleSets() throws LoginException;
  274      
  275      protected boolean getUseFirstPass()
  276      {
  277         return useFirstPass;
  278      }
  279      protected Principal getUnauthenticatedIdentity()
  280      {
  281         return unauthenticatedIdentity;
  282      }
  283   
  284      /** Find or create a Group with the given name. Subclasses should use this
  285       method to locate the 'Roles' group or create additional types of groups.
  286       @return A named Group from the principals set.
  287       */
  288      protected Group createGroup(String name, Set<Principal> principals)
  289      {
  290         Group roles = null;
  291         Iterator<Principal> iter = principals.iterator();
  292         while( iter.hasNext() )
  293         {
  294            Object next = iter.next();
  295            if( (next instanceof Group) == false )
  296               continue;
  297            Group grp = (Group) next;
  298            if( grp.getName().equals(name) )
  299            {
  300               roles = grp;
  301               break;
  302            }
  303         }
  304         // If we did not find a group create one
  305         if( roles == null )
  306         {
  307            roles = new SimpleGroup(name);
  308            principals.add(roles);
  309         }
  310         return roles;
  311      }
  312   
  313      /** Utility method to create a Principal for the given username. This
  314       * creates an instance of the principalClassName type if this option was
  315       * specified using the class constructor matching: ctor(String). If
  316       * principalClassName was not specified, a SimplePrincipal is created.
  317       *
  318       * @param username the name of the principal
  319       * @return the principal instance
  320       * @throws java.lang.Exception thrown if the custom principal type cannot be created.
  321       */ 
  322      @SuppressWarnings("unchecked")
  323      protected Principal createIdentity(String username)
  324         throws Exception
  325      {
  326         Principal p = null;
  327         if( principalClassName == null )
  328         {
  329            p = new SimplePrincipal(username);
  330         }
  331         else
  332         {
  333               ClassLoader loader = SecurityActions.getContextClassLoader();
  334               Class clazz = loader.loadClass(principalClassName);
  335               Class[] ctorSig = {String.class};
  336               Constructor ctor = clazz.getConstructor(ctorSig);
  337               Object[] ctorArgs = {username};
  338               p = (Principal) ctor.newInstance(ctorArgs);
  339         }
  340         return p;
  341      }
  342   }

Save This Page
Home » JBoss-5.1.0 » org » jboss » security » auth » spi » [javadoc | source]