001: /*
002: * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/users/MemoryUserDatabase.java,v 1.6 2002/02/10 03:20:17 craigmcc Exp $
003: * $Revision: 1.6 $
004: * $Date: 2002/02/10 03:20:17 $
005: *
006: * ====================================================================
007: * The Apache Software License, Version 1.1
008: *
009: * Copyright (c) 2002 The Apache Software Foundation. All rights
010: * reserved.
011: *
012: * Redistribution and use in source and binary forms, with or without
013: * modification, are permitted provided that the following conditions
014: * are met:
015: *
016: * 1. Redistributions of source code must retain the above copyright
017: * notice, this list of conditions and the following disclaimer.
018: *
019: * 2. Redistributions in binary form must reproduce the above copyright
020: * notice, this list of conditions and the following disclaimer in
021: * the documentation and/or other materials provided with the
022: * distribution.
023: *
024: * 3. The end-user documentation included with the redistribution, if
025: * any, must include the following acknowlegement:
026: * "This product includes software developed by the
027: * Apache Software Foundation (http://www.apache.org/)."
028: * Alternately, this acknowlegement may appear in the software itself,
029: * if and wherever such third-party acknowlegements normally appear.
030: *
031: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
032: * Foundation" must not be used to endorse or promote products derived
033: * from this software without prior written permission. For written
034: * permission, please contact apache@apache.org.
035: *
036: * 5. Products derived from this software may not be called "Apache"
037: * nor may "Apache" appear in their names without prior written
038: * permission of the Apache Group.
039: *
040: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
041: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
042: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
043: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
044: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
045: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
046: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
047: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
048: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
049: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
050: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
051: * SUCH DAMAGE.
052: * ====================================================================
053: *
054: * This software consists of voluntary contributions made by many
055: * individuals on behalf of the Apache Software Foundation. For more
056: * information on the Apache Software Foundation, please see
057: * <http://www.apache.org/>.
058: *
059: * [Additional notices, if required by prior licensing conditions]
060: *
061: */
062:
063: package org.apache.catalina.users;
064:
065: import java.io.File;
066: import java.io.FileInputStream;
067: import java.io.FileOutputStream;
068: import java.io.IOException;
069: import java.io.OutputStreamWriter;
070: import java.io.PrintWriter;
071: import java.util.HashMap;
072: import java.util.Iterator;
073: import org.apache.catalina.Group;
074: import org.apache.catalina.Role;
075: import org.apache.catalina.User;
076: import org.apache.catalina.UserDatabase;
077: import org.apache.catalina.util.StringManager;
078: import org.apache.commons.digester.Digester;
079: import org.apache.commons.digester.ObjectCreationFactory;
080: import org.xml.sax.Attributes;
081:
082: /**
083: * <p>Concrete implementation of {@link UserDatabase} that loads all
084: * defined users, groups, and roles into an in-memory data structure,
085: * and uses a specified XML file for its persistent storage.</p>
086: *
087: * @author Craig R. McClanahan
088: * @version $Revision: 1.6 $ $Date: 2002/02/10 03:20:17 $
089: * @since 4.1
090: */
091:
092: public class MemoryUserDatabase implements UserDatabase {
093:
094: // ----------------------------------------------------------- Constructors
095:
096: /**
097: * Create a new instance with default values.
098: */
099: public MemoryUserDatabase() {
100:
101: super ();
102:
103: }
104:
105: /**
106: * Create a new instance with the specified values.
107: *
108: * @param id Unique global identifier of this user database
109: */
110: public MemoryUserDatabase(String id) {
111:
112: super ();
113: this .id = id;
114:
115: }
116:
117: // ----------------------------------------------------- Instance Variables
118:
119: /**
120: * The set of {@link Group}s defined in this database, keyed by
121: * group name.
122: */
123: protected HashMap groups = new HashMap();
124:
125: /**
126: * The unique global identifier of this user database.
127: */
128: protected String id = null;
129:
130: /**
131: * The relative (to <code>catalina.base</code>) or absolute pathname to
132: * the XML file in which we will save our persistent information.
133: */
134: protected String pathname = "conf/tomcat-users.xml";
135:
136: /**
137: * The relative or absolute pathname to the file in which our old
138: * information is stored while renaming is in progress.
139: */
140: protected String pathnameOld = pathname + ".old";
141:
142: /**
143: * The relative or absolute pathname ot the file in which we write
144: * our new information prior to renaming.
145: */
146: protected String pathnameNew = pathname + ".new";
147:
148: /**
149: * The set of {@link Role}s defined in this database, keyed by
150: * role name.
151: */
152: protected HashMap roles = new HashMap();
153:
154: /**
155: * The string manager for this package.
156: */
157: private static StringManager sm = StringManager
158: .getManager(Constants.Package);
159:
160: /**
161: * The set of {@link User}s defined in this database, keyed by
162: * user name.
163: */
164: protected HashMap users = new HashMap();
165:
166: // ------------------------------------------------------------- Properties
167:
168: /**
169: * Return the set of {@link Group}s defined in this user database.
170: */
171: public Iterator getGroups() {
172:
173: synchronized (groups) {
174: return (groups.values().iterator());
175: }
176:
177: }
178:
179: /**
180: * Return the unique global identifier of this user database.
181: */
182: public String getId() {
183:
184: return (this .id);
185:
186: }
187:
188: /**
189: * Return the relative or absolute pathname to the persistent storage file.
190: */
191: public String getPathname() {
192:
193: return (this .pathname);
194:
195: }
196:
197: /**
198: * Set the relative or absolute pathname to the persistent storage file.
199: *
200: * @param pathname The new pathname
201: */
202: public void setPathname(String pathname) {
203:
204: this .pathname = pathname;
205: this .pathnameOld = pathname + ".old";
206: this .pathnameNew = pathname + ".new";
207:
208: }
209:
210: /**
211: * Return the set of {@link Role}s defined in this user database.
212: */
213: public Iterator getRoles() {
214:
215: synchronized (roles) {
216: return (roles.values().iterator());
217: }
218:
219: }
220:
221: /**
222: * Return the set of {@link User}s defined in this user database.
223: */
224: public Iterator getUsers() {
225:
226: synchronized (users) {
227: return (users.values().iterator());
228: }
229:
230: }
231:
232: // --------------------------------------------------------- Public Methods
233:
234: /**
235: * Finalize access to this user database.
236: *
237: * @exception Exception if any exception is thrown during closing
238: */
239: public void close() throws Exception {
240:
241: save();
242:
243: synchronized (groups) {
244: synchronized (users) {
245: users.clear();
246: groups.clear();
247: }
248: }
249:
250: }
251:
252: /**
253: * Create and return a new {@link Group} defined in this user database.
254: *
255: * @param groupname The group name of the new group (must be unique)
256: * @param description The description of this group
257: */
258: public Group createGroup(String groupname, String description) {
259:
260: MemoryGroup group = new MemoryGroup(this , groupname,
261: description);
262: synchronized (groups) {
263: groups.put(group.getGroupname(), group);
264: }
265: return (group);
266:
267: }
268:
269: /**
270: * Create and return a new {@link Role} defined in this user database.
271: *
272: * @param rolename The role name of the new group (must be unique)
273: * @param description The description of this group
274: */
275: public Role createRole(String rolename, String description) {
276:
277: MemoryRole role = new MemoryRole(this , rolename, description);
278: synchronized (roles) {
279: roles.put(role.getRolename(), role);
280: }
281: return (role);
282:
283: }
284:
285: /**
286: * Create and return a new {@link User} defined in this user database.
287: *
288: * @param username The logon username of the new user (must be unique)
289: * @param password The logon password of the new user
290: * @param fullName The full name of the new user
291: */
292: public User createUser(String username, String password,
293: String fullName) {
294:
295: MemoryUser user = new MemoryUser(this , username, password,
296: fullName);
297: synchronized (users) {
298: users.put(user.getUsername(), user);
299: }
300: return (user);
301:
302: }
303:
304: /**
305: * Return the {@link Group} with the specified group name, if any;
306: * otherwise return <code>null</code>.
307: *
308: * @param groupname Name of the group to return
309: */
310: public Group findGroup(String groupname) {
311:
312: synchronized (groups) {
313: return ((Group) groups.get(groupname));
314: }
315:
316: }
317:
318: /**
319: * Return the {@link Role} with the specified role name, if any;
320: * otherwise return <code>null</code>.
321: *
322: * @param rolename Name of the role to return
323: */
324: public Role findRole(String rolename) {
325:
326: synchronized (roles) {
327: return ((Role) roles.get(rolename));
328: }
329:
330: }
331:
332: /**
333: * Return the {@link User} with the specified user name, if any;
334: * otherwise return <code>null</code>.
335: *
336: * @param username Name of the user to return
337: */
338: public User findUser(String username) {
339:
340: synchronized (users) {
341: return ((User) users.get(username));
342: }
343:
344: }
345:
346: /**
347: * Initialize access to this user database.
348: *
349: * @exception Exception if any exception is thrown during opening
350: */
351: public void open() throws Exception {
352:
353: synchronized (groups) {
354: synchronized (users) {
355:
356: // Erase any previous groups and users
357: users.clear();
358: groups.clear();
359: roles.clear();
360:
361: // Construct a reader for the XML input file (if it exists)
362: File file = new File(pathname);
363: if (!file.isAbsolute()) {
364: file = new File(
365: System.getProperty("catalina.base"),
366: pathname);
367: }
368: if (!file.exists()) {
369: return;
370: }
371: FileInputStream fis = new FileInputStream(file);
372:
373: // Construct a digester to read the XML input file
374: Digester digester = new Digester();
375: digester.addFactoryCreate("tomcat-users/group",
376: new MemoryGroupCreationFactory(this ));
377: digester.addFactoryCreate("tomcat-users/role",
378: new MemoryRoleCreationFactory(this ));
379: digester.addFactoryCreate("tomcat-users/user",
380: new MemoryUserCreationFactory(this ));
381:
382: // Parse the XML input file to load this database
383: try {
384: digester.parse(fis);
385: fis.close();
386: } catch (Exception e) {
387: try {
388: fis.close();
389: } catch (Throwable t) {
390: ;
391: }
392: throw e;
393: }
394:
395: }
396: }
397:
398: }
399:
400: /**
401: * Remove the specified {@link Group} from this user database.
402: *
403: * @param group The group to be removed
404: */
405: public void removeGroup(Group group) {
406:
407: synchronized (groups) {
408: Iterator users = getUsers();
409: while (users.hasNext()) {
410: User user = (User) users.next();
411: user.removeGroup(group);
412: }
413: groups.remove(group.getGroupname());
414: }
415:
416: }
417:
418: /**
419: * Remove the specified {@link Role} from this user database.
420: *
421: * @param role The role to be removed
422: */
423: public void removeRole(Role role) {
424:
425: synchronized (roles) {
426: Iterator groups = getGroups();
427: while (groups.hasNext()) {
428: Group group = (Group) groups.next();
429: group.removeRole(role);
430: }
431: Iterator users = getUsers();
432: while (users.hasNext()) {
433: User user = (User) users.next();
434: user.removeRole(role);
435: }
436: roles.remove(role.getRolename());
437: }
438:
439: }
440:
441: /**
442: * Remove the specified {@link User} from this user database.
443: *
444: * @param user The user to be removed
445: */
446: public void removeUser(User user) {
447:
448: synchronized (users) {
449: users.remove(user.getUsername());
450: }
451:
452: }
453:
454: /**
455: * Save any updated information to the persistent storage location for
456: * this user database.
457: *
458: * @exception Exception if any exception is thrown during saving
459: */
460: public void save() throws Exception {
461:
462: // Write out contents to a temporary file
463: File fileNew = new File(pathnameNew);
464: if (!fileNew.isAbsolute()) {
465: fileNew = new File(System.getProperty("catalina.base"),
466: pathnameNew);
467: }
468: PrintWriter writer = null;
469: try {
470:
471: // Configure our PrintWriter
472: FileOutputStream fos = new FileOutputStream(fileNew);
473: OutputStreamWriter osw = new OutputStreamWriter(fos);
474: writer = new PrintWriter(osw);
475:
476: // Print the file prolog
477: writer.println("<?xml version='1.0'?>");
478: writer.println("<tomcat-users>");
479:
480: // Print entries for each defined role, group, and user
481: Iterator values = null;
482: values = getRoles();
483: while (values.hasNext()) {
484: writer.print(" ");
485: writer.println(values.next());
486: }
487: values = getGroups();
488: while (values.hasNext()) {
489: writer.print(" ");
490: writer.println(values.next());
491: }
492: values = getUsers();
493: while (values.hasNext()) {
494: writer.print(" ");
495: writer.println(values.next());
496: }
497:
498: // Print the file epilog
499: writer.println("</tomcat-users>");
500:
501: // Check for errors that occurred while printing
502: if (writer.checkError()) {
503: writer.close();
504: fileNew.delete();
505: throw new IOException(sm.getString(
506: "memoryUserDatabase.writeException", fileNew
507: .getAbsolutePath()));
508: }
509: writer.close();
510: } catch (IOException e) {
511: if (writer != null) {
512: writer.close();
513: }
514: fileNew.delete();
515: throw e;
516: }
517:
518: // Perform the required renames to permanently save this file
519: File fileOld = new File(pathnameNew);
520: if (!fileOld.isAbsolute()) {
521: fileOld = new File(System.getProperty("catalina.base"),
522: pathnameOld);
523: }
524: fileOld.delete();
525: File fileOrig = new File(pathname);
526: if (!fileOrig.isAbsolute()) {
527: fileOrig = new File(System.getProperty("catalina.base"),
528: pathname);
529: }
530: if (fileOrig.exists()) {
531: fileOld.delete();
532: if (!fileOrig.renameTo(fileOld)) {
533: throw new IOException(sm.getString(
534: "memoryUserDatabase.renameOld", fileOld
535: .getAbsolutePath()));
536: }
537: }
538: if (!fileNew.renameTo(fileOrig)) {
539: if (fileOld.exists()) {
540: fileOld.renameTo(fileOrig);
541: }
542: throw new IOException(sm.getString(
543: "memoryUserDatabase.renameNew", fileOrig
544: .getAbsolutePath()));
545: }
546: fileOld.delete();
547:
548: }
549:
550: /**
551: * Return a String representation of this UserDatabase.
552: */
553: public String toString() {
554:
555: StringBuffer sb = new StringBuffer("MemoryUserDatabase[id=");
556: sb.append(this .id);
557: sb.append(",pathname=");
558: sb.append(pathname);
559: sb.append(",groupCount=");
560: sb.append(this .groups.size());
561: sb.append(",roleCount=");
562: sb.append(this .roles.size());
563: sb.append(",userCount=");
564: sb.append(this .users.size());
565: sb.append("]");
566: return (sb.toString());
567:
568: }
569:
570: // -------------------------------------------------------- Package Methods
571:
572: /**
573: * Return the <code>StringManager</code> for use in looking up messages.
574: */
575: StringManager getStringManager() {
576:
577: return (sm);
578:
579: }
580:
581: }
582:
583: /**
584: * Digester object creation factory for group instances.
585: */
586: class MemoryGroupCreationFactory implements ObjectCreationFactory {
587:
588: public MemoryGroupCreationFactory(MemoryUserDatabase database) {
589: this .database = database;
590: }
591:
592: public Object createObject(Attributes attributes) {
593: String groupname = attributes.getValue("groupname");
594: if (groupname == null) {
595: groupname = attributes.getValue("name");
596: }
597: String description = attributes.getValue("description");
598: String roles = attributes.getValue("roles");
599: Group group = database.createGroup(groupname, description);
600: if (roles != null) {
601: while (roles.length() > 0) {
602: String rolename = null;
603: int comma = roles.indexOf(',');
604: if (comma >= 0) {
605: rolename = roles.substring(0, comma).trim();
606: roles = roles.substring(comma + 1);
607: } else {
608: rolename = roles.trim();
609: roles = "";
610: }
611: if (rolename.length() > 0) {
612: Role role = database.findRole(rolename);
613: if (role == null) {
614: role = database.createRole(rolename, null);
615: }
616: group.addRole(role);
617: }
618: }
619: }
620: return (group);
621: }
622:
623: private MemoryUserDatabase database = null;
624:
625: private Digester digester = null;
626:
627: public Digester getDigester() {
628: return (this .digester);
629: }
630:
631: public void setDigester(Digester digester) {
632: this .digester = digester;
633: }
634:
635: }
636:
637: /**
638: * Digester object creation factory for role instances.
639: */
640: class MemoryRoleCreationFactory implements ObjectCreationFactory {
641:
642: public MemoryRoleCreationFactory(MemoryUserDatabase database) {
643: this .database = database;
644: }
645:
646: public Object createObject(Attributes attributes) {
647: String rolename = attributes.getValue("rolename");
648: if (rolename == null) {
649: rolename = attributes.getValue("name");
650: }
651: String description = attributes.getValue("description");
652: Role role = database.createRole(rolename, description);
653: return (role);
654: }
655:
656: private MemoryUserDatabase database = null;
657:
658: private Digester digester = null;
659:
660: public Digester getDigester() {
661: return (this .digester);
662: }
663:
664: public void setDigester(Digester digester) {
665: this .digester = digester;
666: }
667:
668: }
669:
670: /**
671: * Digester object creation factory for user instances.
672: */
673: class MemoryUserCreationFactory implements ObjectCreationFactory {
674:
675: public MemoryUserCreationFactory(MemoryUserDatabase database) {
676: this .database = database;
677: }
678:
679: public Object createObject(Attributes attributes) {
680: String username = attributes.getValue("username");
681: if (username == null) {
682: username = attributes.getValue("name");
683: }
684: String password = attributes.getValue("password");
685: String fullName = attributes.getValue("fullName");
686: if (fullName == null) {
687: fullName = attributes.getValue("fullname");
688: }
689: String groups = attributes.getValue("groups");
690: String roles = attributes.getValue("roles");
691: User user = database.createUser(username, password, fullName);
692: if (groups != null) {
693: while (groups.length() > 0) {
694: String groupname = null;
695: int comma = groups.indexOf(',');
696: if (comma >= 0) {
697: groupname = groups.substring(0, comma).trim();
698: groups = groups.substring(comma + 1);
699: } else {
700: groupname = groups.trim();
701: groups = "";
702: }
703: if (groupname.length() > 0) {
704: Group group = database.findGroup(groupname);
705: if (group == null) {
706: group = database.createGroup(groupname, null);
707: }
708: user.addGroup(group);
709: }
710: }
711: }
712: if (roles != null) {
713: while (roles.length() > 0) {
714: String rolename = null;
715: int comma = roles.indexOf(',');
716: if (comma >= 0) {
717: rolename = roles.substring(0, comma).trim();
718: roles = roles.substring(comma + 1);
719: } else {
720: rolename = roles.trim();
721: roles = "";
722: }
723: if (rolename.length() > 0) {
724: Role role = database.findRole(rolename);
725: if (role == null) {
726: role = database.createRole(rolename, null);
727: }
728: user.addRole(role);
729: }
730: }
731: }
732: return (user);
733: }
734:
735: private MemoryUserDatabase database = null;
736:
737: private Digester digester = null;
738:
739: public Digester getDigester() {
740: return (this .digester);
741: }
742:
743: public void setDigester(Digester digester) {
744: this.digester = digester;
745: }
746:
747: }
|