/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE 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.
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
* {@code 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 {@code newPermissionCollection} method in the FilePermission class.
* Similarly, any {@code java.lang.RuntimePermission} objects are
* stored in the PermissionCollection returned by a call to the
* {@code newPermissionCollection} method in the
* RuntimePermission class. Thus, this class represents a collection of
* PermissionCollections.
*
* <p>When the {@code 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
* {@code newPermissionCollection} method is called on that class to create
* the PermissionCollection and add it to the Permissions object. If
* {@code 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.
*
* <p> Enumerations returned via the {@code elements} method are
* not <em>fail-fast</em>. Modifications to a collection should not be
* performed while enumerating over that collection.
*
* @see Permission
* @see PermissionCollection
* @see AllPermission
*
*
* @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
Map<
Class<?>,
PermissionCollection>
permsMap;
// optimization. keep track of whether unresolved permissions need to be
// checked
private transient boolean
hasUnresolved = false;
// optimization. keep track of the AllPermission collection
// - package private for ProtectionDomain optimization
PermissionCollection allPermission;
/**
* Creates a new Permissions object containing no PermissionCollections.
*/
public
Permissions() {
permsMap = new
HashMap<
Class<?>,
PermissionCollection>(11);
allPermission = null;
}
/**
* Adds a permission object to the PermissionCollection for the class the
* permission belongs to. For example, if <i>permission</i> is a
* FilePermission, it is added to the FilePermissionCollection stored
* in this Permissions object.
*
* This method creates
* a new PermissionCollection object (and adds the permission to it)
* if an appropriate collection does not yet exist. <p>
*
* @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 class implies the permissions
* expressed in the <i>permission</i> object. Returns true if the
* combination of permissions in the appropriate PermissionCollection
* (e.g., a FilePermissionCollection for a FilePermission) together
* imply the specified permission.
*
* <p>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 {@code implies} method
* is called with a permission specifying both "read" and "write" access
* to files in the "/tmp/scratch/foo" directory, {@code true} is
* returned.
*
* <p>Additionally, if this PermissionCollection contains the
* AllPermission, this method will always return true.
* <p>
* @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
Enumeration<
Permission>
elements() {
// go through each Permissions in the hash table
// and call their elements() function.
synchronized (this) {
return new
PermissionsEnumerator(
permsMap.
values().
iterator());
}
}
/**
* Gets the PermissionCollection in this Permissions object for
* permissions whose type is the same as that of <i>p</i>.
* For example, if <i>p</i> is a FilePermission,
* the FilePermissionCollection
* stored in this Permissions object will be returned.
*
* If createEmpty is true,
* this method creates a new PermissionCollection object for the specified
* type of permission objects if one does not yet exist.
* To do so, it first calls the {@code newPermissionCollection} method
* on <i>p</i>. 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 {@code 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 =
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<
UnresolvedPermission>
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 =
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<
Class<?>,
PermissionCollection>
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
// writeObject writes a Hashtable<Class<?>, PermissionCollection> for
// the perms key, so this cast is safe, unless the data is corrupt.
@
SuppressWarnings("unchecked")
Hashtable<
Class<?>,
PermissionCollection>
perms =
(
Hashtable<
Class<?>,
PermissionCollection>)
gfields.
get("perms", null);
permsMap = new
HashMap<
Class<?>,
PermissionCollection>(
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<
Permission> {
// all the perms
private
Iterator<
PermissionCollection>
perms;
// the current set
private
Enumeration<
Permission>
permset;
PermissionsEnumerator(
Iterator<
PermissionCollection>
e) {
perms =
e;
permset =
getNextEnumWithMore();
}
// No need to synchronize; caller should sync on object as required
public boolean
hasMoreElements() {
// if we enter with permissionimpl null, we know
// there are no more left.
if (
permset == null)
return false;
// try to see if there are any left in the current one
if (
permset.
hasMoreElements())
return true;
// get the next one that has something in it...
permset =
getNextEnumWithMore();
// if it is null, we are done!
return (
permset != null);
}
// No need to synchronize; caller should sync on object as required
public
Permission nextElement() {
// hasMoreElements will update permset to the next permset
// with something in it...
if (
hasMoreElements()) {
return
permset.
nextElement();
} else {
throw new
NoSuchElementException("PermissionsEnumerator");
}
}
private
Enumeration<
Permission>
getNextEnumWithMore() {
while (
perms.
hasNext()) {
PermissionCollection pc =
perms.
next();
Enumeration<
Permission>
next =
pc.
elements();
if (
next.
hasMoreElements())
return
next;
}
return null;
}
}
/**
* A PermissionsHash stores a homogeneous set of permissions in a hashtable.
*
* @see Permission
* @see Permissions
*
*
* @author Roland Schemers
*
* @serial include
*/
final class
PermissionsHash extends
PermissionCollection
implements
Serializable
{
/**
* Key and value are (same) permissions objects.
* Not serialized; see serialization section at end of class.
*/
private transient
Map<
Permission,
Permission>
permsMap;
/**
* Create an empty PermissionsHash object.
*/
PermissionsHash() {
permsMap = new
HashMap<
Permission,
Permission>(11);
}
/**
* Adds a permission to the PermissionsHash.
*
* @param permission the Permission object to add.
*/
public void
add(
Permission permission) {
synchronized (this) {
permsMap.
put(
permission,
permission);
}
}
/**
* Check and see if this set of permissions implies the permissions
* expressed in "permission".
*
* @param permission the Permission object to compare
*
* @return true if "permission" is a proper subset of a permission in
* the set, false if not.
*/
public boolean
implies(
Permission permission) {
// attempt a fast lookup and implies. If that fails
// then enumerate through all the permissions.
synchronized (this) {
Permission p =
permsMap.
get(
permission);
// If permission is found, then p.equals(permission)
if (
p == null) {
for (
Permission p_ :
permsMap.
values()) {
if (
p_.
implies(
permission))
return true;
}
return false;
} else {
return true;
}
}
}
/**
* Returns an enumeration of all the Permission objects in the container.
*
* @return an enumeration of all the Permissions.
*/
public
Enumeration<
Permission>
elements() {
// Convert Iterator of Map values into an Enumeration
synchronized (this) {
return
Collections.
enumeration(
permsMap.
values());
}
}
private static final long
serialVersionUID = -8491988220802933440L;
// 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 Permissions (both key and value are same).
*/
private static final
ObjectStreamField[]
serialPersistentFields = {
new
ObjectStreamField("perms",
Hashtable.class),
};
/**
* @serialData Default fields.
*/
/*
* Writes the contents of the permsMap field out as a Hashtable for
* serialization compatibility with earlier releases.
*/
private void
writeObject(
ObjectOutputStream out) throws
IOException {
// Don't call out.defaultWriteObject()
// Copy perms into a Hashtable
Hashtable<
Permission,
Permission>
perms =
new
Hashtable<>(
permsMap.
size()*2);
synchronized (this) {
perms.
putAll(
permsMap);
}
// Write out serializable fields
ObjectOutputStream.
PutField pfields =
out.
putFields();
pfields.
put("perms",
perms);
out.
writeFields();
}
/*
* Reads in a Hashtable of Permission/Permission and saves them in the
* permsMap field.
*/
private void
readObject(
ObjectInputStream in) throws
IOException,
ClassNotFoundException {
// Don't call defaultReadObject()
// Read in serialized fields
ObjectInputStream.
GetField gfields =
in.
readFields();
// Get permissions
// writeObject writes a Hashtable<Class<?>, PermissionCollection> for
// the perms key, so this cast is safe, unless the data is corrupt.
@
SuppressWarnings("unchecked")
Hashtable<
Permission,
Permission>
perms =
(
Hashtable<
Permission,
Permission>)
gfields.
get("perms", null);
permsMap = new
HashMap<
Permission,
Permission>(
perms.
size()*2);
permsMap.
putAll(
perms);
}
}