001: /*
002: * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/realm/MemoryRealm.java,v 1.13 2002/06/09 02:19:43 remm Exp $
003: * $Revision: 1.13 $
004: * $Date: 2002/06/09 02:19:43 $
005: *
006: * ====================================================================
007: *
008: * The Apache Software License, Version 1.1
009: *
010: * Copyright (c) 1999 The Apache Software Foundation. All rights
011: * reserved.
012: *
013: * Redistribution and use in source and binary forms, with or without
014: * modification, are permitted provided that the following conditions
015: * are met:
016: *
017: * 1. Redistributions of source code must retain the above copyright
018: * notice, this list of conditions and the following disclaimer.
019: *
020: * 2. Redistributions in binary form must reproduce the above copyright
021: * notice, this list of conditions and the following disclaimer in
022: * the documentation and/or other materials provided with the
023: * distribution.
024: *
025: * 3. The end-user documentation included with the redistribution, if
026: * any, must include the following acknowlegement:
027: * "This product includes software developed by the
028: * Apache Software Foundation (http://www.apache.org/)."
029: * Alternately, this acknowlegement may appear in the software itself,
030: * if and wherever such third-party acknowlegements normally appear.
031: *
032: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
033: * Foundation" must not be used to endorse or promote products derived
034: * from this software without prior written permission. For written
035: * permission, please contact apache@apache.org.
036: *
037: * 5. Products derived from this software may not be called "Apache"
038: * nor may "Apache" appear in their names without prior written
039: * permission of the Apache Group.
040: *
041: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
042: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
043: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
044: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
045: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
046: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
047: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
048: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
049: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
050: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
051: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
052: * SUCH DAMAGE.
053: * ====================================================================
054: *
055: * This software consists of voluntary contributions made by many
056: * individuals on behalf of the Apache Software Foundation. For more
057: * information on the Apache Software Foundation, please see
058: * <http://www.apache.org/>.
059: *
060: * [Additional notices, if required by prior licensing conditions]
061: *
062: */
063:
064: package org.apache.catalina.realm;
065:
066: import java.security.Principal;
067: import java.io.File;
068: import java.util.ArrayList;
069: import java.util.HashMap;
070: import org.apache.catalina.Container;
071: import org.apache.catalina.Lifecycle;
072: import org.apache.catalina.LifecycleEvent;
073: import org.apache.catalina.LifecycleException;
074: import org.apache.catalina.LifecycleListener;
075: import org.apache.catalina.Logger;
076: import org.apache.catalina.Realm;
077: import org.apache.catalina.util.LifecycleSupport;
078: import org.apache.catalina.util.StringManager;
079: import org.apache.commons.digester.Digester;
080:
081: /**
082: * Simple implementation of <b>Realm</b> that reads an XML file to configure
083: * the valid users, passwords, and roles. The file format (and default file
084: * location) are identical to those currently supported by Tomcat 3.X.
085: * <p>
086: * <strong>IMPLEMENTATION NOTE</strong>: It is assumed that the in-memory
087: * collection representing our defined users (and their roles) is initialized
088: * at application startup and never modified again. Therefore, no thread
089: * synchronization is performed around accesses to the principals collection.
090: *
091: * @author Craig R. McClanahan
092: * @version $Revision: 1.13 $ $Date: 2002/06/09 02:19:43 $
093: */
094:
095: public final class MemoryRealm extends RealmBase {
096:
097: // ----------------------------------------------------- Instance Variables
098:
099: /**
100: * The Container with which this Realm is associated.
101: */
102: private Container container = null;
103:
104: /**
105: * The Digester we will use to process in-memory database files.
106: */
107: private static Digester digester = null;
108:
109: /**
110: * Descriptive information about this Realm implementation.
111: */
112: protected final String info = "org.apache.catalina.realm.MemoryRealm/1.0";
113:
114: /**
115: * Descriptive information about this Realm implementation.
116: */
117:
118: protected static final String name = "MemoryRealm";
119:
120: /**
121: * The pathname (absolute or relative to Catalina's current working
122: * directory) of the XML file containing our database information.
123: */
124: private String pathname = "conf/tomcat-users.xml";
125:
126: /**
127: * The set of valid Principals for this Realm, keyed by user name.
128: */
129: private HashMap principals = new HashMap();
130:
131: /**
132: * The string manager for this package.
133: */
134: private static StringManager sm = StringManager
135: .getManager(Constants.Package);
136:
137: /**
138: * Has this component been started?
139: */
140: private boolean started = false;
141:
142: // ------------------------------------------------------------- Properties
143:
144: /**
145: * Return descriptive information about this Realm implementation and
146: * the corresponding version number, in the format
147: * <code><description>/<version></code>.
148: */
149: public String getInfo() {
150:
151: return info;
152:
153: }
154:
155: /**
156: * Return the pathname of our XML file containing user definitions.
157: */
158: public String getPathname() {
159:
160: return pathname;
161:
162: }
163:
164: /**
165: * Set the pathname of our XML file containing user definitions. If a
166: * relative pathname is specified, it is resolved against "catalina.base".
167: *
168: * @param pathname The new pathname
169: */
170: public void setPathname(String pathname) {
171:
172: this .pathname = pathname;
173:
174: }
175:
176: // --------------------------------------------------------- Public Methods
177:
178: /**
179: * Return the Principal associated with the specified username and
180: * credentials, if there is one; otherwise return <code>null</code>.
181: *
182: * @param username Username of the Principal to look up
183: * @param credentials Password or other credentials to use in
184: * authenticating this username
185: */
186: public Principal authenticate(String username, String credentials) {
187:
188: GenericPrincipal principal = (GenericPrincipal) principals
189: .get(username);
190:
191: boolean validated = false;
192: if (principal != null) {
193: if (hasMessageDigest()) {
194: // Hex hashes should be compared case-insensitive
195: validated = (digest(credentials)
196: .equalsIgnoreCase(principal.getPassword()));
197: } else {
198: validated = (digest(credentials).equals(principal
199: .getPassword()));
200: }
201: }
202:
203: if (validated) {
204: if (debug >= 2)
205: log(sm.getString("memoryRealm.authenticateSuccess",
206: username));
207: return (principal);
208: } else {
209: if (debug >= 2)
210: log(sm.getString("memoryRealm.authenticateFailure",
211: username));
212: return (null);
213: }
214:
215: }
216:
217: // -------------------------------------------------------- Package Methods
218:
219: /**
220: * Add a new user to the in-memory database.
221: *
222: * @param username User's username
223: * @param password User's password (clear text)
224: * @param roles Comma-delimited set of roles associated with this user
225: */
226: void addUser(String username, String password, String roles) {
227:
228: // Accumulate the list of roles for this user
229: ArrayList list = new ArrayList();
230: roles += ",";
231: while (true) {
232: int comma = roles.indexOf(',');
233: if (comma < 0)
234: break;
235: String role = roles.substring(0, comma).trim();
236: list.add(role);
237: roles = roles.substring(comma + 1);
238: }
239:
240: // Construct and cache the Principal for this user
241: GenericPrincipal principal = new GenericPrincipal(this ,
242: username, password, list);
243: principals.put(username, principal);
244:
245: }
246:
247: // ------------------------------------------------------ Protected Methods
248:
249: /**
250: * Return a configured <code>Digester</code> to use for processing
251: * the XML input file, creating a new one if necessary.
252: */
253: protected synchronized Digester getDigester() {
254:
255: if (digester == null) {
256: digester = new Digester();
257: digester.setDebug(this .debug);
258: digester.setValidating(false);
259: digester.addRuleSet(new MemoryRuleSet());
260: }
261: return (digester);
262:
263: }
264:
265: /**
266: * Return a short name for this Realm implementation.
267: */
268: protected String getName() {
269:
270: return (this .name);
271:
272: }
273:
274: /**
275: * Return the password associated with the given principal's user name.
276: */
277: protected String getPassword(String username) {
278:
279: GenericPrincipal principal = (GenericPrincipal) principals
280: .get(username);
281: if (principal != null) {
282: return (principal.getPassword());
283: } else {
284: return (null);
285: }
286:
287: }
288:
289: /**
290: * Return the Principal associated with the given user name.
291: */
292: protected Principal getPrincipal(String username) {
293:
294: return (Principal) principals.get(username);
295:
296: }
297:
298: // ------------------------------------------------------ Lifecycle Methods
299:
300: /**
301: * Prepare for active use of the public methods of this Component.
302: *
303: * @exception LifecycleException if this component detects a fatal error
304: * that prevents it from being started
305: */
306: public synchronized void start() throws LifecycleException {
307:
308: // Validate the existence of our database file
309: File file = new File(pathname);
310: if (!file.isAbsolute())
311: file = new File(System.getProperty("catalina.base"),
312: pathname);
313: if (!file.exists() || !file.canRead())
314: throw new LifecycleException(sm.getString(
315: "memoryRealm.loadExist", file.getAbsolutePath()));
316:
317: // Load the contents of the database file
318: if (debug >= 1)
319: log(sm.getString("memoryRealm.loadPath", file
320: .getAbsolutePath()));
321: Digester digester = getDigester();
322: try {
323: synchronized (digester) {
324: digester.push(this );
325: digester.parse(file);
326: }
327: } catch (Exception e) {
328: throw new LifecycleException("memoryRealm.readXml", e);
329: }
330:
331: // Perform normal superclass initialization
332: super .start();
333:
334: }
335:
336: /**
337: * Gracefully shut down active use of the public methods of this Component.
338: *
339: * @exception LifecycleException if this component detects a fatal error
340: * that needs to be reported
341: */
342: public synchronized void stop() throws LifecycleException {
343:
344: // Perform normal superclass finalization
345: super .stop();
346:
347: // No shutdown activities required
348:
349: }
350:
351: }
|