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