KickJava   Java API By Example, From Geeks To Geeks.

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


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
10 import java.security.Principal JavaDoc;
11 import java.security.acl.Group JavaDoc;
12 import java.util.Enumeration JavaDoc;
13 import java.util.Iterator JavaDoc;
14 import java.util.Map JavaDoc;
15 import java.util.Set JavaDoc;
16 import java.lang.reflect.Constructor JavaDoc;
17
18 import javax.security.auth.Subject JavaDoc;
19 import javax.security.auth.callback.CallbackHandler JavaDoc;
20 import javax.security.auth.login.LoginException JavaDoc;
21 import javax.security.auth.spi.LoginModule JavaDoc;
22
23 import org.jboss.logging.Logger;
24 import org.jboss.security.NestableGroup;
25 import org.jboss.security.SimpleGroup;
26 import org.jboss.security.SimplePrincipal;
27
28 /**
29  * This class implements the common functionality required for a JAAS
30  * server side LoginModule and implements the JBossSX standard Subject usage
31  * pattern of storing identities and roles. Subclass this module to create your
32  * own custom LoginModule and override the login(), getRoleSets() and getIdentity()
33  * methods.
34  * <p>
35  * You may also wish to override
36  * <pre>
37  * public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options)
38  * </pre>
39  * In which case the first line of your initialize() method should be:
40  * <pre>
41  * super.initialize(subject, callbackHandler, sharedState, options);
42  * </pre>
43  * <p>
44  * You may also wish to override
45  * <pre>
46  * public boolean login() throws LoginException
47  * </pre>
48  * In which case the last line of your login() method should be
49  * <pre>
50  * return super.login();
51  * </pre>
52  *
53  *@author <a HREF="edward.kenworthy@crispgroup.co.uk">Edward Kenworthy</a>, 12th Dec 2000
54  *@author Scott.Stark@jboss.org
55  *@version $Revision: 1.12.4.1 $
56  */

57 public abstract class AbstractServerLoginModule implements LoginModule JavaDoc
58 {
59    protected Subject JavaDoc subject;
60    protected CallbackHandler JavaDoc callbackHandler;
61    protected Map JavaDoc sharedState;
62    protected Map JavaDoc options;
63    protected Logger log;
64    /** Flag indicating if the shared credential should be used */
65    protected boolean useFirstPass;
66    /** Flag indicating if the login phase succeeded. Subclasses that override
67     the login method must set this to true on successful completion of login
68     */

69    protected boolean loginOk;
70    /** An optional custom Principal class implementation */
71    protected String JavaDoc principalClassName;
72    /** the principal to use when a null username and password are seen */
73    protected Principal JavaDoc unauthenticatedIdentity;
74
75 //--- Begin LoginModule interface methods
76
/** Initialize the login module. This stores the subject, callbackHandler
77     * and sharedState and options for the login session. Subclasses should override
78     * if they need to process their own options. A call to super.initialize(...)
79     * must be made in the case of an override.
80     * <p>
81     * option: password-stacking: If this is set to "useFirstPass", the login
82     * identity will be taken from the <code>javax.security.auth.login.name</code>
83     * value of the sharedState map, and the proof of identity from the
84     * <code>javax.security.auth.login.password</code> value of the sharedState
85     * map.
86     * option: principalClass: A Principal implementation that support a ctor
87     * taking a String argument for the princpal name.
88     * option: unauthenticatedIdentity: the name of the principal to asssign
89     * and authenticate when a null username and password are seen.
90     *
91     * @param subject the Subject to update after a successful login.
92     * @param callbackHandler the CallbackHandler that will be used to obtain the
93     * the user identity and credentials.
94     * @param sharedState a Map shared between all configured login module instances
95     * @param options the parameters passed to the login module.
96     */

97    public void initialize(Subject JavaDoc subject, CallbackHandler JavaDoc callbackHandler,
98       Map JavaDoc sharedState, Map JavaDoc options)
99    {
100       this.subject = subject;
101       this.callbackHandler = callbackHandler;
102       this.sharedState = sharedState;
103       this.options = options;
104       log = Logger.getLogger(getClass());
105       if( log.isTraceEnabled() )
106          log.trace("initialize, instance=@"+System.identityHashCode(this));
107      /* Check for password sharing options. Any non-null value for
108          password_stacking sets useFirstPass as this module has no way to
109          validate any shared password.
110       */

111       String JavaDoc passwordStacking = (String JavaDoc) options.get("password-stacking");
112       if( passwordStacking != null && passwordStacking.equalsIgnoreCase("useFirstPass") )
113          useFirstPass = true;
114
115       // Check for a custom Principal implementation
116
principalClassName = (String JavaDoc) options.get("principalClass");
117
118       // Check for unauthenticatedIdentity option.
119
String JavaDoc name = (String JavaDoc) options.get("unauthenticatedIdentity");
120       if( name != null )
121       {
122          try
123          {
124             unauthenticatedIdentity = createIdentity(name);
125             log.trace("Saw unauthenticatedIdentity="+name);
126          }
127          catch(Exception JavaDoc e)
128          {
129             log.warn("Failed to create custom unauthenticatedIdentity", e);
130          }
131       }
132    }
133
134    /** Looks for javax.security.auth.login.name and javax.security.auth.login.password
135     values in the sharedState map if the useFirstPass option was true and returns
136     true if they exist. If they do not or are null this method returns false.
137
138     Note that subclasses that override the login method must set the loginOk
139     ivar to true if the login succeeds in order for the commit phase to
140     populate the Subject. This implementation sets loginOk to true if the
141     login() method returns true, otherwise, it sets loginOk to false.
142     */

143    public boolean login() throws LoginException JavaDoc
144    {
145       log.trace("login");
146       loginOk = false;
147       // If useFirstPass is true, look for the shared password
148
if( useFirstPass == true )
149       {
150          try
151          {
152             Object JavaDoc identity = sharedState.get("javax.security.auth.login.name");
153             Object JavaDoc credential = sharedState.get("javax.security.auth.login.password");
154             if( identity != null && credential != null )
155             {
156                loginOk = true;
157                return true;
158             }
159             // Else, fall through and perform the login
160
}
161          catch(Exception JavaDoc e)
162          { // Dump the exception and continue
163
log.error("login failed", e);
164          }
165       }
166       return false;
167    }
168
169    /** Method to commit the authentication process (phase 2). If the login
170     method completed successfully as indicated by loginOk == true, this
171     method adds the getIdentity() value to the subject getPrincipals() Set.
172     It also adds the members of each Group returned by getRoleSets()
173     to the subject getPrincipals() Set.
174     
175     @see javax.security.auth.Subject;
176     @see java.security.acl.Group;
177     @return true always.
178     */

179    public boolean commit() throws LoginException JavaDoc
180    {
181       log.trace("commit, loginOk="+loginOk);
182       if( loginOk == false )
183          return false;
184
185       Set JavaDoc principals = subject.getPrincipals();
186       Principal JavaDoc identity = getIdentity();
187       principals.add(identity);
188       Group JavaDoc[] roleSets = getRoleSets();
189       for(int g = 0; g < roleSets.length; g ++)
190       {
191          Group JavaDoc group = roleSets[g];
192          String JavaDoc name = group.getName();
193          Group JavaDoc subjectGroup = createGroup(name, principals);
194          if( subjectGroup instanceof NestableGroup )
195          {
196             /* A NestableGroup only allows Groups to be added to it so we
197             need to add a SimpleGroup to subjectRoles to contain the roles
198             */

199             SimpleGroup tmp = new SimpleGroup("Roles");
200             subjectGroup.addMember(tmp);
201             subjectGroup = tmp;
202          }
203          // Copy the group members to the Subject group
204
Enumeration JavaDoc members = group.members();
205          while( members.hasMoreElements() )
206          {
207             Principal JavaDoc role = (Principal JavaDoc) members.nextElement();
208             subjectGroup.addMember(role);
209          }
210       }
211       return true;
212    }
213
214    /** Method to abort the authentication process (phase 2).
215     @return true alaways
216     */

217    public boolean abort() throws LoginException JavaDoc
218    {
219       log.trace("abort");
220       return true;
221    }
222    
223    /** Remove the user identity and roles added to the Subject during commit.
224     @return true always.
225     */

226    public boolean logout() throws LoginException JavaDoc
227    {
228       log.trace("logout");
229       // Remove the user identity
230
Principal JavaDoc identity = getIdentity();
231       Set JavaDoc principals = subject.getPrincipals();
232       principals.remove(identity);
233       // Remove any added Groups...
234
return true;
235    }
236    //--- End LoginModule interface methods
237

238    // --- Protected methods
239

240    /** Overriden by subclasses to return the Principal that corresponds to
241     the user primary identity.
242     */

243    abstract protected Principal JavaDoc getIdentity();
244    /** Overriden by subclasses to return the Groups that correspond to the
245     to the role sets assigned to the user. Subclasses should create at
246     least a Group named "Roles" that contains the roles assigned to the user.
247     A second common group is "CallerPrincipal" that provides the application
248     identity of the user rather than the security domain identity.
249     @return Group[] containing the sets of roles
250     */

251    abstract protected Group JavaDoc[] getRoleSets() throws LoginException JavaDoc;
252    
253    protected boolean getUseFirstPass()
254    {
255       return useFirstPass;
256    }
257    protected Principal JavaDoc getUnauthenticatedIdentity()
258    {
259       return unauthenticatedIdentity;
260    }
261
262    /** Find or create a Group with the given name. Subclasses should use this
263     method to locate the 'Roles' group or create additional types of groups.
264     @return A named Group from the principals set.
265     */

266    protected Group JavaDoc createGroup(String JavaDoc name, Set JavaDoc principals)
267    {
268       Group JavaDoc roles = null;
269       Iterator JavaDoc iter = principals.iterator();
270       while( iter.hasNext() )
271       {
272          Object JavaDoc next = iter.next();
273          if( (next instanceof Group JavaDoc) == false )
274             continue;
275          Group JavaDoc grp = (Group JavaDoc) next;
276          if( grp.getName().equals(name) )
277          {
278             roles = grp;
279             break;
280          }
281       }
282       // If we did not find a group create one
283
if( roles == null )
284       {
285          roles = new SimpleGroup(name);
286          principals.add(roles);
287       }
288       return roles;
289    }
290
291    /** Utility method to create a Principal for the given username. This
292     * creates an instance of the principalClassName type if this option was
293     * specified using the class constructor matching: ctor(String). If
294     * principalClassName was not specified, a SimplePrincipal is created.
295     *
296     * @param username the name of the principal
297     * @return the principal instance
298     * @throws java.lang.Exception thrown if the custom principal type cannot be created.
299     */

300    protected Principal JavaDoc createIdentity(String JavaDoc username)
301       throws Exception JavaDoc
302    {
303       Principal JavaDoc p = null;
304       if( principalClassName == null )
305       {
306          p = new SimplePrincipal(username);
307       }
308       else
309       {
310             ClassLoader JavaDoc loader = Thread.currentThread().getContextClassLoader();
311             Class JavaDoc clazz = loader.loadClass(principalClassName);
312             Class JavaDoc[] ctorSig = {String JavaDoc.class};
313             Constructor JavaDoc ctor = clazz.getConstructor(ctorSig);
314             Object JavaDoc[] ctorArgs = {username};
315             p = (Principal JavaDoc) ctor.newInstance(ctorArgs);
316       }
317       return p;
318    }
319 }
320
Popular Tags