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