648 lines
23 KiB
Java
648 lines
23 KiB
Java
/*
|
|
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package java.security;
|
|
|
|
import java.io.InvalidObjectException;
|
|
import java.io.IOException;
|
|
import java.io.ObjectInputStream;
|
|
import java.io.ObjectOutputStream;
|
|
import java.io.ObjectStreamField;
|
|
import java.io.Serializable;
|
|
import java.util.Enumeration;
|
|
import java.util.Hashtable;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.NoSuchElementException;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
/**
|
|
* This class represents a heterogeneous collection of permissions.
|
|
* That is, it contains different types of {@code Permission} objects,
|
|
* organized into {@code PermissionCollection} objects. For example, if any
|
|
* {@code java.io.FilePermission} objects are added to an instance of
|
|
* this class, they are all stored in a single {@code PermissionCollection}.
|
|
* It is the {@code PermissionCollection} returned by a call to
|
|
* the {@code newPermissionCollection} method in the {@code FilePermission}
|
|
* class. Similarly, any {@code java.lang.RuntimePermission} objects are
|
|
* stored in the {@code PermissionCollection} returned by a call to the
|
|
* {@code newPermissionCollection} method in the {@code RuntimePermission}
|
|
* class. Thus, this class represents a collection of
|
|
* {@code PermissionCollection} objects.
|
|
*
|
|
* <p>When the {@code add} method is called to add a {@code Permission}, the
|
|
* {@code Permission} is stored in the appropriate {@code PermissionCollection}.
|
|
* If no such collection exists yet, the {@code Permission} object's class is
|
|
* determined and the {@code newPermissionCollection} method is called on that
|
|
* class to create the {@code PermissionCollection} and add it to the
|
|
* {@code Permissions} object. If {@code newPermissionCollection} returns
|
|
* {@code null}, then a default {@code PermissionCollection} that uses a
|
|
* hashtable will be created and used. Each hashtable entry stores a
|
|
* {@code 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
|
|
* @since 1.2
|
|
*
|
|
* @serial exclude
|
|
*/
|
|
|
|
public final class Permissions extends PermissionCollection
|
|
implements Serializable
|
|
{
|
|
/**
|
|
* Key is permissions Class, value is {@code PermissionCollection} for
|
|
* that class. Not serialized; see serialization section at end of class.
|
|
*/
|
|
private transient ConcurrentHashMap<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 {@code Permissions} object containing no
|
|
* {@code PermissionCollection} objects.
|
|
*/
|
|
public Permissions() {
|
|
permsMap = new ConcurrentHashMap<>(11);
|
|
allPermission = null;
|
|
}
|
|
|
|
/**
|
|
* Adds a {@code Permission} object to the {@code PermissionCollection}
|
|
* for the class the permission belongs to. For example,
|
|
* if <i>permission</i> is a {@code FilePermission}, it is added to
|
|
* the {@code FilePermissionCollection} stored in this
|
|
* {@code Permissions} object.
|
|
*
|
|
* This method creates a new {@code PermissionCollection} object
|
|
* (and adds the permission to it) if an appropriate collection does
|
|
* not yet exist.
|
|
*
|
|
* @param permission the {@code Permission} object to add.
|
|
*
|
|
* @throws SecurityException if this {@code Permissions} object is
|
|
* marked as readonly.
|
|
*
|
|
* @see PermissionCollection#isReadOnly()
|
|
*/
|
|
@Override
|
|
public void add(Permission permission) {
|
|
if (isReadOnly())
|
|
throw new SecurityException(
|
|
"attempt to add a Permission to a readonly Permissions object");
|
|
|
|
PermissionCollection 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 {@code PermissionCollection} for
|
|
* permissions of the specified permission's class implies the permissions
|
|
* expressed in the <i>permission</i> object. Returns {@code true} if the
|
|
* combination of permissions in the appropriate
|
|
* {@code PermissionCollection} (e.g., a {@code FilePermissionCollection}
|
|
* for a {@code FilePermission}) together imply the specified permission.
|
|
*
|
|
* <p>For example, suppose there is a {@code FilePermissionCollection}
|
|
* in this {@code Permissions} object, and it contains one
|
|
* {@code FilePermission} that specifies "read" access for all files
|
|
* in all subdirectories of the "/tmp" directory, and another
|
|
* {@code 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 {@code PermissionCollection} contains the
|
|
* {@code AllPermission}, this method will always return {@code true}.
|
|
*
|
|
* @param permission the {@code Permission} object to check.
|
|
*
|
|
* @return {@code true} if "permission" is implied by the permissions in the
|
|
* {@code PermissionCollection} it belongs to, {@code false} if not.
|
|
*/
|
|
@Override
|
|
public boolean implies(Permission permission) {
|
|
// No sync; staleness -> skip optimization, which is OK
|
|
if (allPermission != null) {
|
|
return true; // AllPermission has already been added
|
|
} else {
|
|
PermissionCollection pc = getPermissionCollection(permission,
|
|
false);
|
|
if (pc != null) {
|
|
return pc.implies(permission);
|
|
} else {
|
|
// none found
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns an enumeration of all the {@code Permission} objects in all the
|
|
* {@code PermissionCollection} objects in this {@code Permissions} object.
|
|
*
|
|
* @return an enumeration of all the {@code Permission} objects.
|
|
*/
|
|
@Override
|
|
public Enumeration<Permission> elements() {
|
|
// go through each Permissions in the hash table
|
|
// and call their elements() function.
|
|
|
|
return new PermissionsEnumerator(permsMap.values().iterator());
|
|
}
|
|
|
|
/**
|
|
* Gets the {@code PermissionCollection} in this {@code Permissions}
|
|
* object for permissions whose type is the same as that of <i>p</i>.
|
|
* For example, if <i>p</i> is a {@code FilePermission},
|
|
* the {@code FilePermissionCollection} stored in this {@code Permissions}
|
|
* object will be returned.
|
|
*
|
|
* If {@code createEmpty} is {@code true},
|
|
* this method creates a new {@code 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 {@code Permission}
|
|
* override that method if they need to store their permissions in a
|
|
* particular {@code PermissionCollection} object in order to provide the
|
|
* correct semantics when the {@code PermissionCollection.implies}
|
|
* method is called.
|
|
* If the call returns a {@code PermissionCollection}, that collection is
|
|
* stored in this {@code Permissions} object. If the call returns
|
|
* {@code null} and {@code createEmpty} is {@code true}, then this method
|
|
* instantiates and stores a default {@code PermissionCollection}
|
|
* that uses a hashtable to store its permission objects.
|
|
*
|
|
* {@code createEmpty} is ignored when creating empty
|
|
* {@code PermissionCollection} for unresolved permissions because of the
|
|
* overhead of determining the {@code PermissionCollection} to use.
|
|
*
|
|
* {@code createEmpty} should be set to {@code false} when this method is
|
|
* invoked from implies() because it incurs the additional overhead of
|
|
* creating and adding an empty {@code PermissionCollection} that will
|
|
* just return {@code false}.
|
|
* It should be set to {@code true} when invoked from add().
|
|
*/
|
|
private PermissionCollection getPermissionCollection(Permission p,
|
|
boolean createEmpty) {
|
|
PermissionCollection pc = permsMap.get(p.getClass());
|
|
if ((!hasUnresolved && !createEmpty) || pc != null) {
|
|
// Collection not to be created, or already created
|
|
return pc;
|
|
}
|
|
return createPermissionCollection(p, createEmpty);
|
|
}
|
|
|
|
private PermissionCollection createPermissionCollection(Permission p,
|
|
boolean createEmpty) {
|
|
synchronized (permsMap) {
|
|
// Re-read under lock
|
|
Class<?> c = p.getClass();
|
|
PermissionCollection pc = permsMap.get(c);
|
|
|
|
// Collection already created
|
|
if (pc != null) {
|
|
return pc;
|
|
}
|
|
|
|
// Create and add permission collection to map if it is absent.
|
|
// 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) {
|
|
// Add pc, resolving any race
|
|
PermissionCollection oldPc = permsMap.putIfAbsent(c, pc);
|
|
if (oldPc != null) {
|
|
pc = oldPc;
|
|
}
|
|
}
|
|
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 {@code null} if there were no unresolved permissions of type p.
|
|
*
|
|
*/
|
|
private PermissionCollection getUnresolvedPermissions(Permission p)
|
|
{
|
|
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;
|
|
}
|
|
|
|
@java.io.Serial
|
|
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 {@code Permission} classes and
|
|
* {@code PermissionCollection} objects.
|
|
* @serialField allPermission java.security.PermissionCollection
|
|
*/
|
|
@java.io.Serial
|
|
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.
|
|
*/
|
|
@java.io.Serial
|
|
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
|
|
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.
|
|
*/
|
|
@java.io.Serial
|
|
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);
|
|
|
|
if (perms == null) {
|
|
throw new InvalidObjectException("perms can't be null");
|
|
}
|
|
|
|
permsMap = new ConcurrentHashMap<>(perms.size()*2);
|
|
permsMap.putAll(perms);
|
|
|
|
// Check that Class is mapped to PermissionCollection containing
|
|
// Permissions of the same class
|
|
for (Map.Entry<Class<?>, PermissionCollection> e : perms.entrySet()) {
|
|
Class<?> k = e.getKey();
|
|
PermissionCollection v = e.getValue();
|
|
Enumeration<Permission> en = v.elements();
|
|
while (en.hasMoreElements()) {
|
|
Permission p = en.nextElement();
|
|
if (!k.equals(p.getClass())) {
|
|
throw new InvalidObjectException("Permission with class " +
|
|
k + " incorrectly mapped to PermissionCollection " +
|
|
"containing Permission with " + p.getClass());
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 final 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 {@code 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 ConcurrentHashMap<Permission, Permission> permsMap;
|
|
|
|
/**
|
|
* Create an empty {@code PermissionsHash} object.
|
|
*/
|
|
PermissionsHash() {
|
|
permsMap = new ConcurrentHashMap<>(11);
|
|
}
|
|
|
|
/**
|
|
* Adds a permission to the {@code PermissionsHash}.
|
|
*
|
|
* @param permission the {@code Permission} object to add.
|
|
*/
|
|
@Override
|
|
public void add(Permission permission) {
|
|
permsMap.put(permission, permission);
|
|
}
|
|
|
|
/**
|
|
* Check and see if this set of permissions implies the permissions
|
|
* expressed in "permission".
|
|
*
|
|
* @param permission the {@code Permission} object to compare
|
|
*
|
|
* @return {@code true} if "permission" is a proper subset of a permission
|
|
* in the set, {@code false} if not.
|
|
*/
|
|
@Override
|
|
public boolean implies(Permission permission) {
|
|
// attempt a fast lookup and implies. If that fails
|
|
// then enumerate through all the permissions.
|
|
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 {@code Permission} objects in the
|
|
* container.
|
|
*
|
|
* @return an enumeration of all the {@code Permission} objects.
|
|
*/
|
|
@Override
|
|
public Enumeration<Permission> elements() {
|
|
return permsMap.elements();
|
|
}
|
|
|
|
@java.io.Serial
|
|
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).
|
|
*/
|
|
@java.io.Serial
|
|
private static final ObjectStreamField[] serialPersistentFields = {
|
|
new ObjectStreamField("perms", Hashtable.class),
|
|
};
|
|
|
|
/**
|
|
* Writes the contents of the permsMap field out as a Hashtable for
|
|
* serialization compatibility with earlier releases.
|
|
*
|
|
* @param out the {@code ObjectOutputStream} to which data is written
|
|
* @throws IOException if an I/O error occurs
|
|
*/
|
|
@java.io.Serial
|
|
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);
|
|
perms.putAll(permsMap);
|
|
|
|
// Write out serializable fields
|
|
ObjectOutputStream.PutField pfields = out.putFields();
|
|
pfields.put("perms", perms);
|
|
out.writeFields();
|
|
}
|
|
|
|
/**
|
|
* Reads in a {@code Hashtable} of Permission/Permission and saves them
|
|
* in the permsMap field.
|
|
*
|
|
* @param in the {@code ObjectInputStream} from which data is read
|
|
* @throws IOException if an I/O error occurs
|
|
* @throws ClassNotFoundException if a serialized class cannot be loaded
|
|
*/
|
|
@java.io.Serial
|
|
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 ConcurrentHashMap<>(perms.size()*2);
|
|
permsMap.putAll(perms);
|
|
|
|
// check that the Permission key and value are the same object
|
|
for (Map.Entry<Permission, Permission> e : perms.entrySet()) {
|
|
Permission k = e.getKey();
|
|
Permission v = e.getValue();
|
|
if (k != v) {
|
|
throw new InvalidObjectException("Permission (" + k +
|
|
") incorrectly mapped to Permission (" + v + ")");
|
|
}
|
|
}
|
|
}
|
|
}
|