001: /*
002: * @(#)BasicPermission.java 1.34 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package java.security;
029:
030: import java.security.*;
031: import java.util.Enumeration;
032: import java.util.Iterator;
033: import java.util.Map;
034: import java.util.HashMap;
035: import java.util.Hashtable;
036: import java.util.Collections;
037: import java.util.StringTokenizer;
038: import java.io.ObjectStreamField;
039: import java.io.ObjectOutputStream;
040: import java.io.ObjectInputStream;
041: import java.io.IOException;
042:
043: /**
044: * The BasicPermission class extends the Permission class, and
045: * can be used as the base class for permissions that want to
046: * follow the same naming convention as BasicPermission.
047: * <P>
048: * The name for a BasicPermission is the name of the given permission
049: * (for example, "exit",
050: * "setFactory", "print.queueJob", etc). The naming
051: * convention follows the hierarchical property naming convention.
052: * An asterisk may appear by itself, or if immediately preceded by a "."
053: * may appear at the end of the name, to signify a wildcard match.
054: * For example, "*" and "java.*" are valid, while "*java", "a*b",
055: * and "java*" are not valid.
056: * <P>
057: * The action string (inherited from Permission) is unused.
058: * Thus, BasicPermission is commonly used as the base class for
059: * "named" permissions
060: * (ones that contain a name but no actions list; you either have the
061: * named permission or you don't.)
062: * Subclasses may implement actions on top of BasicPermission,
063: * if desired.
064: * <p>
065: * <P>
066: * @see java.security.Permission
067: * @see java.security.Permissions
068: * @see java.security.PermissionCollection
069: * @see java.lang.RuntimePermission
070: * @see java.security.SecurityPermission
071: * @see java.util.PropertyPermission
072: * @see java.net.NetPermission
073: * @see java.lang.SecurityManager
074: *
075: * @version 1.26 00/02/02
076: *
077: * @author Marianne Mueller
078: * @author Roland Schemers
079: */
080:
081: public abstract class BasicPermission extends Permission implements
082: java.io.Serializable {
083:
084: // does this permission have a wildcard at the end?
085: private transient boolean wildcard;
086:
087: // the name without the wildcard on the end
088: private transient String path;
089:
090: /**
091: * initialize a BasicPermission object. Common to all constructors.
092: *
093: */
094:
095: private void init(String name) {
096: if (name == null)
097: throw new NullPointerException("name can't be null");
098:
099: int len = name.length();
100:
101: if (len == 0) {
102: throw new IllegalArgumentException("name can't be empty");
103: }
104:
105: char last = name.charAt(len - 1);
106:
107: // Is wildcard or ends with ".*"?
108: if (last == '*' && (len == 1 || name.charAt(len - 2) == '.')) {
109: wildcard = true;
110: if (len == 1) {
111: path = "";
112: } else {
113: path = name.substring(0, len - 1);
114: }
115: } else {
116: path = name;
117: }
118: }
119:
120: /**
121: * Creates a new BasicPermission with the specified name.
122: * Name is the symbolic name of the permission, such as
123: * "setFactory",
124: * "print.queueJob", or "topLevelWindow", etc.
125: *
126: * @param name the name of the BasicPermission.
127: *
128: * @throws NullPointerException if <code>name</code> is <code>null</code>.
129: * @throws IllegalArgumentException if <code>name</code> is empty.
130: */
131:
132: public BasicPermission(String name) {
133: super (name);
134: init(name);
135: }
136:
137: /**
138: * Creates a new BasicPermission object with the specified name.
139: * The name is the symbolic name of the BasicPermission, and the
140: * actions String is currently unused.
141: *
142: * @param name the name of the BasicPermission.
143: * @param actions ignored.
144: *
145: * @throws NullPointerException if <code>name</code> is <code>null</code>.
146: * @throws IllegalArgumentException if <code>name</code> is empty.
147: */
148: public BasicPermission(String name, String actions) {
149: super (name);
150: init(name);
151: }
152:
153: /**
154: * Checks if the specified permission is "implied" by
155: * this object.
156: * <P>
157: * More specifically, this method returns true if:<p>
158: * <ul>
159: * <li> <i>p</i>'s class is the same as this object's class, and<p>
160: * <li> <i>p</i>'s name equals or (in the case of wildcards)
161: * is implied by this object's
162: * name. For example, "a.b.*" implies "a.b.c".
163: * </ul>
164: *
165: * @param p the permission to check against.
166: *
167: * @return true if the passed permission is equal to or
168: * implied by this permission, false otherwise.
169: */
170: public boolean implies(Permission p) {
171: if ((p == null) || (p.getClass() != getClass()))
172: return false;
173:
174: BasicPermission that = (BasicPermission) p;
175:
176: if (this .wildcard) {
177: if (that.wildcard)
178: // one wildcard can imply another
179: return that.path.startsWith(path);
180: else
181: // make sure ap.path is longer so a.b.* doesn't imply a.b
182: return (that.path.length() > this .path.length())
183: && that.path.startsWith(this .path);
184: } else {
185: if (that.wildcard) {
186: // a non-wildcard can't imply a wildcard
187: return false;
188: } else {
189: return this .path.equals(that.path);
190: }
191: }
192: }
193:
194: /**
195: * Checks two BasicPermission objects for equality.
196: * Checks that <i>obj</i>'s class is the same as this object's class
197: * and has the same name as this object.
198: * <P>
199: * @param obj the object we are testing for equality with this object.
200: * @return true if <i>obj</i> is a BasicPermission, and has the same name
201: * as this BasicPermission object, false otherwise.
202: */
203: public boolean equals(Object obj) {
204: if (obj == this )
205: return true;
206:
207: if ((obj == null) || (obj.getClass() != getClass()))
208: return false;
209:
210: BasicPermission bp = (BasicPermission) obj;
211:
212: return getName().equals(bp.getName());
213: }
214:
215: /**
216: * Returns the hash code value for this object.
217: * The hash code used is the hash code of the name, that is,
218: * <code>getName().hashCode()</code>, where <code>getName</code> is
219: * from the Permission superclass.
220: *
221: * @return a hash code value for this object.
222: */
223:
224: public int hashCode() {
225: return this .getName().hashCode();
226: }
227:
228: /**
229: * Returns the canonical string representation of the actions,
230: * which currently is the empty string "", since there are no actions for
231: * a BasicPermission.
232: *
233: * @return the empty string "".
234: */
235: public String getActions() {
236: return "";
237: }
238:
239: /**
240: * Returns a new PermissionCollection object for storing BasicPermission
241: * objects.
242: * <p>
243: * A BasicPermissionCollection stores a collection of
244: * BasicPermission permissions.
245: *
246: * <p>BasicPermission objects must be stored in a manner that allows them
247: * to be inserted in any order, but that also enables the
248: * PermissionCollection <code>implies</code> method
249: * to be implemented in an efficient (and consistent) manner.
250: *
251: * @return a new PermissionCollection object suitable for
252: * storing BasicPermissions.
253: */
254:
255: public PermissionCollection newPermissionCollection() {
256: return new BasicPermissionCollection();
257: }
258:
259: /**
260: * readObject is called to restore the state of the BasicPermission from
261: * a stream.
262: */
263: private void readObject(ObjectInputStream s) throws IOException,
264: ClassNotFoundException {
265: s.defaultReadObject();
266: // init is called to initialize the rest of the values.
267: init(getName());
268: }
269: }
270:
271: /**
272: * A BasicPermissionCollection stores a collection
273: * of BasicPermission permissions. BasicPermission objects
274: * must be stored in a manner that allows them to be inserted in any
275: * order, but enable the implies function to evaluate the implies
276: * method in an efficient (and consistent) manner.
277: *
278: * A BasicPermissionCollection handles comparing a permission like "a.b.c.d.e"
279: * with a Permission such as "a.b.*", or "*".
280: *
281: * @see java.security.Permission
282: * @see java.security.Permissions
283: * @see java.security.PermissionsImpl
284: *
285: * @version 1.26 02/02/00
286: *
287: * @author Roland Schemers
288: *
289: * @serial include
290: */
291:
292: final class BasicPermissionCollection extends PermissionCollection
293: implements java.io.Serializable {
294:
295: private static final long serialVersionUID = 739301742472979399L;
296:
297: /**
298: * Key is name, value is permission. All permission objects in
299: * collection must be of the same type.
300: * Not serialized; see serialization section at end of class.
301: */
302: private transient Map perms;
303:
304: /**
305: * This is set to <code>true</code> if this BasicPermissionCollection
306: * contains a BasicPermission with '*' as its permission name.
307: *
308: * @see #serialPersistentFields
309: */
310: private boolean all_allowed;
311:
312: /**
313: * The class to which all BasicPermissions in this
314: * BasicPermissionCollection belongs.
315: *
316: * @see #serialPersistentFields
317: */
318: private Class permClass;
319:
320: /**
321: * Create an empty BasicPermissionCollection object.
322: *
323: */
324:
325: public BasicPermissionCollection() {
326: perms = new HashMap(11);
327: all_allowed = false;
328: }
329:
330: /**
331: * Adds a permission to the BasicPermissions. The key for the hash is
332: * permission.path.
333: *
334: * @param permission the Permission object to add.
335: *
336: * @exception IllegalArgumentException - if the permission is not a
337: * BasicPermission, or if
338: * the permission is not of the
339: * same Class as the other
340: * permissions in this collection.
341: *
342: * @exception SecurityException - if this BasicPermissionCollection object
343: * has been marked readonly
344: */
345:
346: public void add(Permission permission) {
347: if (!(permission instanceof BasicPermission))
348: throw new IllegalArgumentException("invalid permission: "
349: + permission);
350: if (isReadOnly())
351: throw new SecurityException(
352: "attempt to add a Permission to a readonly PermissionCollection");
353:
354: BasicPermission bp = (BasicPermission) permission;
355:
356: if (perms.size() == 0) {
357: // adding first permission
358: permClass = bp.getClass();
359: } else {
360: // make sure we only add new BasicPermissions of the same class
361: if (bp.getClass() != permClass)
362: throw new IllegalArgumentException(
363: "invalid permission: " + permission);
364: }
365:
366: // No need to synchronize because all adds are done sequentially
367: // before any implies() calls
368:
369: perms.put(bp.getName(), permission);
370: if (!all_allowed) {
371: if (bp.getName().equals("*"))
372: all_allowed = true;
373: }
374: }
375:
376: /**
377: * Check and see if this set of permissions implies the permissions
378: * expressed in "permission".
379: *
380: * @param p the Permission object to compare
381: *
382: * @return true if "permission" is a proper subset of a permission in
383: * the set, false if not.
384: */
385:
386: public boolean implies(Permission permission) {
387: if (!(permission instanceof BasicPermission))
388: return false;
389:
390: BasicPermission bp = (BasicPermission) permission;
391:
392: // random subclasses of BasicPermission do not imply each other
393: if (bp.getClass() != permClass)
394: return false;
395:
396: // short circuit if the "*" Permission was added
397: if (all_allowed)
398: return true;
399:
400: // strategy:
401: // Check for full match first. Then work our way up the
402: // path looking for matches on a.b..*
403:
404: String path = bp.getName();
405: //System.out.println("check "+path);
406:
407: Permission x = (Permission) perms.get(path);
408:
409: if (x != null) {
410: // we have a direct hit!
411: return x.implies(permission);
412: }
413:
414: // work our way up the tree...
415: int last, offset;
416:
417: offset = path.length() - 1;
418:
419: while ((last = path.lastIndexOf(".", offset)) != -1) {
420:
421: path = path.substring(0, last + 1) + "*";
422: //System.out.println("check "+path);
423: x = (Permission) perms.get(path);
424:
425: if (x != null) {
426: return x.implies(permission);
427: }
428: offset = last - 1;
429: }
430:
431: // we don't have to check for "*" as it was already checked
432: // at the top (all_allowed), so we just return false
433: return false;
434: }
435:
436: /**
437: * Returns an enumeration of all the BasicPermission objects in the
438: * container.
439: *
440: * @return an enumeration of all the BasicPermission objects.
441: */
442:
443: public Enumeration elements() {
444: // Convert Iterator of Map values into an Enumeration
445: return Collections.enumeration(perms.values());
446: }
447:
448: // Need to maintain serialization interoperability with earlier releases,
449: // which had the serializable field:
450: //
451: // @serial the Hashtable is indexed by the BasicPermission name
452: //
453: // private Hashtable permissions;
454: /**
455: * @serialField permissions java.util.Hashtable
456: * The BasicPermissions in this BasicPermissionCollection.
457: * All BasicPermissions in the collection must belong to the same class.
458: * The Hashtable is indexed by the BasicPermission name; the value
459: * of the Hashtable entry is the permission.
460: * @serialField all_allowed boolean
461: * This is set to <code>true</code> if this BasicPermissionCollection
462: * contains a BasicPermission with '*' as its permission name.
463: * @serialField permClass java.lang.Class
464: * The class to which all BasicPermissions in this
465: * BasicPermissionCollection belongs.
466: */
467: private static final ObjectStreamField[] serialPersistentFields = {
468: new ObjectStreamField("permissions", Hashtable.class),
469: new ObjectStreamField("all_allowed", Boolean.TYPE),
470: new ObjectStreamField("permClass", Class.class), };
471:
472: /**
473: * @serialData Default fields.
474: */
475: /*
476: * Writes the contents of the perms field out as a Hashtable for
477: * serialization compatibility with earlier releases. all_allowed
478: * and permClass unchanged.
479: */
480: private void writeObject(ObjectOutputStream out) throws IOException {
481: // Don't call out.defaultWriteObject()
482:
483: // Copy perms into a Hashtable
484: Hashtable permissions = new Hashtable(perms.size() * 2);
485: permissions.putAll(perms);
486:
487: // Write out serializable fields
488: ObjectOutputStream.PutField pfields = out.putFields();
489: pfields.put("all_allowed", all_allowed);
490: pfields.put("permissions", permissions);
491: pfields.put("permClass", permClass);
492: out.writeFields();
493: }
494:
495: /**
496: * readObject is called to restore the state of the
497: * BasicPermissionCollection from a stream.
498: */
499: private void readObject(java.io.ObjectInputStream in)
500: throws IOException, ClassNotFoundException {
501: // Don't call defaultReadObject()
502:
503: // Read in serialized fields
504: ObjectInputStream.GetField gfields = in.readFields();
505:
506: // Get permissions
507: Hashtable permissions = (Hashtable) gfields.get("permissions",
508: null);
509: perms = new HashMap(permissions.size() * 2);
510: perms.putAll(permissions);
511:
512: // Get all_allowed
513: all_allowed = gfields.get("all_allowed", false);
514:
515: // Get permClass
516: permClass = (Class) gfields.get("permClass", null);
517:
518: if (permClass == null) {
519: // set permClass
520: Enumeration e = permissions.elements();
521: if (e.hasMoreElements()) {
522: Permission p = (Permission) e.nextElement();
523: permClass = p.getClass();
524: }
525: }
526: }
527: }
|