Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » security » auth » spi » [javadoc | source]
    1   /*
    2    * JBoss, the OpenSource WebOS
    3    *
    4    * Distributable under LGPL license.
    5    * See terms of license at gnu.org.
    6    */
    7   package org.jboss.security.auth.spi;
    8   
    9   import java.security.Principal;
   10   import java.util.Map;
   11   
   12   import javax.security.auth.Subject;
   13   import javax.security.auth.callback.Callback;
   14   import javax.security.auth.callback.CallbackHandler;
   15   import javax.security.auth.callback.NameCallback;
   16   import javax.security.auth.callback.PasswordCallback;
   17   import javax.security.auth.callback.UnsupportedCallbackException;
   18   import javax.security.auth.login.FailedLoginException;
   19   import javax.security.auth.login.LoginException;
   20   
   21   import org.jboss.security.SimplePrincipal;
   22   import org.jboss.security.Util;
   23   import org.jboss.security.auth.spi.AbstractServerLoginModule;
   24   
   25   
   26   /** An abstract subclass of AbstractServerLoginModule that imposes
   27    * an identity == String username, credentials == String password view on
   28    * the login process.
   29    * <p>
   30    * Subclasses override the <code>getUsersPassword()</code>
   31    * and <code>getRoleSets()</code> methods to return the expected password and roles
   32    * for the user.
   33    *
   34    * @see #getUsername()
   35    * @see #getUsersPassword()
   36    * @see #getRoleSets()
   37    
   38    @author Scott.Stark@jboss.org
   39    @version $Revision: 1.11.4.3 $
   40    */
   41   public abstract class UsernamePasswordLoginModule extends AbstractServerLoginModule
   42   {
   43      /** The login identity */
   44      private Principal identity;
   45      /** The proof of login identity */
   46      private char[] credential;
   47      /** the principal to use when a null username and password are seen */
   48      private Principal unauthenticatedIdentity;
   49      /** the message digest algorithm used to hash passwords. If null then
   50       plain passwords will be used. */
   51      private String hashAlgorithm = null;
   52     /** the name of the charset/encoding to use when converting the password
   53      String to a byte array. Default is the platform's default encoding.
   54      */
   55      private String hashCharset = null;
   56      /** the string encoding format to use. Defaults to base64. */
   57      private String hashEncoding = null;
   58      /** A flag indicating if the password comparison should ignore case */
   59      private boolean ignorePasswordCase;
   60   
   61      /** Override the superclass method to look for a unauthenticatedIdentity
   62       property. This method first invokes the super version.
   63       @param options,
   64       @option unauthenticatedIdentity: the name of the principal to asssign
   65       and authenticate when a null username and password are seen.
   66       @option hashAlgorithm: the message digest algorithm used to hash passwords.
   67       If null then plain passwords will be used.
   68       @option hashCharset: the name of the charset/encoding to use when converting
   69       the password String to a byte array. Default is the platform's default
   70       encoding.
   71       @option hashEncoding: the string encoding format to use. Defaults to base64.
   72       @option ignorePasswordCase: A flag indicating if the password comparison
   73       should ignore case
   74       */
   75      public void initialize(Subject subject, CallbackHandler callbackHandler,
   76         Map sharedState, Map options)
   77      {
   78         super.initialize(subject, callbackHandler, sharedState, options);
   79         // Check for unauthenticatedIdentity option.
   80         String name = (String) options.get("unauthenticatedIdentity");
   81         if( name != null )
   82         {
   83            unauthenticatedIdentity = new SimplePrincipal(name);
   84            super.log.trace("Saw unauthenticatedIdentity="+name);
   85         }
   86   
   87         // Check to see if password hashing has been enabled.
   88         // If an algorithm is set, check for a format and charset.
   89         hashAlgorithm = (String) options.get("hashAlgorithm");
   90         if( hashAlgorithm != null )
   91         {
   92            hashEncoding = (String) options.get("hashEncoding");
   93            if( hashEncoding == null )
   94               hashEncoding = Util.BASE64_ENCODING;
   95            hashCharset = (String) options.get("hashCharset");
   96            if( log.isTraceEnabled() )
   97            {
   98               log.trace("Passworg hashing activated: algorithm = " + hashAlgorithm +
   99                  ", encoding = " + hashEncoding+ (hashCharset == null ? "" : "charset = " + hashCharset));
  100            }
  101         }
  102         String flag = (String) options.get("ignorePasswordCase");
  103         ignorePasswordCase = Boolean.valueOf(flag).booleanValue();
  104      }
  105   
  106      /** Perform the authentication of the username and password.
  107       */
  108      public boolean login() throws LoginException
  109      {
  110         // See if shared credentials exist
  111         if( super.login() == true )
  112         {
  113            // Setup our view of the user
  114            Object username = sharedState.get("javax.security.auth.login.name");
  115            if( username instanceof Principal )
  116               identity = (Principal) username;
  117            else
  118            {
  119               String name = username.toString();
  120               identity = new SimplePrincipal(name);
  121            }
  122            Object password = sharedState.get("javax.security.auth.login.password");
  123            if( password instanceof char[] )
  124               credential = (char[]) password;
  125            else if( password != null )
  126            {
  127               String tmp = password.toString();
  128               credential = tmp.toCharArray();
  129            }
  130            return true;
  131         }
  132   
  133         super.loginOk = false;
  134         String[] info = getUsernameAndPassword();
  135         String username = info[0];
  136         String password = info[1];
  137         if( username == null && password == null )
  138         {
  139            identity = unauthenticatedIdentity;
  140            super.log.trace("Authenticating as unauthenticatedIdentity="+identity);
  141         }
  142   
  143         if( identity == null )
  144         {
  145            identity = new SimplePrincipal(username);
  146            // Hash the user entered password if password hashing is in use
  147            if( hashAlgorithm != null )
  148               password = createPasswordHash(username, password);
  149            // Validate the password supplied by the subclass
  150            String expectedPassword = getUsersPassword();
  151            if( validatePassword(password, expectedPassword) == false )
  152            {
  153               super.log.debug("Bad password for username="+username);
  154               throw new FailedLoginException("Password Incorrect/Password Required");
  155            }
  156         }
  157   
  158         if( getUseFirstPass() == true )
  159         {    // Add the username and password to the shared state map
  160            sharedState.put("javax.security.auth.login.name", username);
  161            sharedState.put("javax.security.auth.login.password", credential);
  162         }
  163         super.loginOk = true;
  164         super.log.trace("User '" + identity + "' authenticated, loginOk="+loginOk);
  165         return true;
  166      }
  167   
  168      protected Principal getIdentity()
  169      {
  170         return identity;
  171      }
  172      protected Principal getUnauthenticatedIdentity()
  173      {
  174         return unauthenticatedIdentity;
  175      }
  176   
  177      protected Object getCredentials()
  178      {
  179         return credential;
  180      }
  181      protected String getUsername()
  182      {
  183         String username = null;
  184         if( getIdentity() != null )
  185            username = getIdentity().getName();
  186         return username;
  187      }
  188   
  189      /** Called by login() to acquire the username and password strings for
  190       authentication. This method does no validation of either.
  191       @return String[], [0] = username, [1] = password
  192       @exception LoginException thrown if CallbackHandler is not set or fails.
  193       */
  194      protected String[] getUsernameAndPassword() throws LoginException
  195      {
  196         String[] info = {null, null};
  197         // prompt for a username and password
  198         if( callbackHandler == null )
  199         {
  200            throw new LoginException("Error: no CallbackHandler available " +
  201            "to collect authentication information");
  202         }
  203         NameCallback nc = new NameCallback("User name: ", "guest");
  204         PasswordCallback pc = new PasswordCallback("Password: ", false);
  205         Callback[] callbacks = {nc, pc};
  206         String username = null;
  207         String password = null;
  208         try
  209         {
  210            callbackHandler.handle(callbacks);
  211            username = nc.getName();
  212            char[] tmpPassword = pc.getPassword();
  213            if( tmpPassword != null )
  214            {
  215               credential = new char[tmpPassword.length];
  216               System.arraycopy(tmpPassword, 0, credential, 0, tmpPassword.length);
  217               pc.clearPassword();
  218               password = new String(credential);
  219            }
  220         }
  221         catch(java.io.IOException ioe)
  222         {
  223            throw new LoginException(ioe.toString());
  224         }
  225         catch(UnsupportedCallbackException uce)
  226         {
  227            throw new LoginException("CallbackHandler does not support: " + uce.getCallback());
  228         }
  229         info[0] = username;
  230         info[1] = password;
  231         return info;
  232      }
  233   
  234     /**
  235      * If hashing is enabled, this method is called from <code>login()</code>
  236      * prior to password validation.
  237      * <p>
  238      * Subclasses may override it to provide customized password hashing,
  239      * for example by adding user-specific information or salting.
  240      * <p>
  241      * The default version calculates the hash based on the following options:
  242      * <ul>
  243      * <li><em>hashAlgorithm</em>: The digest algorithm to use.
  244      * <li><em>hashEncoding</em>: The format used to store the hashes (base64 or hex)
  245      * <li><em>hashCharset</em>: The encoding used to convert the password to bytes
  246      * for hashing.
  247      * </ul>
  248      * It will return null if the hash fails for any reason, which will in turn
  249      * cause <code>validatePassword()</code> to fail.
  250      * 
  251      * @param username ignored in default version
  252      * @param password the password string to be hashed
  253      */
  254      protected String createPasswordHash(String username, String password)
  255      {
  256         String passwordHash = Util.createPasswordHash(hashAlgorithm, hashEncoding,
  257            hashCharset, username, password);
  258         return passwordHash;
  259      }
  260   
  261      /** A hook that allows subclasses to change the validation of the input
  262       password against the expected password. This version checks that
  263       neither inputPassword or expectedPassword are null that that
  264       inputPassword.equals(expectedPassword) is true;
  265       @return true if the inputPassword is valid, false otherwise.
  266       */
  267      protected boolean validatePassword(String inputPassword, String expectedPassword)
  268      {
  269         if( inputPassword == null || expectedPassword == null )
  270            return false;
  271         boolean valid = false;
  272         if( ignorePasswordCase == true )
  273            valid = inputPassword.equalsIgnoreCase(expectedPassword);
  274         else
  275            valid = inputPassword.equals(expectedPassword);
  276         return valid;
  277      }
  278   
  279      /** Get the expected password for the current username available via
  280       the getUsername() method. This is called from within the login()
  281       method after the CallbackHandler has returned the username and
  282       candidate password.
  283       @return the valid password String
  284       */
  285      abstract protected String getUsersPassword() throws LoginException;
  286      
  287   }

Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » security » auth » spi » [javadoc | source]