/*
* @(#)Permissions.java 1.58 04/05/05
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package java.security;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.NoSuchElementException;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Collections;
import java.io.Serializable;
import java.io.ObjectStreamField;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.IOException;
/**
* This class represents a heterogeneous collection of Permissions. That is,
* it contains different types of Permission objects, organized into
* PermissionCollections. For example, if any
* java.io.FilePermission
objects are added to an instance of
* this class, they are all stored in a single
* PermissionCollection. It is the PermissionCollection returned by a call to
* the newPermissionCollection
method in the FilePermission class.
* Similarly, any java.lang.RuntimePermission
objects are
* stored in the PermissionCollection returned by a call to the
* newPermissionCollection
method in the
* RuntimePermission class. Thus, this class represents a collection of
* PermissionCollections.
*
*
When the add
method is called to add a Permission, the
* Permission is stored in the appropriate PermissionCollection. If no such
* collection exists yet, the Permission object's class is determined and the
* newPermissionCollection
method is called on that class to create
* the PermissionCollection and add it to the Permissions object. If
* newPermissionCollection
returns null, then a default
* PermissionCollection that uses a hashtable will be created and used. Each
* hashtable entry stores a Permission object as both the key and the value.
*
*
Enumerations returned via the
*
* @param permission the Permission object to add.
*
* @exception SecurityException if this Permissions object is
* marked as readonly.
*
* @see PermissionCollection#isReadOnly()
*/
public void add(Permission permission) {
if (isReadOnly())
throw new SecurityException(
"attempt to add a Permission to a readonly Permissions object");
PermissionCollection pc;
synchronized (this) {
pc = getPermissionCollection(permission, true);
pc.add(permission);
}
// No sync; staleness -> optimizations delayed, which is OK
if (permission instanceof AllPermission) {
allPermission = pc;
}
if (permission instanceof UnresolvedPermission) {
hasUnresolved = true;
}
}
/**
* Checks to see if this object's PermissionCollection for permissions of
* the specified permission's type implies the permissions
* expressed in the permission object. Returns true if the
* combination of permissions in the appropriate PermissionCollection
* (e.g., a FilePermissionCollection for a FilePermission) together
* imply the specified permission.
*
* For example, suppose there is a FilePermissionCollection in this
* Permissions object, and it contains one FilePermission that specifies
* "read" access for all files in all subdirectories of the "/tmp"
* directory, and another FilePermission that specifies "write" access
* for all files in the "/tmp/scratch/foo" directory.
* Then if the Additionally, if this PermissionCollection contains the
* AllPermission, this method will always return true.
*
* @param permission the Permission object to check.
*
* @return true if "permission" is implied by the permissions in the
* PermissionCollection it
* belongs to, false if not.
*/
public boolean implies(Permission permission) {
// No sync; staleness -> skip optimization, which is OK
if (allPermission != null) {
return true; // AllPermission has already been added
} else {
synchronized (this) {
PermissionCollection pc = getPermissionCollection(permission,
false);
if (pc != null) {
return pc.implies(permission);
} else {
// none found
return false;
}
}
}
}
/**
* Returns an enumeration of all the Permission objects in all the
* PermissionCollections in this Permissions object.
*
* @return an enumeration of all the Permissions.
*/
public Enumerationelements
method are
* not fail-fast. Modifications to a collection should not be
* performed while enumerating over that collection.
*
* @see Permission
* @see PermissionCollection
* @see AllPermission
*
* @version 1.58, 04/05/05
*
* @author Marianne Mueller
* @author Roland Schemers
*
* @serial exclude
*/
public final class Permissions extends PermissionCollection
implements Serializable
{
/**
* Key is permissions Class, value is PermissionCollection for that class.
* Not serialized; see serialization section at end of class.
*/
private transient Mapimplies
method
* is called with a permission specifying both "read" and "write" access
* to files in the "/tmp/scratch/foo" directory, true
is
* returned.
*
* newPermissionCollection
method
* on p. Subclasses of class Permission
* override that method if they need to store their permissions in a
* particular PermissionCollection object in order to provide the
* correct semantics when the PermissionCollection.implies
* method is called.
* If the call returns a PermissionCollection, that collection is stored
* in this Permissions object. If the call returns null and createEmpty
* is true, then
* this method instantiates and stores a default PermissionCollection
* that uses a hashtable to store its permission objects.
*
* createEmpty is ignored when creating empty PermissionCollection
* for unresolved permissions because of the overhead of determining the
* PermissionCollection to use.
*
* createEmpty should be set to false when this method is invoked from
* implies() because it incurs the additional overhead of creating and
* adding an empty PermissionCollection that will just return false.
* It should be set to true when invoked from add().
*/
private PermissionCollection getPermissionCollection(Permission p,
boolean createEmpty) {
Class c = p.getClass();
PermissionCollection pc = (PermissionCollection) permsMap.get(c);
if (!hasUnresolved && !createEmpty) {
return pc;
} else if (pc == null) {
// Check for unresolved permissions
pc = (hasUnresolved ? getUnresolvedPermissions(p) : null);
// if still null, create a new collection
if (pc == null && createEmpty) {
pc = p.newPermissionCollection();
// still no PermissionCollection?
// We'll give them a PermissionsHash.
if (pc == null)
pc = new PermissionsHash();
}
if (pc != null) {
permsMap.put(c, pc);
}
}
return pc;
}
/**
* Resolves any unresolved permissions of type p.
*
* @param p the type of unresolved permission to resolve
*
* @return PermissionCollection containing the unresolved permissions,
* or null if there were no unresolved permissions of type p.
*
*/
private PermissionCollection getUnresolvedPermissions(Permission p)
{
// Called from within synchronized method so permsMap doesn't need lock
UnresolvedPermissionCollection uc =
(UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class);
// we have no unresolved permissions if uc is null
if (uc == null)
return null;
List unresolvedPerms = uc.getUnresolvedPermissions(p);
// we have no unresolved permissions of this type if unresolvedPerms is null
if (unresolvedPerms == null)
return null;
java.security.cert.Certificate certs[] = null;
Object signers[] = p.getClass().getSigners();
int n = 0;
if (signers != null) {
for (int j=0; j < signers.length; j++) {
if (signers[j] instanceof java.security.cert.Certificate) {
n++;
}
}
certs = new java.security.cert.Certificate[n];
n = 0;
for (int j=0; j < signers.length; j++) {
if (signers[j] instanceof java.security.cert.Certificate) {
certs[n++] = (java.security.cert.Certificate)signers[j];
}
}
}
PermissionCollection pc = null;
synchronized (unresolvedPerms) {
int len = unresolvedPerms.size();
for (int i = 0; i < len; i++) {
UnresolvedPermission up =
(UnresolvedPermission)unresolvedPerms.get(i);
Permission perm = up.resolve(p, certs);
if (perm != null) {
if (pc == null) {
pc = p.newPermissionCollection();
if (pc == null)
pc = new PermissionsHash();
}
pc.add(perm);
}
}
}
return pc;
}
private static final long serialVersionUID = 4858622370623524688L;
// Need to maintain serialization interoperability with earlier releases,
// which had the serializable field:
// private Hashtable perms;
/**
* @serialField perms java.util.Hashtable
* A table of the Permission classes and PermissionCollections.
* @serialField allPermission java.security.PermissionCollection
*/
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("perms", Hashtable.class),
new ObjectStreamField("allPermission", PermissionCollection.class),
};
/**
* @serialData Default fields.
*/
/*
* Writes the contents of the permsMap field out as a Hashtable for
* serialization compatibility with earlier releases. allPermission
* unchanged.
*/
private void writeObject(ObjectOutputStream out) throws IOException {
// Don't call out.defaultWriteObject()
// Copy perms into a Hashtable
Hashtable perms = new Hashtable(permsMap.size()*2); // no sync; estimate
synchronized (this) {
perms.putAll(permsMap);
}
// Write out serializable fields
ObjectOutputStream.PutField pfields = out.putFields();
pfields.put("allPermission", allPermission); // no sync; staleness OK
pfields.put("perms", perms);
out.writeFields();
}
/*
* Reads in a Hashtable of Class/PermissionCollections and saves them in the
* permsMap field. Reads in allPermission.
*/
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
// Don't call defaultReadObject()
// Read in serialized fields
ObjectInputStream.GetField gfields = in.readFields();
// Get allPermission
allPermission = (PermissionCollection) gfields.get("allPermission", null);
// Get permissions
Hashtable perms = (Hashtable)gfields.get("perms", null);
permsMap = new HashMap(perms.size()*2);
permsMap.putAll(perms);
// Set hasUnresolved
UnresolvedPermissionCollection uc =
(UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class);
hasUnresolved = (uc != null && uc.elements().hasMoreElements());
}
}
final class PermissionsEnumerator implements Enumeration