KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > security > auth > spi > UsernamePasswordLoginModule


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 JavaDoc;
10 import java.util.Map JavaDoc;
11 import java.util.HashMap JavaDoc;
12
13 import javax.security.auth.Subject JavaDoc;
14 import javax.security.auth.callback.Callback JavaDoc;
15 import javax.security.auth.callback.CallbackHandler JavaDoc;
16 import javax.security.auth.callback.NameCallback JavaDoc;
17 import javax.security.auth.callback.PasswordCallback JavaDoc;
18 import javax.security.auth.callback.UnsupportedCallbackException JavaDoc;
19 import javax.security.auth.login.FailedLoginException JavaDoc;
20 import javax.security.auth.login.LoginException JavaDoc;
21
22 import org.jboss.security.Util;
23 import org.jboss.crypto.digest.DigestCallback;
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  * @see #createIdentity(String)
38  
39  @author Scott.Stark@jboss.org
40  @version $Revision: 1.18.4.2 $
41  */

42 public abstract class UsernamePasswordLoginModule extends AbstractServerLoginModule
43 {
44    /** The login identity */
45    private Principal JavaDoc identity;
46    /** The proof of login identity */
47    private char[] credential;
48    /** the message digest algorithm used to hash passwords. If null then
49     plain passwords will be used. */

50    private String JavaDoc hashAlgorithm = null;
51   /** the name of the charset/encoding to use when converting the password
52    String to a byte array. Default is the platform's default encoding.
53    */

54    private String JavaDoc hashCharset = null;
55    /** the string encoding format to use. Defaults to base64. */
56    private String JavaDoc hashEncoding = null;
57    /** A flag indicating if the password comparison should ignore case */
58    private boolean ignorePasswordCase;
59
60    /** Override the superclass method to look for the following options after
61     first invoking the super version.
62     @param options :
63     @option hashAlgorithm - the message digest algorithm used to hash passwords.
64     If null then plain passwords will be used.
65     @option hashCharset - the name of the charset/encoding to use when converting
66     the password String to a byte array. Default is the platform's default
67     encoding.
68     @option hashEncoding - the string encoding format to use. Defaults to base64.
69     @option ignorePasswordCase: A flag indicating if the password comparison
70       should ignore case.
71     @option digestCallback - The class name of the DigestCallback {@link org.jboss.crypto.digest.DigestCallback}
72       implementation that includes pre/post digest content like salts.
73     */

74    public void initialize(Subject JavaDoc subject, CallbackHandler JavaDoc callbackHandler,
75       Map JavaDoc sharedState, Map JavaDoc options)
76    {
77       super.initialize(subject, callbackHandler, sharedState, options);
78
79       // Check to see if password hashing has been enabled.
80
// If an algorithm is set, check for a format and charset.
81
hashAlgorithm = (String JavaDoc) options.get("hashAlgorithm");
82       if( hashAlgorithm != null )
83       {
84          hashEncoding = (String JavaDoc) options.get("hashEncoding");
85          if( hashEncoding == null )
86             hashEncoding = Util.BASE64_ENCODING;
87          hashCharset = (String JavaDoc) options.get("hashCharset");
88          if( log.isTraceEnabled() )
89          {
90             log.trace("Password hashing activated: algorithm = " + hashAlgorithm
91                + ", encoding = " + hashEncoding
92                + ", charset = " + (hashCharset == null ? "{default}" : hashCharset)
93                + ", callback = " + options.get("digestCallback")
94             );
95          }
96       }
97       String JavaDoc flag = (String JavaDoc) options.get("ignorePasswordCase");
98       ignorePasswordCase = Boolean.valueOf(flag).booleanValue();
99    }
100
101    /** Perform the authentication of the username and password.
102     */

103    public boolean login() throws LoginException JavaDoc
104    {
105       // See if shared credentials exist
106
if( super.login() == true )
107       {
108          // Setup our view of the user
109
Object JavaDoc username = sharedState.get("javax.security.auth.login.name");
110          if( username instanceof Principal JavaDoc )
111             identity = (Principal JavaDoc) username;
112          else
113          {
114             String JavaDoc name = username.toString();
115             try
116             {
117                identity = createIdentity(name);
118             }
119             catch(Exception JavaDoc e)
120             {
121                log.debug("Failed to create principal", e);
122                throw new LoginException JavaDoc("Failed to create principal: "+ e.getMessage());
123             }
124          }
125          Object JavaDoc password = sharedState.get("javax.security.auth.login.password");
126          if( password instanceof char[] )
127             credential = (char[]) password;
128          else if( password != null )
129          {
130             String JavaDoc tmp = password.toString();
131             credential = tmp.toCharArray();
132          }
133          return true;
134       }
135
136       super.loginOk = false;
137       String JavaDoc[] info = getUsernameAndPassword();
138       String JavaDoc username = info[0];
139       String JavaDoc password = info[1];
140       if( username == null && password == null )
141       {
142          identity = unauthenticatedIdentity;
143          super.log.trace("Authenticating as unauthenticatedIdentity="+identity);
144       }
145
146       if( identity == null )
147       {
148          try
149          {
150             identity = createIdentity(username);
151          }
152          catch(Exception JavaDoc e)
153          {
154             log.debug("Failed to create principal", e);
155             throw new LoginException JavaDoc("Failed to create principal: "+ e.getMessage());
156          }
157
158          // Hash the user entered password if password hashing is in use
159
if( hashAlgorithm != null )
160             password = createPasswordHash(username, password);
161          // Validate the password supplied by the subclass
162
String JavaDoc expectedPassword = getUsersPassword();
163          if( validatePassword(password, expectedPassword) == false )
164          {
165             super.log.debug("Bad password for username="+username);
166             throw new FailedLoginException JavaDoc("Password Incorrect/Password Required");
167          }
168       }
169
170       if( getUseFirstPass() == true )
171       { // Add the username and password to the shared state map
172
sharedState.put("javax.security.auth.login.name", username);
173          sharedState.put("javax.security.auth.login.password", credential);
174       }
175       super.loginOk = true;
176       super.log.trace("User '" + identity + "' authenticated, loginOk="+loginOk);
177       return true;
178    }
179
180    protected Principal JavaDoc getIdentity()
181    {
182       return identity;
183    }
184    protected Principal JavaDoc getUnauthenticatedIdentity()
185    {
186       return unauthenticatedIdentity;
187    }
188
189    protected Object JavaDoc getCredentials()
190    {
191       return credential;
192    }
193    protected String JavaDoc getUsername()
194    {
195       String JavaDoc username = null;
196       if( getIdentity() != null )
197          username = getIdentity().getName();
198       return username;
199    }
200
201    /** Called by login() to acquire the username and password strings for
202     authentication. This method does no validation of either.
203     @return String[], [0] = username, [1] = password
204     @exception LoginException thrown if CallbackHandler is not set or fails.
205     */

206    protected String JavaDoc[] getUsernameAndPassword() throws LoginException JavaDoc
207    {
208       String JavaDoc[] info = {null, null};
209       // prompt for a username and password
210
if( callbackHandler == null )
211       {
212          throw new LoginException JavaDoc("Error: no CallbackHandler available " +
213          "to collect authentication information");
214       }
215       NameCallback JavaDoc nc = new NameCallback JavaDoc("User name: ", "guest");
216       PasswordCallback JavaDoc pc = new PasswordCallback JavaDoc("Password: ", false);
217       Callback JavaDoc[] callbacks = {nc, pc};
218       String JavaDoc username = null;
219       String JavaDoc password = null;
220       try
221       {
222          callbackHandler.handle(callbacks);
223          username = nc.getName();
224          char[] tmpPassword = pc.getPassword();
225          if( tmpPassword != null )
226          {
227             credential = new char[tmpPassword.length];
228             System.arraycopy(tmpPassword, 0, credential, 0, tmpPassword.length);
229             pc.clearPassword();
230             password = new String JavaDoc(credential);
231          }
232       }
233       catch(java.io.IOException JavaDoc ioe)
234       {
235          throw new LoginException JavaDoc(ioe.toString());
236       }
237       catch(UnsupportedCallbackException JavaDoc uce)
238       {
239          throw new LoginException JavaDoc("CallbackHandler does not support: " + uce.getCallback());
240       }
241       info[0] = username;
242       info[1] = password;
243       return info;
244    }
245
246   /**
247    * If hashing is enabled, this method is called from <code>login()</code>
248    * prior to password validation.
249    * <p>
250    * Subclasses may override it to provide customized password hashing,
251    * for example by adding user-specific information or salting.
252    * <p>
253    * The default version calculates the hash based on the following options:
254    * <ul>
255    * <li><em>hashAlgorithm</em>: The digest algorithm to use.
256    * <li><em>hashEncoding</em>: The format used to store the hashes (base64 or hex)
257    * <li><em>hashCharset</em>: The encoding used to convert the password to bytes
258    * for hashing.
259    * <li><em>digestCallback</em>: The class name of the
260    * org.jboss.security.auth.spi.DigestCallback implementation that includes
261    * pre/post digest content like salts.
262    * </ul>
263    * It will return null if the hash fails for any reason, which will in turn
264    * cause <code>validatePassword()</code> to fail.
265    *
266    * @param username ignored in default version
267    * @param password the password string to be hashed
268    * @throws SecurityException - thrown if there is a failure to load the
269    * digestCallback
270    */

271    protected String JavaDoc createPasswordHash(String JavaDoc username, String JavaDoc password)
272    {
273       DigestCallback callback = null;
274       String JavaDoc callbackClassName = (String JavaDoc) options.get("digestCallback");
275       if( callbackClassName != null )
276       {
277          try
278          {
279             ClassLoader JavaDoc loader = Thread.currentThread().getContextClassLoader();
280             Class JavaDoc callbackClass = loader.loadClass(callbackClassName);
281             callback = (DigestCallback) callbackClass.newInstance();
282             if( log.isTraceEnabled() )
283                log.trace("Created DigestCallback: "+callback);
284          }
285          catch (Exception JavaDoc e)
286          {
287             if( log.isTraceEnabled() )
288                log.trace("Failed to load DigestCallback", e);
289             SecurityException JavaDoc ex = new SecurityException JavaDoc("Failed to load DigestCallback");
290             ex.initCause(e);
291             throw ex;
292          }
293          HashMap JavaDoc tmp = new HashMap JavaDoc(options);
294          tmp.put("javax.security.auth.login.name", username);
295          tmp.put("javax.security.auth.login.password", password);
296          callback.init(tmp);
297       }
298       String JavaDoc passwordHash = Util.createPasswordHash(hashAlgorithm, hashEncoding,
299          hashCharset, username, password, callback);
300       return passwordHash;
301    }
302
303    /** A hook that allows subclasses to change the validation of the input
304     password against the expected password. This version checks that
305     neither inputPassword or expectedPassword are null that that
306     inputPassword.equals(expectedPassword) is true;
307     @return true if the inputPassword is valid, false otherwise.
308     */

309    protected boolean validatePassword(String JavaDoc inputPassword, String JavaDoc expectedPassword)
310    {
311       if( inputPassword == null || expectedPassword == null )
312          return false;
313       boolean valid = false;
314       if( ignorePasswordCase == true )
315          valid = inputPassword.equalsIgnoreCase(expectedPassword);
316       else
317          valid = inputPassword.equals(expectedPassword);
318       return valid;
319    }
320
321
322    /** Get the expected password for the current username available via
323     the getUsername() method. This is called from within the login()
324     method after the CallbackHandler has returned the username and
325     candidate password.
326     @return the valid password String
327     */

328    abstract protected String JavaDoc getUsersPassword() throws LoginException JavaDoc;
329    
330 }
331
Popular Tags