001: /*
002: * @(#)Permissions.java 1.54 06/10/11
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.util.Enumeration;
031: import java.util.Hashtable;
032: import java.util.NoSuchElementException;
033: import java.util.Map;
034: import java.util.HashMap;
035: import java.util.List;
036: import java.util.ArrayList;
037: import java.util.Iterator;
038: import java.util.Collections;
039: import java.io.Serializable;
040: import java.io.ObjectStreamField;
041: import java.io.ObjectOutputStream;
042: import java.io.ObjectInputStream;
043: import java.io.IOException;
044:
045: /**
046: * This class represents a heterogeneous collection of Permissions. That is,
047: * it contains different types of Permission objects, organized into
048: * PermissionCollections. For example, if any
049: * <code>java.io.FilePermission</code> objects are added to an instance of
050: * this class, they are all stored in a single
051: * PermissionCollection. It is the PermissionCollection returned by a call to
052: * the <code>newPermissionCollection</code> method in the FilePermission class.
053: * Similarly, any <code>java.lang.RuntimePermission</code> objects are
054: * stored in the PermissionCollection returned by a call to the
055: * <code>newPermissionCollection</code> method in the
056: * RuntimePermission class. Thus, this class represents a collection of
057: * PermissionCollections.
058: *
059: * <p>When the <code>add</code> method is called to add a Permission, the
060: * Permission is stored in the appropriate PermissionCollection. If no such
061: * collection exists yet, the Permission object's class is determined and the
062: * <code>newPermissionCollection</code> method is called on that class to create
063: * the PermissionCollection and add it to the Permissions object. If
064: * <code>newPermissionCollection</code> returns null, then a default
065: * PermissionCollection that uses a hashtable will be created and used. Each
066: * hashtable entry stores a Permission object as both the key and the value.
067: *
068: * <p> Enumerations returned via the <code>elements</code> method are
069: * not <em>fail-fast</em>. Modifications to a collection should not be
070: * performed while enumerating over that collection.
071: *
072: * @see Permission
073: * @see PermissionCollection
074: * @see AllPermission
075: *
076: * @version 1.46, 00/02/02
077: *
078: * @author Marianne Mueller
079: * @author Roland Schemers
080: *
081: * @serial exclude
082: */
083:
084: public final class Permissions extends PermissionCollection implements
085: Serializable {
086: /**
087: * Key is permissions Class, value is PermissionCollection for that class.
088: * Not serialized; see serialization section at end of class.
089: */
090: private transient Map permsMap;
091:
092: // optimization. keep track of whether unresolved permissions need to be checked
093: private transient boolean hasUnresolved = false;
094:
095: // optimization. keep track of the AllPermission collection
096: private PermissionCollection allPermission;
097:
098: /**
099: * Creates a new Permissions object containing no PermissionCollections.
100: */
101: public Permissions() {
102: permsMap = new HashMap(11);
103: allPermission = null;
104: }
105:
106: /**
107: * Adds a permission object to the PermissionCollection for the class the
108: * permission belongs to. For example, if <i>permission</i> is a
109: * FilePermission, it is added to the FilePermissionCollection stored
110: * in this Permissions object.
111: *
112: * This method creates
113: * a new PermissionCollection object (and adds the permission to it)
114: * if an appropriate collection does not yet exist. <p>
115: *
116: * @param permission the Permission object to add.
117: *
118: * @exception SecurityException if this Permissions object is
119: * marked as readonly.
120: *
121: * @see PermissionCollection#isReadOnly()
122: */
123:
124: public void add(Permission permission) {
125: if (isReadOnly())
126: throw new SecurityException(
127: "attempt to add a Permission to a readonly Permissions object");
128: PermissionCollection pc = getPermissionCollection(permission,
129: true);
130: pc.add(permission);
131:
132: // No need to synchronize because all adds are done sequentially
133: // and allPermission and hasUnresolved are set only by add()
134: if (permission instanceof AllPermission) {
135: allPermission = pc;
136: }
137: if (permission instanceof UnresolvedPermission) {
138: hasUnresolved = true;
139: }
140: }
141:
142: /**
143: * Checks to see if this object's PermissionCollection for permissions of
144: * the specified permission's type implies the permissions
145: * expressed in the <i>permission</i> object. Returns true if the
146: * combination of permissions in the appropriate PermissionCollection
147: * (e.g., a FilePermissionCollection for a FilePermission) together
148: * imply the specified permission.
149: *
150: * <p>For example, suppose there is a FilePermissionCollection in this
151: * Permissions object, and it contains one FilePermission that specifies
152: * "read" access for all files in all subdirectories of the "/tmp"
153: * directory, and another FilePermission that specifies "write" access
154: * for all files in the "/tmp/scratch/foo" directory.
155: * Then if the <code>implies</code> method
156: * is called with a permission specifying both "read" and "write" access
157: * to files in the "/tmp/scratch/foo" directory, <code>true</code> is
158: * returned.
159: *
160: * <p>Additionally, if this PermissionCollection contains the
161: * AllPermission, this method will always return true.
162: * <p>
163: * @param permission the Permission object to check.
164: *
165: * @return true if "permission" is implied by the permissions in the
166: * PermissionCollection it
167: * belongs to, false if not.
168: */
169:
170: public boolean implies(Permission permission) {
171: if (allPermission != null) {
172: return true; // AllPermission has already been added
173: } else {
174: PermissionCollection pc = getPermissionCollection(
175: permission, false);
176: if (pc != null) {
177: return pc.implies(permission);
178: } else {
179: // none found
180: return false;
181: }
182: }
183: }
184:
185: /**
186: * Returns an enumeration of all the Permission objects in all the
187: * PermissionCollections in this Permissions object.
188: *
189: * @return an enumeration of all the Permissions.
190: */
191:
192: public Enumeration elements() {
193: // go through each Permissions in the hash table
194: // and call their elements() function.
195:
196: if (hasUnresolved) {
197: // Take snapshot of what's in permsMap because permsMap might
198: // change during iteration by implies() of other threads;
199: // individual PermissionCollections won't change during implies().
200:
201: List copy = new ArrayList();
202: synchronized (permsMap) {
203: copy.addAll(permsMap.values());
204: }
205: return new PermissionsEnumerator(copy.iterator());
206: } else {
207: // permsMap won't be updated during iteration; sync unnecessary
208: return new PermissionsEnumerator(permsMap.values()
209: .iterator());
210: }
211: }
212:
213: /**
214: * Gets the PermissionCollection in this Permissions object for
215: * permissions whose type is the same as that of <i>p</i>.
216: * For example, if <i>p</i> is a FilePermission,
217: * the FilePermissionCollection
218: * stored in this Permissions object will be returned.
219: *
220: * If createEmtpy is true,
221: * this method creates a new PermissionCollection object for the specified
222: * type of permission objects if one does not yet exist.
223: * To do so, it first calls the <code>newPermissionCollection</code> method
224: * on <i>p</i>. Subclasses of class Permission
225: * override that method if they need to store their permissions in a
226: * particular PermissionCollection object in order to provide the
227: * correct semantics when the <code>PermissionCollection.implies</code>
228: * method is called.
229: * If the call returns a PermissionCollection, that collection is stored
230: * in this Permissions object. If the call returns null and createEmpty
231: * is true, then
232: * this method instantiates and stores a default PermissionCollection
233: * that uses a hashtable to store its permission objects.
234: *
235: * createEmtpy is ignored when creating empty PermissionCollection
236: * for unresolved permissions because of the overhead of determining the
237: * PermissionCollection to use.
238: *
239: * createEmpty should be set to false when this method is invoked from
240: * implies() because it incurs the additional overhead of creating and
241: * adding an empty PermissionCollection that will just return false.
242: * It should be set to true when invoked from add().
243: */
244: private PermissionCollection getPermissionCollection(Permission p,
245: boolean createEmpty) {
246: Class c = p.getClass();
247:
248: if (!hasUnresolved && !createEmpty) {
249: // No need to synchronize because updates have all been done
250: return (PermissionCollection) permsMap.get(c);
251: } else {
252: PermissionCollection pc = null;
253:
254: // synchronize because permsMap might be updated with a new
255: // PermissionCollection
256: synchronized (permsMap) {
257: pc = (PermissionCollection) permsMap.get(c);
258:
259: // Check for unresolved permissions
260: if (pc == null) {
261: pc = (hasUnresolved ? getUnresolvedPermissions(p)
262: : null);
263:
264: // if still null, create a new collection
265: if (pc == null && createEmpty) {
266:
267: pc = p.newPermissionCollection();
268:
269: // still no PermissionCollection?
270: // We'll give them a PermissionsHash.
271: if (pc == null)
272: pc = new PermissionsHash();
273: }
274:
275: if (pc != null) {
276: permsMap.put(c, pc);
277: }
278: }
279: }
280: return pc;
281: }
282: }
283:
284: /**
285: * Resolves any unresolved permissions of type p.
286: *
287: * @param p the type of unresolved permission to resolve
288: *
289: * @return PermissionCollection containing the unresolved permissions,
290: * or null if there were no unresolved permissions of type p.
291: *
292: */
293: private PermissionCollection getUnresolvedPermissions(Permission p) {
294: // Called from within synchronized method so permsMap doesn't need lock
295:
296: UnresolvedPermissionCollection uc = (UnresolvedPermissionCollection) permsMap
297: .get(UnresolvedPermission.class);
298:
299: // we have no unresolved permissions if uc is null
300: if (uc == null)
301: return null;
302:
303: List unresolvedPerms = uc.getUnresolvedPermissions(p);
304:
305: // we have no unresolved permissions of this type if unresolvedPerms is null
306: if (unresolvedPerms == null)
307: return null;
308:
309: java.security.cert.Certificate certs[] = null;
310:
311: Object signers[] = p.getClass().getSigners();
312:
313: int n = 0;
314: if (signers != null) {
315: for (int j = 0; j < signers.length; j++) {
316: if (signers[j] instanceof java.security.cert.Certificate) {
317: n++;
318: }
319: }
320: certs = new java.security.cert.Certificate[n];
321: n = 0;
322: for (int j = 0; j < signers.length; j++) {
323: if (signers[j] instanceof java.security.cert.Certificate) {
324: certs[n++] = (java.security.cert.Certificate) signers[j];
325: }
326: }
327: }
328:
329: PermissionCollection pc = null;
330: int len = unresolvedPerms.size();
331: for (int i = 0; i < len; i++) {
332: UnresolvedPermission up = (UnresolvedPermission) unresolvedPerms
333: .get(i);
334: Permission perm = up.resolve(p, certs);
335: if (perm != null) {
336: if (pc == null) {
337: pc = p.newPermissionCollection();
338: if (pc == null)
339: pc = new PermissionsHash();
340: }
341: pc.add(perm);
342: }
343:
344: }
345: return pc;
346: }
347:
348: private static final long serialVersionUID = 4858622370623524688L;
349:
350: // Need to maintain serialization interoperability with earlier releases,
351: // which had the serializable field:
352: // private Hashtable perms;
353:
354: /**
355: * @serialField perms java.util.Hashtable
356: * A table of the Permission classes and PermissionCollections.
357: * @serialField allPermission java.security.PermissionCollection
358: */
359: private static final ObjectStreamField[] serialPersistentFields = {
360: new ObjectStreamField("perms", Hashtable.class),
361: new ObjectStreamField("allPermission",
362: PermissionCollection.class), };
363:
364: /**
365: * @serialData Default fields.
366: */
367: /*
368: * Writes the contents of the permsMap field out as a Hashtable for
369: * serialization compatibility with earlier releases. allPermission
370: * unchanged.
371: */
372: private void writeObject(ObjectOutputStream out) throws IOException {
373: // Don't call out.defaultWriteObject()
374:
375: // Copy perms into a Hashtable
376: Hashtable perms = new Hashtable(permsMap.size() * 2);
377: perms.putAll(permsMap);
378:
379: // Write out serializable fields
380: ObjectOutputStream.PutField pfields = out.putFields();
381: pfields.put("allPermission", allPermission);
382: pfields.put("perms", perms);
383: out.writeFields();
384: }
385:
386: /*
387: * Reads in a Hashtable of Class/PermissionCollections and saves them in the
388: * permsMap field. Reads in allPermission.
389: */
390: private void readObject(ObjectInputStream in) throws IOException,
391: ClassNotFoundException {
392: // Don't call defaultReadObject()
393:
394: // Read in serialized fields
395: ObjectInputStream.GetField gfields = in.readFields();
396:
397: // Get allPermission
398: allPermission = (PermissionCollection) gfields.get(
399: "allPermission", null);
400:
401: // Get permissions
402: Hashtable perms = (Hashtable) gfields.get("perms", null);
403: permsMap = new HashMap(perms.size() * 2);
404: permsMap.putAll(perms);
405:
406: // Set hasUnresolved
407: UnresolvedPermissionCollection uc = (UnresolvedPermissionCollection) permsMap
408: .get(UnresolvedPermission.class);
409: hasUnresolved = (uc != null && uc.elements().hasMoreElements());
410: }
411: }
412:
413: final class PermissionsEnumerator implements Enumeration {
414:
415: // all the perms
416: private Iterator perms;
417: // the current set
418: private Enumeration permset;
419:
420: PermissionsEnumerator(Iterator e) {
421: perms = e;
422: permset = getNextEnumWithMore();
423: }
424:
425: // No need to synchronize; caller should sync on object as required
426: public boolean hasMoreElements() {
427: // if we enter with permissionimpl null, we know
428: // there are no more left.
429:
430: if (permset == null)
431: return false;
432:
433: // try to see if there are any left in the current one
434:
435: if (permset.hasMoreElements())
436: return true;
437:
438: // get the next one that has something in it...
439: permset = getNextEnumWithMore();
440:
441: // if it is null, we are done!
442: return (permset != null);
443: }
444:
445: // No need to synchronize; caller should sync on object as required
446: public Object nextElement() {
447:
448: // hasMoreElements will update permset to the next permset
449: // with something in it...
450:
451: if (hasMoreElements()) {
452: return permset.nextElement();
453: } else {
454: throw new NoSuchElementException("PermissionsEnumerator");
455: }
456:
457: }
458:
459: private Enumeration getNextEnumWithMore() {
460: while (perms.hasNext()) {
461: PermissionCollection pc = (PermissionCollection) perms
462: .next();
463: Enumeration next = (Enumeration) pc.elements();
464: if (next.hasMoreElements())
465: return next;
466: }
467: return null;
468:
469: }
470: }
471:
472: /**
473: * A PermissionsHash stores a homogeneous set of permissions in a hashtable.
474: *
475: * @see Permission
476: * @see Permissions
477: *
478: * @version 1.46, 02/02/00
479: *
480: * @author Roland Schemers
481: *
482: * @serial include
483: */
484:
485: final class PermissionsHash extends PermissionCollection implements
486: Serializable {
487: /**
488: * Key and value are (same) permissions objects.
489: * Not serialized; see serialization section at end of class.
490: */
491: private transient Map permsMap;
492:
493: /**
494: * Create an empty PermissionsHash object.
495: */
496:
497: PermissionsHash() {
498: permsMap = new HashMap(11);
499: }
500:
501: /**
502: * Adds a permission to the PermissionsHash.
503: *
504: * @param permission the Permission object to add.
505: */
506:
507: public void add(Permission permission) {
508: permsMap.put(permission, permission);
509: }
510:
511: /**
512: * Check and see if this set of permissions implies the permissions
513: * expressed in "permission".
514: *
515: * @param permission the Permission object to compare
516: *
517: * @return true if "permission" is a proper subset of a permission in
518: * the set, false if not.
519: */
520:
521: public boolean implies(Permission permission) {
522: // attempt a fast lookup and implies. If that fails
523: // then enumerate through all the permissions.
524: Permission p = (Permission) permsMap.get(permission);
525:
526: // If permission is found, then p.equals(permission)
527: if (p == null) {
528: Iterator enum_ = permsMap.values().iterator();
529: while (enum_.hasNext()) {
530: p = (Permission) enum_.next();
531: if (p.implies(permission))
532: return true;
533: }
534: return false;
535: } else {
536: return true;
537: }
538: }
539:
540: /**
541: * Returns an enumeration of all the Permission objects in the container.
542: *
543: * @return an enumeration of all the Permissions.
544: */
545:
546: public Enumeration elements() {
547: // Convert Iterator of Map values into an Enumeration
548: return Collections.enumeration(permsMap.values());
549: }
550:
551: private static final long serialVersionUID = -8491988220802933440L;
552: // Need to maintain serialization interoperability with earlier releases,
553: // which had the serializable field:
554: // private Hashtable perms;
555: /**
556: * @serialField perms java.util.Hashtable
557: * A table of the Permissions (both key and value are same).
558: */
559: private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField(
560: "perms", Hashtable.class), };
561:
562: /**
563: * @serialData Default fields.
564: */
565: /*
566: * Writes the contents of the permsMap field out as a Hashtable for
567: * serialization compatibility with earlier releases.
568: */
569: private void writeObject(ObjectOutputStream out) throws IOException {
570: // Don't call out.defaultWriteObject()
571:
572: // Copy perms into a Hashtable
573: Hashtable perms = new Hashtable(permsMap.size() * 2);
574: perms.putAll(permsMap);
575:
576: // Write out serializable fields
577: ObjectOutputStream.PutField pfields = out.putFields();
578: pfields.put("perms", perms);
579: out.writeFields();
580: }
581:
582: /*
583: * Reads in a Hashtable of Permission/Permission and saves them in the
584: * permsMap field.
585: */
586: private void readObject(ObjectInputStream in) throws IOException,
587: ClassNotFoundException {
588: // Don't call defaultReadObject()
589:
590: // Read in serialized fields
591: ObjectInputStream.GetField gfields = in.readFields();
592:
593: // Get permissions
594: Hashtable perms = (Hashtable) gfields.get("perms", null);
595: permsMap = new HashMap(perms.size() * 2);
596: permsMap.putAll(perms);
597: }
598: }
|