/* * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.osgi.framework; import java.io.IOException; import java.io.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; import java.security.AccessController; import java.security.BasicPermission; import java.security.Permission; import java.security.PermissionCollection; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Map; /** * A bundle's authority to perform specific privileged administrative operations * on or to get sensitive information about a bundle. The actions for this * permission are: * * <pre> * Action Methods * class Bundle.loadClass * execute Bundle.start * Bundle.stop * BundleStartLevel.setStartLevel * extensionLifecycle BundleContext.installBundle for extension bundles * Bundle.update for extension bundles * Bundle.uninstall for extension bundles * lifecycle BundleContext.installBundle * Bundle.update * Bundle.uninstall * listener BundleContext.addBundleListener for SynchronousBundleListener * BundleContext.removeBundleListener for SynchronousBundleListener * metadata Bundle.getHeaders * Bundle.getLocation * resolve FrameworkWiring.refreshBundles * FrameworkWiring.resolveBundles * resource Bundle.getResource * Bundle.getResources * Bundle.getEntry * Bundle.getEntryPaths * Bundle.findEntries * Bundle resource/entry URL creation * startlevel FrameworkStartLevel.setStartLevel * FrameworkStartLevel.setInitialBundleStartLevel * context Bundle.getBundleContext * weave WovenClass.setBytes * WovenClass.getDynamicImports for modification * </pre> * * <p> * The special action "*" will represent all actions. The * {@code resolve} action is implied by the {@code class}, {@code execute} and * {@code resource} actions. * <p> * The name of this permission is a filter expression. The filter gives access * to the following attributes: * <ul> * <li>signer - A Distinguished Name chain used to sign a bundle. Wildcards in a * DN are not matched according to the filter string rules, but according to the * rules defined for a DN chain.</li> * <li>location - The location of a bundle.</li> * <li>id - The bundle ID of the designated bundle.</li> * <li>name - The symbolic name of a bundle.</li> * </ul> * Filter attribute names are processed in a case sensitive manner. * * @ThreadSafe * @version $Id: cd883e81fde210ce8f0cabaebea377378d672818 $ */ public final class AdminPermission extends BasicPermission { static final long serialVersionUID = 307051004521261705L; /** * The action string {@code class}. The {@code class} action implies the * {@code resolve} action. * * @since 1.3 */ public final static String CLASS = "class"; /** * The action string {@code execute}. The {@code execute} action implies the * {@code resolve} action. * * @since 1.3 */ public final static String EXECUTE = "execute"; /** * The action string {@code extensionLifecycle}. * * @since 1.3 */ public final static String EXTENSIONLIFECYCLE = "extensionLifecycle"; /** * The action string {@code lifecycle}. * * @since 1.3 */ public final static String LIFECYCLE = "lifecycle"; /** * The action string {@code listener}. * * @since 1.3 */ public final static String LISTENER = "listener"; /** * The action string {@code metadata}. * * @since 1.3 */ public final static String METADATA = "metadata"; /** * The action string {@code resolve}. The {@code resolve} action is implied * by the {@code class}, {@code execute} and {@code resource} actions. * * @since 1.3 */ public final static String RESOLVE = "resolve"; /** * The action string {@code resource}. The {@code resource} action implies * the {@code resolve} action. * * @since 1.3 */ public final static String RESOURCE = "resource"; /** * The action string {@code startlevel}. * * @since 1.3 */ public final static String STARTLEVEL = "startlevel"; /** * The action string {@code context}. * * @since 1.4 */ public final static String CONTEXT = "context"; /** * The action string {@code weave}. * * @since 1.6 */ public final static String WEAVE = "weave"; private final static int ACTION_CLASS = 0x00000001; private final static int ACTION_EXECUTE = 0x00000002; private final static int ACTION_LIFECYCLE = 0x00000004; private final static int ACTION_LISTENER = 0x00000008; private final static int ACTION_METADATA = 0x00000010; private final static int ACTION_RESOLVE = 0x00000040; private final static int ACTION_RESOURCE = 0x00000080; private final static int ACTION_STARTLEVEL = 0x00000100; private final static int ACTION_EXTENSIONLIFECYCLE = 0x00000200; private final static int ACTION_CONTEXT = 0x00000400; private final static int ACTION_WEAVE = 0x00000800; private final static int ACTION_ALL = ACTION_CLASS | ACTION_EXECUTE | ACTION_LIFECYCLE | ACTION_LISTENER | ACTION_METADATA | ACTION_RESOLVE | ACTION_RESOURCE | ACTION_STARTLEVEL | ACTION_EXTENSIONLIFECYCLE | ACTION_CONTEXT | ACTION_WEAVE; final static int ACTION_NONE = 0; /** * The actions in canonical form. * * @serial */ private volatile String actions = null; /** * The actions mask. */ transient int action_mask; /** * If this AdminPermission was constructed with a filter, this holds a * Filter matching object used to evaluate the filter in implies. */ transient Filter filter; /** * The bundle governed by this AdminPermission - only used if filter == null */ transient final Bundle bundle; /** * This map holds the properties of the permission, used to match a filter * in implies. This is not initialized until necessary, and then cached in * this object. */ private transient volatile Map<String, Object> properties; /** * ThreadLocal used to determine if we have recursively called * getProperties. */ private static final ThreadLocal<Bundle> recurse = new ThreadLocal<Bundle>(); /** * Creates a new {@code AdminPermission} object that matches all bundles and * has all actions. Equivalent to AdminPermission("*","*"); */ public AdminPermission() { this(null, ACTION_ALL); } /** * Create a new AdminPermission. * * This constructor must only be used to create a permission that is going * to be checked. * <p> * Examples: * * <pre> * (signer=\*,o=ACME,c=US) * (&(signer=\*,o=ACME,c=US)(name=com.acme.*)(location=http://www.acme.com/bundles/*)) * (id>=1) * </pre> * * <p> * When a signer key is used within the filter expression the signer value * must escape the special filter chars ('*', '(', ')'). * <p> * Null arguments are equivalent to "*". * * @param filter A filter expression that can use signer, location, id, and * name keys. A value of "*" or {@code null} matches all * bundle. Filter attribute names are processed in a case sensitive * manner. * @param actions {@code class}, {@code execute}, {@code extensionLifecycle} * , {@code lifecycle}, {@code listener}, {@code metadata}, * {@code resolve} , {@code resource}, {@code startlevel}, * {@code context} or {@code weave}. A value of "*" or {@code null} * indicates all actions. * @throws IllegalArgumentException If the filter has an invalid syntax. */ public AdminPermission(String filter, String actions) { // arguments will be null if called from a PermissionInfo defined with // no args this(parseFilter(filter), parseActions(actions)); } /** * Creates a new requested {@code AdminPermission} object to be used by the * code that must perform {@code checkPermission}. {@code AdminPermission} * objects created with this constructor cannot be added to an * {@code AdminPermission} permission collection. * * @param bundle A bundle. * @param actions {@code class}, {@code execute}, {@code extensionLifecycle} * , {@code lifecycle}, {@code listener}, {@code metadata}, * {@code resolve} , {@code resource}, {@code startlevel}, * {@code context}, {@code weave}. A value of "*" or {@code null} * indicates all actions. * @since 1.3 */ public AdminPermission(Bundle bundle, String actions) { super(createName(bundle)); setTransients(null, parseActions(actions)); this.bundle = bundle; } /** * Create a permission name from a Bundle * * @param bundle Bundle to use to create permission name. * @return permission name. */ private static String createName(Bundle bundle) { if (bundle == null) { throw new IllegalArgumentException("bundle must not be null"); } StringBuffer sb = new StringBuffer("(id="); sb.append(bundle.getBundleId()); sb.append(")"); return sb.toString(); } /** * Package private constructor used by AdminPermissionCollection. * * @param filter name filter or {@code null} for wildcard. * @param mask action mask */ AdminPermission(Filter filter, int mask) { super((filter == null) ? "*" : filter.toString()); setTransients(filter, mask); this.bundle = null; } /** * Called by constructors and when deserialized. * * @param filter Permission's filter or {@code null} for wildcard. * @param mask action mask */ private void setTransients(Filter filter, int mask) { this.filter = filter; if ((mask == ACTION_NONE) || ((mask & ACTION_ALL) != mask)) { throw new IllegalArgumentException("invalid action string"); } this.action_mask = mask; } /** * Parse action string into action mask. * * @param actions Action string. * @return action mask. */ private static int parseActions(String actions) { if ((actions == null) || actions.equals("*")) { return ACTION_ALL; } boolean seencomma = false; int mask = ACTION_NONE; char[] a = actions.toCharArray(); int i = a.length - 1; if (i < 0) return mask; while (i != -1) { char c; // skip whitespace while ((i != -1) && ((c = a[i]) == ' ' || c == '\r' || c == '\n' || c == '\f' || c == '\t')) i--; // check for the known strings int matchlen; if (i >= 4 && (a[i - 4] == 'c' || a[i - 4] == 'C') && (a[i - 3] == 'l' || a[i - 3] == 'L') && (a[i - 2] == 'a' || a[i - 2] == 'A') && (a[i - 1] == 's' || a[i - 1] == 'S') && (a[i] == 's' || a[i] == 'S')) { matchlen = 5; mask |= ACTION_CLASS | ACTION_RESOLVE; } else if (i >= 6 && (a[i - 6] == 'e' || a[i - 6] == 'E') && (a[i - 5] == 'x' || a[i - 5] == 'X') && (a[i - 4] == 'e' || a[i - 4] == 'E') && (a[i - 3] == 'c' || a[i - 3] == 'C') && (a[i - 2] == 'u' || a[i - 2] == 'U') && (a[i - 1] == 't' || a[i - 1] == 'T') && (a[i] == 'e' || a[i] == 'E')) { matchlen = 7; mask |= ACTION_EXECUTE | ACTION_RESOLVE; } else if (i >= 17 && (a[i - 17] == 'e' || a[i - 17] == 'E') && (a[i - 16] == 'x' || a[i - 16] == 'X') && (a[i - 15] == 't' || a[i - 15] == 'T') && (a[i - 14] == 'e' || a[i - 14] == 'E') && (a[i - 13] == 'n' || a[i - 13] == 'N') && (a[i - 12] == 's' || a[i - 12] == 'S') && (a[i - 11] == 'i' || a[i - 11] == 'I') && (a[i - 10] == 'o' || a[i - 10] == 'O') && (a[i - 9] == 'n' || a[i - 9] == 'N') && (a[i - 8] == 'l' || a[i - 8] == 'L') && (a[i - 7] == 'i' || a[i - 7] == 'I') && (a[i - 6] == 'f' || a[i - 6] == 'F') && (a[i - 5] == 'e' || a[i - 5] == 'E') && (a[i - 4] == 'c' || a[i - 4] == 'C') && (a[i - 3] == 'y' || a[i - 3] == 'Y') && (a[i - 2] == 'c' || a[i - 2] == 'C') && (a[i - 1] == 'l' || a[i - 1] == 'L') && (a[i] == 'e' || a[i] == 'E')) { matchlen = 18; mask |= ACTION_EXTENSIONLIFECYCLE; } else if (i >= 8 && (a[i - 8] == 'l' || a[i - 8] == 'L') && (a[i - 7] == 'i' || a[i - 7] == 'I') && (a[i - 6] == 'f' || a[i - 6] == 'F') && (a[i - 5] == 'e' || a[i - 5] == 'E') && (a[i - 4] == 'c' || a[i - 4] == 'C') && (a[i - 3] == 'y' || a[i - 3] == 'Y') && (a[i - 2] == 'c' || a[i - 2] == 'C') && (a[i - 1] == 'l' || a[i - 1] == 'L') && (a[i] == 'e' || a[i] == 'E')) { matchlen = 9; mask |= ACTION_LIFECYCLE; } else if (i >= 7 && (a[i - 7] == 'l' || a[i - 7] == 'L') && (a[i - 6] == 'i' || a[i - 6] == 'I') && (a[i - 5] == 's' || a[i - 5] == 'S') && (a[i - 4] == 't' || a[i - 4] == 'T') && (a[i - 3] == 'e' || a[i - 3] == 'E') && (a[i - 2] == 'n' || a[i - 2] == 'N') && (a[i - 1] == 'e' || a[i - 1] == 'E') && (a[i] == 'r' || a[i] == 'R')) { matchlen = 8; mask |= ACTION_LISTENER; } else if (i >= 7 && (a[i - 7] == 'm' || a[i - 7] == 'M') && (a[i - 6] == 'e' || a[i - 6] == 'E') && (a[i - 5] == 't' || a[i - 5] == 'T') && (a[i - 4] == 'a' || a[i - 4] == 'A') && (a[i - 3] == 'd' || a[i - 3] == 'D') && (a[i - 2] == 'a' || a[i - 2] == 'A') && (a[i - 1] == 't' || a[i - 1] == 'T') && (a[i] == 'a' || a[i] == 'A')) { matchlen = 8; mask |= ACTION_METADATA; } else if (i >= 6 && (a[i - 6] == 'r' || a[i - 6] == 'R') && (a[i - 5] == 'e' || a[i - 5] == 'E') && (a[i - 4] == 's' || a[i - 4] == 'S') && (a[i - 3] == 'o' || a[i - 3] == 'O') && (a[i - 2] == 'l' || a[i - 2] == 'L') && (a[i - 1] == 'v' || a[i - 1] == 'V') && (a[i] == 'e' || a[i] == 'E')) { matchlen = 7; mask |= ACTION_RESOLVE; } else if (i >= 7 && (a[i - 7] == 'r' || a[i - 7] == 'R') && (a[i - 6] == 'e' || a[i - 6] == 'E') && (a[i - 5] == 's' || a[i - 5] == 'S') && (a[i - 4] == 'o' || a[i - 4] == 'O') && (a[i - 3] == 'u' || a[i - 3] == 'U') && (a[i - 2] == 'r' || a[i - 2] == 'R') && (a[i - 1] == 'c' || a[i - 1] == 'C') && (a[i] == 'e' || a[i] == 'E')) { matchlen = 8; mask |= ACTION_RESOURCE | ACTION_RESOLVE; } else if (i >= 9 && (a[i - 9] == 's' || a[i - 9] == 'S') && (a[i - 8] == 't' || a[i - 8] == 'T') && (a[i - 7] == 'a' || a[i - 7] == 'A') && (a[i - 6] == 'r' || a[i - 6] == 'R') && (a[i - 5] == 't' || a[i - 5] == 'T') && (a[i - 4] == 'l' || a[i - 4] == 'L') && (a[i - 3] == 'e' || a[i - 3] == 'E') && (a[i - 2] == 'v' || a[i - 2] == 'V') && (a[i - 1] == 'e' || a[i - 1] == 'E') && (a[i] == 'l' || a[i] == 'L')) { matchlen = 10; mask |= ACTION_STARTLEVEL; } else if (i >= 6 && (a[i - 6] == 'c' || a[i - 6] == 'C') && (a[i - 5] == 'o' || a[i - 5] == 'O') && (a[i - 4] == 'n' || a[i - 4] == 'N') && (a[i - 3] == 't' || a[i - 3] == 'T') && (a[i - 2] == 'e' || a[i - 2] == 'E') && (a[i - 1] == 'x' || a[i - 1] == 'X') && (a[i] == 't' || a[i] == 'T')) { matchlen = 7; mask |= ACTION_CONTEXT; } else if (i >= 4 && (a[i - 4] == 'w' || a[i - 4] == 'W') && (a[i - 3] == 'e' || a[i - 3] == 'E') && (a[i - 2] == 'a' || a[i - 2] == 'A') && (a[i - 1] == 'v' || a[i - 1] == 'V') && (a[i] == 'e' || a[i] == 'E')) { matchlen = 5; mask |= ACTION_WEAVE; } else if (i >= 0 && (a[i] == '*')) { matchlen = 1; mask |= ACTION_ALL; } else { // parse error throw new IllegalArgumentException("invalid permission: " + actions); } // make sure we didn't just match the tail of a word // like "ackbarfstartlevel". Also, skip to the comma. seencomma = false; while (i >= matchlen && !seencomma) { switch (a[i - matchlen]) { case ',' : seencomma = true; /* FALLTHROUGH */ case ' ' : case '\r' : case '\n' : case '\f' : case '\t' : break; default : throw new IllegalArgumentException("invalid permission: " + actions); } i--; } // point i at the location of the comma minus one (or -1). i -= matchlen; } if (seencomma) { throw new IllegalArgumentException("invalid permission: " + actions); } return mask; } /** * Parse filter string into a Filter object. * * @param filterString The filter string to parse. * @return a Filter for this bundle. If the specified filterString is * {@code null} or equals "*", then {@code null} is returned to * indicate a wildcard. * @throws IllegalArgumentException If the filter syntax is invalid. */ private static Filter parseFilter(String filterString) { if (filterString == null) { return null; } filterString = filterString.trim(); if (filterString.equals("*")) { return null; } try { return FrameworkUtil.createFilter(filterString); } catch (InvalidSyntaxException e) { IllegalArgumentException iae = new IllegalArgumentException("invalid filter"); iae.initCause(e); throw iae; } } /** * Determines if the specified permission is implied by this object. This * method throws an exception if the specified permission was not * constructed with a bundle. * * <p> * This method returns {@code true} if the specified permission is an * AdminPermission AND * <ul> * <li>this object's filter matches the specified permission's bundle ID, * bundle symbolic name, bundle location and bundle signer distinguished * name chain OR</li> * <li>this object's filter is "*"</li> * </ul> * AND this object's actions include all of the specified permission's * actions. * <p> * Special case: if the specified permission was constructed with "*" * filter, then this method returns {@code true} if this object's filter is * "*" and this object's actions include all of the specified permission's * actions * * @param p The requested permission. * @return {@code true} if the specified permission is implied by this * object; {@code false} otherwise. */ public boolean implies(Permission p) { if (!(p instanceof AdminPermission)) { return false; } AdminPermission requested = (AdminPermission) p; if (bundle != null) { return false; } // if requested permission has a filter, then it is an invalid argument if (requested.filter != null) { return false; } return implies0(requested, ACTION_NONE); } /** * Internal implies method. Used by the implies and the permission * collection implies methods. * * @param requested The requested AdminPermision which has already be * validated as a proper argument. The requested AdminPermission must * not have a filter expression. * @param effective The effective actions with which to start. * @return {@code true} if the specified permission is implied by this * object; {@code false} otherwise. */ boolean implies0(AdminPermission requested, int effective) { /* check actions first - much faster */ effective |= action_mask; final int desired = requested.action_mask; if ((effective & desired) != desired) { return false; } /* Get our filter */ Filter f = filter; if (f == null) { // it's "*" return true; } /* is requested a wildcard filter? */ if (requested.bundle == null) { return false; } Map<String, Object> requestedProperties = requested.getProperties(); if (requestedProperties == null) { /* * If the requested properties are null, then we have detected a * recursion getting the bundle location. So we return true to * permit the bundle location request in the AdminPermission check * up the stack to succeed. */ return true; } return f.matches(requestedProperties); } /** * Returns the canonical string representation of the * {@code AdminPermission} actions. * * <p> * Always returns present {@code AdminPermission} actions in the following * order: {@code class}, {@code execute}, {@code extensionLifecycle}, * {@code lifecycle}, {@code listener}, {@code metadata}, {@code resolve}, * {@code resource}, {@code startlevel}, {@code context}, {@code weave}. * * @return Canonical string representation of the {@code AdminPermission} * actions. */ public String getActions() { String result = actions; if (result == null) { StringBuffer sb = new StringBuffer(); int mask = action_mask; if ((mask & ACTION_CLASS) == ACTION_CLASS) { sb.append(CLASS); sb.append(','); } if ((mask & ACTION_EXECUTE) == ACTION_EXECUTE) { sb.append(EXECUTE); sb.append(','); } if ((mask & ACTION_EXTENSIONLIFECYCLE) == ACTION_EXTENSIONLIFECYCLE) { sb.append(EXTENSIONLIFECYCLE); sb.append(','); } if ((mask & ACTION_LIFECYCLE) == ACTION_LIFECYCLE) { sb.append(LIFECYCLE); sb.append(','); } if ((mask & ACTION_LISTENER) == ACTION_LISTENER) { sb.append(LISTENER); sb.append(','); } if ((mask & ACTION_METADATA) == ACTION_METADATA) { sb.append(METADATA); sb.append(','); } if ((mask & ACTION_RESOLVE) == ACTION_RESOLVE) { sb.append(RESOLVE); sb.append(','); } if ((mask & ACTION_RESOURCE) == ACTION_RESOURCE) { sb.append(RESOURCE); sb.append(','); } if ((mask & ACTION_STARTLEVEL) == ACTION_STARTLEVEL) { sb.append(STARTLEVEL); sb.append(','); } if ((mask & ACTION_CONTEXT) == ACTION_CONTEXT) { sb.append(CONTEXT); sb.append(','); } if ((mask & ACTION_WEAVE) == ACTION_WEAVE) { sb.append(WEAVE); sb.append(','); } // remove trailing comma if (sb.length() > 0) { sb.setLength(sb.length() - 1); } actions = result = sb.toString(); } return result; } /** * Returns a new {@code PermissionCollection} object suitable for storing * {@code AdminPermission}s. * * @return A new {@code PermissionCollection} object. */ public PermissionCollection newPermissionCollection() { return new AdminPermissionCollection(); } /** * Determines the equality of two {@code AdminPermission} objects. * * @param obj The object being compared for equality with this object. * @return {@code true} if {@code obj} is equivalent to this * {@code AdminPermission}; {@code false} otherwise. */ public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof AdminPermission)) { return false; } AdminPermission ap = (AdminPermission) obj; return (action_mask == ap.action_mask) && ((bundle == ap.bundle) || ((bundle != null) && bundle.equals(ap.bundle))) && (filter == null ? ap.filter == null : filter.equals(ap.filter)); } /** * Returns the hash code value for this object. * * @return Hash code value for this object. */ public int hashCode() { int h = 31 * 17 + getName().hashCode(); h = 31 * h + getActions().hashCode(); if (bundle != null) { h = 31 * h + bundle.hashCode(); } return h; } /** * WriteObject is called to save the state of this permission object to a * stream. The actions are serialized, and the superclass takes care of the * name. */ private synchronized void writeObject(java.io.ObjectOutputStream s) throws IOException { if (bundle != null) { throw new NotSerializableException("cannot serialize"); } // Write out the actions. The superclass takes care of the name // call getActions to make sure actions field is initialized if (actions == null) getActions(); s.defaultWriteObject(); } /** * readObject is called to restore the state of this permission from a * stream. */ private synchronized void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { // Read in the data, then initialize the transients s.defaultReadObject(); setTransients(parseFilter(getName()), parseActions(actions)); } /** * Called by {@code implies0} on an AdminPermission which was constructed * with a Bundle. This method loads a map with the filter-matchable * properties of this bundle. The map is cached so this lookup only happens * once. * * This method should only be called on an AdminPermission which was * constructed with a bundle * * @return a map of properties for this bundle */ private Map<String, Object> getProperties() { Map<String, Object> result = properties; if (result != null) { return result; } /* * We may have recursed here due to the Bundle.getLocation call in the * doPrivileged below. If this is the case, return null to allow implies * to return true. */ final Object mark = recurse.get(); if (mark == bundle) { return null; } recurse.set(bundle); try { final Map<String, Object> map = new HashMap<String, Object>(4); AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { map.put("id", new Long(bundle.getBundleId())); map.put("location", bundle.getLocation()); String name = bundle.getSymbolicName(); if (name != null) { map.put("name", name); } SignerProperty signer = new SignerProperty(bundle); if (signer.isBundleSigned()) { map.put("signer", signer); } return null; } }); return properties = map; } finally { recurse.set(null); } } } /** * Stores a collection of {@code AdminPermission}s. */ final class AdminPermissionCollection extends PermissionCollection { private static final long serialVersionUID = 3906372644575328048L; /** * Collection of permissions. * * @GuardedBy this */ private transient Map<String, AdminPermission> permissions; /** * Boolean saying if "*" is in the collection. * * @serial * @GuardedBy this */ private boolean all_allowed; /** * Create an empty AdminPermissions object. * */ public AdminPermissionCollection() { permissions = new HashMap<String, AdminPermission>(); } /** * Adds a permission to this permission collection. * * @param permission The {@code AdminPermission} object to add. * @throws IllegalArgumentException If the specified permission is not an * {@code AdminPermission} instance or was constructed with a Bundle * object. * @throws SecurityException If this {@code AdminPermissionCollection} * object has been marked read-only. */ public void add(Permission permission) { if (!(permission instanceof AdminPermission)) { throw new IllegalArgumentException("invalid permission: " + permission); } if (isReadOnly()) { throw new SecurityException("attempt to add a Permission to a " + "readonly PermissionCollection"); } final AdminPermission ap = (AdminPermission) permission; if (ap.bundle != null) { throw new IllegalArgumentException("cannot add to collection: " + ap); } final String name = ap.getName(); synchronized (this) { Map<String, AdminPermission> pc = permissions; AdminPermission existing = pc.get(name); if (existing != null) { int oldMask = existing.action_mask; int newMask = ap.action_mask; if (oldMask != newMask) { pc.put(name, new AdminPermission(existing.filter, oldMask | newMask)); } } else { pc.put(name, ap); } if (!all_allowed) { if (name.equals("*")) { all_allowed = true; } } } } /** * Determines if the specified permissions implies the permissions expressed * in {@code permission}. * * @param permission The Permission object to compare with the * {@code AdminPermission} objects in this collection. * @return {@code true} if {@code permission} is implied by an * {@code AdminPermission} in this collection, {@code false} * otherwise. */ public boolean implies(Permission permission) { if (!(permission instanceof AdminPermission)) { return false; } AdminPermission requested = (AdminPermission) permission; // if requested permission has a filter, then it is an invalid argument if (requested.filter != null) { return false; } int effective = AdminPermission.ACTION_NONE; Collection<AdminPermission> perms; synchronized (this) { Map<String, AdminPermission> pc = permissions; // short circuit if the "*" Permission was added if (all_allowed) { AdminPermission ap = pc.get("*"); if (ap != null) { effective |= ap.action_mask; final int desired = requested.action_mask; if ((effective & desired) == desired) { return true; } } } perms = pc.values(); } // just iterate one by one for (AdminPermission perm : perms) { if (perm.implies0(requested, effective)) { return true; } } return false; } /** * Returns an enumeration of all {@code AdminPermission} objects in the * container. * * @return Enumeration of all {@code AdminPermission} objects. */ public synchronized Enumeration<Permission> elements() { List<Permission> all = new ArrayList<Permission>(permissions.values()); return Collections.enumeration(all); } /* serialization logic */ private static final ObjectStreamField[] serialPersistentFields = {new ObjectStreamField("permissions", Hashtable.class), new ObjectStreamField("all_allowed", Boolean.TYPE)}; private synchronized void writeObject(ObjectOutputStream out) throws IOException { Hashtable<String, AdminPermission> hashtable = new Hashtable<String, AdminPermission>(permissions); ObjectOutputStream.PutField pfields = out.putFields(); pfields.put("permissions", hashtable); pfields.put("all_allowed", all_allowed); out.writeFields(); } private synchronized void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { ObjectInputStream.GetField gfields = in.readFields(); Hashtable<String, AdminPermission> hashtable = (Hashtable<String, AdminPermission>) gfields.get("permissions", null); permissions = new HashMap<String, AdminPermission>(hashtable); all_allowed = gfields.get("all_allowed", false); } }