KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > catalina > realm > JAASMemoryLoginModule


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18
19 package org.apache.catalina.realm;
20
21
22 import java.io.File JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.security.Principal JavaDoc;
25 import java.util.ArrayList JavaDoc;
26 import java.util.HashMap JavaDoc;
27 import java.util.Map JavaDoc;
28
29 import javax.security.auth.Subject JavaDoc;
30 import javax.security.auth.callback.Callback JavaDoc;
31 import javax.security.auth.callback.CallbackHandler JavaDoc;
32 import javax.security.auth.callback.NameCallback JavaDoc;
33 import javax.security.auth.callback.PasswordCallback JavaDoc;
34 import javax.security.auth.callback.UnsupportedCallbackException JavaDoc;
35 import javax.security.auth.login.FailedLoginException JavaDoc;
36 import javax.security.auth.login.LoginException JavaDoc;
37 import javax.security.auth.spi.LoginModule JavaDoc;
38
39 import org.apache.catalina.Context;
40 import org.apache.catalina.Realm;
41 import org.apache.catalina.connector.Request;
42 import org.apache.catalina.deploy.SecurityConstraint;
43 import org.apache.catalina.util.RequestUtil;
44 import org.apache.catalina.util.StringManager;
45 import org.apache.commons.logging.Log;
46 import org.apache.commons.logging.LogFactory;
47 import org.apache.tomcat.util.digester.Digester;
48
49
50 /**
51  * <p>Implementation of the JAAS <strong>LoginModule</strong> interface,
52  * primarily for use in testing <code>JAASRealm</code>. It utilizes an
53  * XML-format data file of username/password/role information identical to
54  * that supported by <code>org.apache.catalina.realm.MemoryRealm</code>
55  * (except that digested passwords are not supported).</p>
56  *
57  * <p>This class recognizes the following string-valued options, which are
58  * specified in the configuration file (and passed to our constructor in
59  * the <code>options</code> argument:</p>
60  * <ul>
61  * <li><strong>debug</strong> - Set to "true" to get debugging messages
62  * generated to System.out. The default value is <code>false</code>.</li>
63  * <li><strong>pathname</strong> - Relative (to the pathname specified by the
64  * "catalina.base" system property) or absolute pahtname to the
65  * XML file containing our user information, in the format supported by
66  * {@link MemoryRealm}. The default value matches the MemoryRealm
67  * default.</li>
68  * </ul>
69  *
70  * <p><strong>IMPLEMENTATION NOTE</strong> - This class implements
71  * <code>Realm</code> only to satisfy the calling requirements of the
72  * <code>GenericPrincipal</code> constructor. It does not actually perform
73  * the functionality required of a <code>Realm</code> implementation.</p>
74  *
75  * @author Craig R. McClanahan
76  * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
77  */

78
79 public class JAASMemoryLoginModule extends MemoryRealm implements LoginModule JavaDoc, Realm {
80     // We need to extend MemoryRealm to avoid class cast
81

82     private static Log log = LogFactory.getLog(JAASMemoryLoginModule.class);
83
84     // ----------------------------------------------------- Instance Variables
85

86
87     /**
88      * The callback handler responsible for answering our requests.
89      */

90     protected CallbackHandler JavaDoc callbackHandler = null;
91
92
93     /**
94      * Has our own <code>commit()</code> returned successfully?
95      */

96     protected boolean committed = false;
97
98
99     /**
100      * The configuration information for this <code>LoginModule</code>.
101      */

102     protected Map JavaDoc options = null;
103
104
105     /**
106      * The absolute or relative pathname to the XML configuration file.
107      */

108     protected String JavaDoc pathname = "conf/tomcat-users.xml";
109
110
111     /**
112      * The <code>Principal</code> identified by our validation, or
113      * <code>null</code> if validation falied.
114      */

115     protected Principal JavaDoc principal = null;
116
117
118     /**
119      * The set of <code>Principals</code> loaded from our configuration file.
120      */

121     protected HashMap JavaDoc principals = new HashMap JavaDoc();
122
123     /**
124      * The string manager for this package.
125      */

126     protected static StringManager sm =
127         StringManager.getManager(Constants.Package);
128
129     /**
130      * The state information that is shared with other configured
131      * <code>LoginModule</code> instances.
132      */

133     protected Map JavaDoc sharedState = null;
134
135
136     /**
137      * The subject for which we are performing authentication.
138      */

139     protected Subject JavaDoc subject = null;
140
141
142     // --------------------------------------------------------- Public Methods
143

144     public JAASMemoryLoginModule() {
145         log.debug("MEMORY LOGIN MODULE");
146     }
147
148     /**
149      * Phase 2 of authenticating a <code>Subject</code> when Phase 1
150      * fails. This method is called if the <code>LoginContext</code>
151      * failed somewhere in the overall authentication chain.
152      *
153      * @return <code>true</code> if this method succeeded, or
154      * <code>false</code> if this <code>LoginModule</code> should be
155      * ignored
156      *
157      * @exception LoginException if the abort fails
158      */

159     public boolean abort() throws LoginException JavaDoc {
160
161         // If our authentication was not successful, just return false
162
if (principal == null)
163             return (false);
164
165         // Clean up if overall authentication failed
166
if (committed)
167             logout();
168         else {
169             committed = false;
170             principal = null;
171         }
172         log.debug("Abort");
173         return (true);
174
175     }
176
177
178     /**
179      * Phase 2 of authenticating a <code>Subject</code> when Phase 1
180      * was successful. This method is called if the <code>LoginContext</code>
181      * succeeded in the overall authentication chain.
182      *
183      * @return <code>true</code> if the authentication succeeded, or
184      * <code>false</code> if this <code>LoginModule</code> should be
185      * ignored
186      *
187      * @exception LoginException if the commit fails
188      */

189     public boolean commit() throws LoginException JavaDoc {
190         log.debug("commit " + principal);
191
192         // If authentication was not successful, just return false
193
if (principal == null)
194             return (false);
195
196         // Add our Principal to the Subject if needed
197
if (!subject.getPrincipals().contains(principal))
198             subject.getPrincipals().add(principal);
199
200         committed = true;
201         return (true);
202
203     }
204
205     
206     /**
207      * Return the SecurityConstraints configured to guard the request URI for
208      * this request, or <code>null</code> if there is no such constraint.
209      *
210      * @param request Request we are processing
211      * @param context Context the Request is mapped to
212      */

213     public SecurityConstraint [] findSecurityConstraints(Request request,
214                                                      Context context) {
215         ArrayList JavaDoc results = null;
216         // Are there any defined security constraints?
217
SecurityConstraint constraints[] = context.findConstraints();
218         if ((constraints == null) || (constraints.length == 0)) {
219             if (context.getLogger().isDebugEnabled())
220                 context.getLogger().debug(" No applicable constraints defined");
221             return (null);
222         }
223
224         // Check each defined security constraint
225
String JavaDoc uri = request.getDecodedRequestURI();
226         String JavaDoc contextPath = request.getContextPath();
227         if (contextPath.length() > 0)
228             uri = uri.substring(contextPath.length());
229         uri = RequestUtil.URLDecode(uri); // Before checking constraints
230
String JavaDoc method = request.getMethod();
231         for (int i = 0; i < constraints.length; i++) {
232             if (context.getLogger().isDebugEnabled())
233                 context.getLogger().debug(" Checking constraint '" + constraints[i] +
234                     "' against " + method + " " + uri + " --> " +
235                     constraints[i].included(uri, method));
236             if (constraints[i].included(uri, method)) {
237                 if(results == null) {
238                     results = new ArrayList JavaDoc();
239                 }
240                 results.add(constraints[i]);
241             }
242         }
243
244         // No applicable security constraint was found
245
if (context.getLogger().isDebugEnabled())
246             context.getLogger().debug(" No applicable constraint located");
247         if(results == null)
248             return null;
249         SecurityConstraint [] array = new SecurityConstraint[results.size()];
250         System.arraycopy(results.toArray(), 0, array, 0, array.length);
251         return array;
252     }
253     
254     
255     /**
256      * Initialize this <code>LoginModule</code> with the specified
257      * configuration information.
258      *
259      * @param subject The <code>Subject</code> to be authenticated
260      * @param callbackHandler A <code>CallbackHandler</code> for communicating
261      * with the end user as necessary
262      * @param sharedState State information shared with other
263      * <code>LoginModule</code> instances
264      * @param options Configuration information for this specific
265      * <code>LoginModule</code> instance
266      */

267     public void initialize(Subject JavaDoc subject, CallbackHandler JavaDoc callbackHandler,
268                            Map JavaDoc sharedState, Map JavaDoc options) {
269         log.debug("Init");
270
271         // Save configuration values
272
this.subject = subject;
273         this.callbackHandler = callbackHandler;
274         this.sharedState = sharedState;
275         this.options = options;
276
277         // Perform instance-specific initialization
278
if (options.get("pathname") != null)
279             this.pathname = (String JavaDoc) options.get("pathname");
280
281         // Load our defined Principals
282
load();
283
284     }
285
286
287     /**
288      * Phase 1 of authenticating a <code>Subject</code>.
289      *
290      * @return <code>true</code> if the authentication succeeded, or
291      * <code>false</code> if this <code>LoginModule</code> should be
292      * ignored
293      *
294      * @exception LoginException if the authentication fails
295      */

296     public boolean login() throws LoginException JavaDoc {
297
298         // Set up our CallbackHandler requests
299
if (callbackHandler == null)
300             throw new LoginException JavaDoc("No CallbackHandler specified");
301         Callback JavaDoc callbacks[] = new Callback JavaDoc[2];
302         callbacks[0] = new NameCallback JavaDoc("Username: ");
303         callbacks[1] = new PasswordCallback JavaDoc("Password: ", false);
304
305         // Interact with the user to retrieve the username and password
306
String JavaDoc username = null;
307         String JavaDoc password = null;
308         try {
309             callbackHandler.handle(callbacks);
310             username = ((NameCallback JavaDoc) callbacks[0]).getName();
311             password =
312                 new String JavaDoc(((PasswordCallback JavaDoc) callbacks[1]).getPassword());
313         } catch (IOException JavaDoc e) {
314             throw new LoginException JavaDoc(e.toString());
315         } catch (UnsupportedCallbackException JavaDoc e) {
316             throw new LoginException JavaDoc(e.toString());
317         }
318
319         // Validate the username and password we have received
320
principal = super.authenticate(username, password);
321
322         log.debug("login " + username + " " + principal);
323
324         // Report results based on success or failure
325
if (principal != null) {
326             return (true);
327         } else {
328             throw new
329                 FailedLoginException JavaDoc("Username or password is incorrect");
330         }
331
332     }
333
334
335     /**
336      * Log out this user.
337      *
338      * @return <code>true</code> in all cases because thie
339      * <code>LoginModule</code> should not be ignored
340      *
341      * @exception LoginException if logging out failed
342      */

343     public boolean logout() throws LoginException JavaDoc {
344
345         subject.getPrincipals().remove(principal);
346         committed = false;
347         principal = null;
348         return (true);
349
350     }
351
352
353     // ---------------------------------------------------------- Realm Methods
354
// ------------------------------------------------------ Protected Methods
355

356
357     /**
358      * Load the contents of our configuration file.
359      */

360     protected void load() {
361
362         // Validate the existence of our configuration file
363
File JavaDoc file = new File JavaDoc(pathname);
364         if (!file.isAbsolute())
365             file = new File JavaDoc(System.getProperty("catalina.base"), pathname);
366         if (!file.exists() || !file.canRead()) {
367             log.warn("Cannot load configuration file " + file.getAbsolutePath());
368             return;
369         }
370
371         // Load the contents of our configuration file
372
Digester digester = new Digester();
373         digester.setValidating(false);
374         digester.addRuleSet(new MemoryRuleSet());
375         try {
376             digester.push(this);
377             digester.parse(file);
378         } catch (Exception JavaDoc e) {
379             log.warn("Error processing configuration file " +
380                 file.getAbsolutePath(), e);
381             return;
382         } finally {
383             digester.reset();
384         }
385
386     }
387 }
388
Popular Tags