You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@river.apache.org by pe...@apache.org on 2010/03/28 14:57:05 UTC
svn commit: r928394 [2/6] - in /incubator/river/jtsk/trunk: ./ qa/
qa/harness/policy/ qa/jtreg/net/jini/jeri/kerberos/UnitTests/
qa/jtreg/net/jini/jeri/transport/multihomed/ qa/jtreg/unittestlib/
qa/src/com/sun/jini/qa/harness/ qa/src/com/sun/jini/qa/r...
Modified: incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProvider.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProvider.java?rev=928394&r1=928393&r2=928394&view=diff
==============================================================================
--- incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProvider.java (original)
+++ incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProvider.java Sun Mar 28 12:57:03 2010
@@ -1,81 +1,139 @@
/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
*/
package net.jini.security.policy;
-import com.sun.jini.collection.WeakIdentityMap;
-import java.lang.ref.ReferenceQueue;
-import java.lang.ref.WeakReference;
+import java.security.AccessControlException;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.Permission;
import java.security.PermissionCollection;
-import java.security.Permissions;
-import java.security.Principal;
import java.security.Policy;
+import java.security.Principal;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.security.Security;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
-import net.jini.security.GrantPermission;
+//import java.util.ServiceLoader;
+import java.util.logging.Logger;
+import java.util.logging.Level;
+import sun.misc.Service;
+import org.apache.river.security.policy.spi.RevokeablePolicy;
+import org.apache.river.security.policy.spi.RevokeableDynamicPolicySpi;
/**
- * Security policy provider that supports dynamic granting of permissions at
- * run-time. This provider is designed as a wrapper to layer dynamic grant
- * functionality on top of an underlying policy provider. If the underlying
- * provider does not implement the {@link DynamicPolicy} interface, then its
- * permission mappings are assumed to change only when its {@link
- * Policy#refresh refresh} method is called. Permissions are granted on the
- * granularity of class loader; granting a permission requires (of the calling
- * context) {@link GrantPermission} for that permission.
- *
- * @author Sun Microsystems, Inc.
+ * This class replaces the existing DynamicPolicyProvider, the existing
+ * implementation has been modified to partially
+ * implement RevokableDynamiPolicySpi but doing so in a manner compatible with
+ * Java 1.4. In that implementation it will throw an exception for the revoke
+ * method unless the additional work required has sufficient
+ * demand, in which case it may be implemented for java 1.4 also.
+ *
+ * I would have liked to use the java 6 ServiceLoader in preference
+ * to the Java 1.4 and cdc (java 1.4) foundation profile 1.1.2 compatible
+ * forbidden sun.misc.Service implementation, since ServiceLoader doesn't exist
+ * in Java 1.4, this might be better as an OSGi service. A dependency could
+ * be resolved automatically based on the platform.
*
- * @since 2.0
+ * @author Peter Firmstone
*/
-public class DynamicPolicyProvider extends Policy implements DynamicPolicy {
-
+public class DynamicPolicyProvider extends Policy implements RevokeablePolicy {
+
+ //Java 1.4 compatible
+ @SuppressWarnings("unchecked")
+ private synchronized static RevokeableDynamicPolicySpi getDynamicPolicy()
+ throws AccessControlException {
+ RevokeableDynamicPolicySpi rdps;
+ rdps = (RevokeableDynamicPolicySpi) AccessController.doPrivileged(
+ new PrivilegedAction(){
+ public Object run(){
+ Iterator sp = Service.providers(RevokeableDynamicPolicySpi.class);
+ while (sp.hasNext()){
+ RevokeableDynamicPolicySpi inst =
+ (RevokeableDynamicPolicySpi) sp.next();
+ if (inst != null){
+ return inst;
+ }
+ }
+ return null;
+ }
+ });
+ return rdps;
+ }
+
+ private static final Logger logger =
+ Logger.getLogger("net.jini.security.policy");
+
+ /* If true, always grant permission */
+ @SuppressWarnings("unchecked")
+ private static volatile boolean grantAll =
+ ((Boolean) AccessController.doPrivileged(
+ new PrivilegedAction() {
+ public Object run() {
+ return Boolean.valueOf(
+ Security.getProperty(
+ "net.jini.security.policy.grantAllandLog"));
+ }
+ })).booleanValue();
+
private static final String basePolicyClassProperty =
"net.jini.security.policy." +
"DynamicPolicyProvider.basePolicyClass";
- private static final String defaultBasePolicyClass =
- "net.jini.security.policy.PolicyFileProvider";
- private static final ProtectionDomain sysDomain = (ProtectionDomain)
- AccessController.doPrivileged(new PrivilegedAction() {
- public Object run() { return Object.class.getProtectionDomain(); }
- });
-
- private final Policy basePolicy;
- private final boolean cacheBasePerms;
- private final WeakIdentityMap domainPerms = new WeakIdentityMap();
- private final WeakIdentityMap loaderGrants = new WeakIdentityMap();
- private final Grants globalGrants = new Grants();
-
- /**
+
+ private static Policy getBasePolicy() throws PolicyInitializationException {
+ String cname = "net.jini.security.policy.PolicyFileProvider";
+ Policy basePolicy = null;
+ try {
+ String bpc = Security.getProperty(basePolicyClassProperty);
+ if (bpc != null) cname = bpc;
+ basePolicy = (Policy) Class.forName(cname).newInstance();
+ } catch (InstantiationException ex) {
+ if (logger.isLoggable(Level.SEVERE)){
+ logger.log(Level.SEVERE, null, ex);
+ }
+ throw new PolicyInitializationException(
+ "Unable to create a new instance of: " +
+ cname, ex);
+ } catch (IllegalAccessException ex) {
+ if (logger.isLoggable(Level.SEVERE)){
+ logger.logp(Level.SEVERE, "DynamicPolicyProviderImpl",
+ "initialize()", "Unable to create a new instance of: " +
+ cname, ex);
+ }
+ throw new PolicyInitializationException(
+ "Unable to create a new instance of: " +
+ cname, ex);
+ } catch (ClassNotFoundException ex) {
+ if (logger.isLoggable(Level.SEVERE)){
+ logger.log(Level.SEVERE, "Check " + cname + " is accessable" +
+ "from your classpath", ex);
+ }
+ throw new PolicyInitializationException(
+ "Unable to create a new instance of: " +
+ cname, ex);
+ } catch (SecurityException ex) {
+ if (logger.isLoggable(Level.SEVERE)){
+ logger.log(Level.SEVERE,
+ "You don't have sufficient permissions to create" +
+ "a new instance of" + cname, ex);
+ }
+ throw new PolicyInitializationException(
+ "Unable to create a new instance of: " +
+ cname, ex);
+ }
+ return basePolicy;
+ }
+
+ // Try using an enum here for optional loading? Or just get whatever is available?
+ private RevokeableDynamicPolicySpi instance; //= getDynamicPolicy();
+
+
+ // The original implementation wraps a base Policy we still need that
+ // don't return until the instance has been created properly.
+ // This is undesireable for revokeable permissions.
+ /**
* Creates a new <code>DynamicPolicyProvider</code> instance that wraps a
* default underlying policy. The underlying policy is created as follows:
* if the
@@ -93,7 +151,7 @@ public class DynamicPolicyProvider exten
* Note that this constructor requires the appropriate
* <code>"getProperty"</code> {@link java.security.SecurityPermission} to
* read the
- * <code>net.jini.security.policy.DynamicPolicyProvider.basePolicyClass</code>
+ * <code>net.jini.security.policy.DynamicPolicyProviderImpl.basePolicyClass</code>
* security property, and may require <code>"accessClassInPackage.*"</code>
* {@link RuntimePermission}s, depending on the package of the base policy
* class.
@@ -102,28 +160,15 @@ public class DynamicPolicyProvider exten
* policy
* @throws SecurityException if there is a security manager and the
* calling context does not have adequate permissions to read the
- * <code>net.jini.security.policy.DynamicPolicyProvider.basePolicyClass</code>
+ * <code>net.jini.security.policy.DynamicPolicyProviderImpl.basePolicyClass</code>
* security property, or if the calling context does not have
* adequate permissions to access the base policy class
*/
public DynamicPolicyProvider() throws PolicyInitializationException {
- String cname = Security.getProperty(basePolicyClassProperty);
- if (cname == null) {
- cname = defaultBasePolicyClass;
- }
- try {
- basePolicy = (Policy) Class.forName(cname).newInstance();
- } catch (SecurityException e) {
- throw e;
- } catch (Exception e) {
- throw new PolicyInitializationException(
- "unable to construct base policy", e);
- }
- cacheBasePerms = !(basePolicy instanceof DynamicPolicy);
- ensureDependenciesResolved();
+ this(getBasePolicy());
}
-
- /**
+
+ /**
* Creates a new <code>DynamicPolicyProvider</code> instance that wraps
* around the given non-<code>null</code> base policy object.
*
@@ -133,446 +178,135 @@ public class DynamicPolicyProvider exten
* <code>null</code>
*/
public DynamicPolicyProvider(Policy basePolicy) {
- if (basePolicy == null) {
- throw new NullPointerException();
- }
- this.basePolicy = basePolicy;
- cacheBasePerms = !(basePolicy instanceof DynamicPolicy);
- ensureDependenciesResolved();
- }
-
- /**
- * Behaves as specified by {@link Policy#getPermissions(CodeSource)}.
- */
- public PermissionCollection getPermissions(CodeSource source) {
- PermissionCollection pc = basePolicy.getPermissions(source);
- Permission[] pa = globalGrants.get(null);
- for (int i = 0; i < pa.length; i++) {
- Permission p = pa[i];
- if (!pc.implies(p)) {
- pc.add(p);
- }
- }
- return pc;
- }
-
- /**
- * Behaves as specified by {@link Policy#getPermissions(ProtectionDomain)}.
- */
- public PermissionCollection getPermissions(ProtectionDomain domain) {
- return getDomainPermissions(domain).getPermissions(domain);
- }
-
- /**
- * Behaves as specified by {@link Policy#implies}.
- */
- public boolean implies(ProtectionDomain domain, Permission permission) {
- return getDomainPermissions(domain).implies(permission, domain);
- }
-
- /**
- * Behaves as specified by {@link Policy#refresh}.
- */
- public void refresh() {
- basePolicy.refresh();
- if (cacheBasePerms) {
- synchronized (domainPerms) {
- domainPerms.clear();
- }
- }
+ if (basePolicy == null) {
+ throw new NullPointerException();
+ }
+ try {
+ instance = getDynamicPolicy();
+ } catch (AccessControlException ex) {
+ if (logger.isLoggable(Level.CONFIG)) {
+ logger.logp(Level.CONFIG,
+ DynamicPolicyProvider.class.toString(),
+ "DynamicPolicyProvider(Policy basePolicy) constructor",
+ "If you see this message, it means that you need to grant" +
+ "the java.lang.RuntimePermission accessClassInPackage.sun.misc" +
+ "in your Policy file in order to take advantage of " +
+ "the RevokeableDynamicPolicyProviderSpi", ex);
+ }
+ }
+ if (instance == null) {
+ instance = new DynamicPolicyProviderImpl();
+ }
+ try {
+ instance.basePolicy(basePolicy);
+ instance.initialize();
+ } catch (PolicyInitializationException ex) {
+ logger.log(Level.SEVERE, "This should never happen, since" +
+ "basePolicy is not null", ex);
+ }
+ instance.ensureDependenciesResolved();
}
- // documentation inherited from DynamicPolicy.grantSupported
public boolean grantSupported() {
- return true;
+ return instance.grantSupported();
}
- // documentation inherited from DynamicPolicy.grant
- public void grant(Class cl,
- Principal[] principals,
- Permission[] permissions)
- {
- if (cl != null) {
- checkDomain(cl);
- }
- if (principals != null && principals.length > 0) {
- principals = (Principal[]) principals.clone();
- checkNullElements(principals);
- }
- if (permissions == null || permissions.length == 0) {
- return;
- }
- permissions = (Permission[]) permissions.clone();
- checkNullElements(permissions);
-
- SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- sm.checkPermission(new GrantPermission(permissions));
- }
-
- Grants g = (cl != null) ?
- getLoaderGrants(getClassLoader(cl)) : globalGrants;
- g.add(principals, permissions);
+ public void grant(Class cl, Principal[] principals, Permission[] permissions) {
+ instance.grant(cl, principals, permissions);
}
- // documentation inherited from DynamicPolicy.getGrants
public Permission[] getGrants(Class cl, Principal[] principals) {
- if (cl != null) {
- checkDomain(cl);
- }
- if (principals != null && principals.length > 0) {
- principals = (Principal[]) principals.clone();
- checkNullElements(principals);
- }
-
- List l = Arrays.asList(globalGrants.get(principals));
- if (cl != null) {
- l = new ArrayList(l);
- l.addAll(Arrays.asList(
- getLoaderGrants(getClassLoader(cl)).get(principals)));
- }
- PermissionCollection pc = new Permissions();
- for (Iterator i = l.iterator(); i.hasNext(); ) {
- Permission p = (Permission) i.next();
- if (!pc.implies(p)) {
- pc.add(p);
- }
- }
- l = Collections.list(pc.elements());
- return (Permission[]) l.toArray(new Permission[l.size()]);
+ return instance.getGrants(cl, principals);
}
- /**
- * Ensures that any classes depended on by this policy provider are
- * resolved. This is to preclude lazy resolution of such classes during
- * operation of the provider, which can result in deadlock as described by
- * bug 4911907.
- */
- private void ensureDependenciesResolved() {
- // force class resolution by pre-invoking method called by implies()
- getDomainPermissions(sysDomain);
- }
-
- private DomainPermissions getDomainPermissions(ProtectionDomain pd) {
- DomainPermissions dp;
- synchronized (domainPerms) {
- dp = (DomainPermissions) domainPerms.get(pd);
- }
- if (dp == null) {
- dp = new DomainPermissions(pd);
- globalGrants.register(dp);
- if (pd != null) {
- getLoaderGrants(pd.getClassLoader()).register(dp);
- }
- synchronized (domainPerms) {
- domainPerms.put(pd, dp);
- }
- }
- return dp;
- }
-
- private Grants getLoaderGrants(ClassLoader ldr) {
- synchronized (loaderGrants) {
- Grants g = (Grants) loaderGrants.get(ldr);
- if (g == null) {
- loaderGrants.put(ldr, g = new Grants());
- }
- return g;
- }
+ public void revoke(Class cl, Principal[] principals, Permission[] permissions) {
+ instance.revoke(cl, principals, permissions);
}
-
- private static ClassLoader getClassLoader(final Class cl) {
- return (ClassLoader) AccessController.doPrivileged(
- new PrivilegedAction() {
- public Object run() { return cl.getClassLoader(); }
- });
- }
-
- private static void checkDomain(final Class cl) {
- ProtectionDomain pd = (ProtectionDomain) AccessController.doPrivileged(
- new PrivilegedAction() {
- public Object run() { return cl.getProtectionDomain(); }
- });
- if (pd != sysDomain && pd.getClassLoader() == null) {
- throw new UnsupportedOperationException(
- "ungrantable protection domain");
- }
+
+ @Override
+ public PermissionCollection getPermissions(CodeSource codesource) {
+ return instance.getPermissions(codesource);
}
-
- private static void checkNullElements(Object[] array) {
- for (int i = 0; i < array.length; i++) {
- if (array[i] == null) {
- throw new NullPointerException();
- }
- }
- }
-
+
/**
- * Class which holds permissions and principals of a ProtectionDomain. The
- * domainPerms map associates ProtectionDomain instances to instances of
- * this class.
+ * Return a PermissionCollection object containing the set of
+ * permissions granted to the specified ProtectionDomain.
+ *
+ * <p> Applications are discouraged from calling this method
+ * since this operation may not be supported by all policy implementations.
+ * Applications should rely on the <code>implies</code> method
+ * to perform policy checks.
+ *
+ * <p> The default implementation of this method first retrieves
+ * the permissions returned via <code>getPermissions(CodeSource)</code>
+ * (the CodeSource is taken from the specified ProtectionDomain),
+ * as well as the permissions located inside the specified ProtectionDomain.
+ * All of these permissions are then combined and returned in a new
+ * PermissionCollection object. If <code>getPermissions(CodeSource)</code>
+ * returns Policy.UNSUPPORTED_EMPTY_COLLECTION, then this method
+ * returns the permissions contained inside the specified ProtectionDomain
+ * in a new PermissionCollection object.
+ *
+ * <p> This method can be overridden if the policy implementation
+ * supports returning a set of permissions granted to a ProtectionDomain.
+ *
+ * @param domain the ProtectionDomain to which the returned
+ * PermissionCollection has been granted.
+ *
+ * @return a set of permissions granted to the specified ProtectionDomain.
+ * If this operation is supported, the returned
+ * set of permissions must be a new mutable instance
+ * and it must support heterogeneous Permission types.
+ * If this operation is not supported,
+ * Policy.UNSUPPORTED_EMPTY_COLLECTION is returned.
+ *
+ * @since 1.4
*/
- private class DomainPermissions {
-
- private final Set principals;
- private final PermissionCollection perms;
- private final List grants = new ArrayList();
-
- DomainPermissions(ProtectionDomain pd) {
- Principal[] pra;
- principals = (pd != null && (pra = pd.getPrincipals()).length > 0)
- ? new HashSet(Arrays.asList(pra)) : Collections.EMPTY_SET;
- perms = cacheBasePerms ? basePolicy.getPermissions(pd) : null;
- }
-
- Set getPrincipals() {
- return principals;
- }
-
- synchronized void add(Permission[] pa) {
- for (int i = 0; i < pa.length; i++) {
- Permission p = pa[i];
- grants.add(p);
- if (perms != null) {
- perms.add(p);
- }
- }
- }
-
- synchronized PermissionCollection getPermissions(ProtectionDomain d) {
- return getPermissions(true, d);
- }
-
- synchronized boolean implies(Permission p, ProtectionDomain domain) {
- if (perms != null) {
- return perms.implies(p);
- }
- if (basePolicy.implies(domain, p)) {
- return true;
- }
- if (grants.isEmpty()) {
- return false;
- }
- return getPermissions(false, domain).implies(p);
- }
-
- private PermissionCollection getPermissions(boolean compact,
- ProtectionDomain domain)
- {
- // base policy permission collection may not be enumerable
- assert Thread.holdsLock(this);
- PermissionCollection pc = basePolicy.getPermissions(domain);
- for (Iterator i = grants.iterator(); i.hasNext(); ) {
- Permission p = (Permission) i.next();
- if (!(compact && pc.implies(p))) {
- pc.add(p);
- }
- }
- return pc;
- }
+ @Override
+ public PermissionCollection getPermissions(ProtectionDomain domain) {
+ return instance.getPermissions(domain);
}
-
+
/**
- * Class which tracks dynamic permission grants.
+ * Evaluates the global policy for the permissions granted to
+ * the ProtectionDomain and tests whether the permission is
+ * granted.
+ *
+ * @param domain the ProtectionDomain to test
+ * @param permission the Permission object to be tested for implication.
+ *
+ * @return true if "permission" is a proper subset of a permission
+ * granted to this ProtectionDomain.
+ *
+ * @see java.security.ProtectionDomain
+ * @since 1.4
*/
- private static class Grants {
-
- private final Map principalGrants = new HashMap();
- private final WeakGroup scope;
-
- Grants() {
- PrincipalGrants pg = new PrincipalGrants();
- principalGrants.put(Collections.EMPTY_SET, pg);
- scope = pg.scope;
- }
-
- synchronized void add(Principal[] pra, Permission[] pa) {
- Set prs = (pra != null && pra.length > 0) ?
- new HashSet(Arrays.asList(pra)) : Collections.EMPTY_SET;
-
- PrincipalGrants pg = (PrincipalGrants) principalGrants.get(prs);
- if (pg == null) {
- pg = new PrincipalGrants();
- for (Iterator i = scope.iterator(); i.hasNext();) {
- DomainPermissions dp = (DomainPermissions) i.next();
- if (containsAll(dp.getPrincipals(), prs)) {
- pg.scope.add(dp);
- }
- }
- principalGrants.put(prs, pg);
- }
-
- ArrayList l = new ArrayList();
- for (int i = 0; i < pa.length; i++) {
- Permission p = pa[i];
- if (!pg.perms.implies(p)) {
- pg.perms.add(p);
- l.add(p);
- }
- }
-
- if (l.size() > 0) {
- pa = (Permission[]) l.toArray(new Permission[l.size()]);
- for (Iterator i = pg.scope.iterator(); i.hasNext();) {
- ((DomainPermissions) i.next()).add(pa);
- }
- }
- }
-
- synchronized Permission[] get(Principal[] pra) {
- Set prs = (pra != null && pra.length > 0) ?
- new HashSet(Arrays.asList(pra)) : Collections.EMPTY_SET;
- List l = new ArrayList();
-
- for (Iterator i = principalGrants.entrySet().iterator();
- i.hasNext(); )
- {
- Map.Entry me = (Map.Entry) i.next();
- if (containsAll(prs, (Set) me.getKey())) {
- PrincipalGrants pg = (PrincipalGrants) me.getValue();
- l.addAll(Collections.list(pg.perms.elements()));
- }
- }
- return (Permission[]) l.toArray(new Permission[l.size()]);
- }
-
- synchronized void register(DomainPermissions dp) {
- Set prs = dp.getPrincipals();
- for (Iterator i = principalGrants.entrySet().iterator();
- i.hasNext(); )
- {
- Map.Entry me = (Map.Entry) i.next();
- if (containsAll(prs, (Set) me.getKey())) {
- PrincipalGrants pg = (PrincipalGrants) me.getValue();
- pg.scope.add(dp);
- List l = Collections.list(pg.perms.elements());
- dp.add((Permission[]) l.toArray(new Permission[l.size()]));
- }
- }
- }
-
- private static boolean containsAll(Set s1, Set s2) {
- return (s1.size() >= s2.size()) && s1.containsAll(s2);
- }
-
- private static class PrincipalGrants {
- final WeakGroup scope = new WeakGroup();
- final PermissionCollection perms = new Permissions();
- PrincipalGrants() {}
- }
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ if (grantAll == false) {
+ return instance.implies(domain, permission);
+ }
+ boolean result = instance.implies(domain, permission);
+ if (result == false){
+ logger.logp(Level.INFO, "instance.getClass().getName()",
+ "implies(ProtectionDomain domain, Permission permission",
+ "domain.toString(), permission.toString() returned false");
+ return true;
+ }
+ return result;
+ }
+
+ @Override
+ public void refresh() {
+ instance.refresh();
}
- /**
- * Grouping of non-null, weakly-referenced objects. The structure is a
- * doubly linked list. The resulting structure is not thread safe and
- * must be synchronized externally.
- */
- private static class WeakGroup {
- private final ReferenceQueue rq = new ReferenceQueue();
- private final Node head;
- private final Node tail;
-
- WeakGroup() {
- head = Node.createEmptyList();
- tail = head.getNext();
- }
-
- void add(Object obj) {
- if (obj == null) {
- throw new NullPointerException();
- }
- processQueue();
- Node newNode = new Node(obj, rq);
- newNode.insertAfter(head);
- }
-
- Iterator iterator() {
- processQueue();
- return new Iterator() {
- private Node curNode = head.getNext();
- private Object nextObj = getNext();
-
- public Object next() {
- if (nextObj == null) {
- throw new NoSuchElementException();
- }
- Object obj = nextObj;
- nextObj = getNext();
- return obj;
- }
-
- public boolean hasNext() {
- return nextObj != null;
- }
-
- public void remove() {
- throw new UnsupportedOperationException();
- }
-
- private Object getNext() {
- while (curNode != tail) {
- Object obj = curNode.get();
- if (obj != null) {
- curNode = curNode.getNext();
- return obj;
- } else {
- curNode.enqueue();
- curNode = curNode.getNext();
- }
- }
- return null;
- }
- };
- }
-
- private void processQueue() {
- Node n;
- while ((n = (Node) rq.poll()) != null) {
- n.remove();
- }
- }
-
- private static class Node extends WeakReference {
- private Node next;
- private Node prev;
-
- static Node createEmptyList() {
- Node head = new Node(null);
- Node tail = new Node(null);
- head.next = tail;
- tail.prev = head;
- return head;
- }
-
- // Constructor for initialization of head and tail nodes which
- // should never be enqueued.
- private Node(Object obj) {
- super(obj);
- }
+ public boolean revokeSupported() {
+ return instance.revokeSupported();
+ }
- Node(Object obj, ReferenceQueue rq) {
- super(obj, rq);
- }
-
- /**
- * Inserts this node between <code>pred</code> and its successor
- */
- void insertAfter(Node pred) {
- Node succ = pred.next;
- next = succ;
- prev = pred;
- pred.next = this;
- succ.prev = this;
- }
-
- void remove() {
- prev.next = next;
- next.prev = prev;
- }
-
- Node getNext() {
- return next;
- }
- }
+ public Object parameters() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException("Not supported yet.");
}
+
}
Copied: incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProviderImpl.java (from r921640, incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProvider.java)
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProviderImpl.java?p2=incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProviderImpl.java&p1=incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProvider.java&r1=921640&r2=928394&rev=928394&view=diff
==============================================================================
--- incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProvider.java (original)
+++ incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProviderImpl.java Sun Mar 28 12:57:03 2010
@@ -41,7 +41,10 @@ import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import net.jini.security.GrantPermission;
+import org.apache.river.security.policy.spi.RevokeableDynamicPolicySpi;
/**
* Security policy provider that supports dynamic granting of permissions at
@@ -57,93 +60,71 @@ import net.jini.security.GrantPermission
*
* @since 2.0
*/
-public class DynamicPolicyProvider extends Policy implements DynamicPolicy {
+class DynamicPolicyProviderImpl extends Policy implements RevokeableDynamicPolicySpi {
- private static final String basePolicyClassProperty =
- "net.jini.security.policy." +
- "DynamicPolicyProvider.basePolicyClass";
- private static final String defaultBasePolicyClass =
- "net.jini.security.policy.PolicyFileProvider";
private static final ProtectionDomain sysDomain = (ProtectionDomain)
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() { return Object.class.getProtectionDomain(); }
});
- private final Policy basePolicy;
- private final boolean cacheBasePerms;
- private final WeakIdentityMap domainPerms = new WeakIdentityMap();
- private final WeakIdentityMap loaderGrants = new WeakIdentityMap();
- private final Grants globalGrants = new Grants();
-
- /**
- * Creates a new <code>DynamicPolicyProvider</code> instance that wraps a
- * default underlying policy. The underlying policy is created as follows:
- * if the
- * <code>net.jini.security.policy.DynamicPolicyProvider.basePolicyClass</code>
- * security property is set, then its value is interpreted as the class
- * name of the base (underlying) policy provider; otherwise, a default
- * class name of
- * <code>"net.jini.security.policy.PolicyFileProvider"</code>
- * is used. The base policy is then instantiated using the no-arg public
- * constructor of the named class. If the base policy class is not found,
- * is not instantiable via a public no-arg constructor, or if invocation of
- * its constructor fails, then a <code>PolicyInitializationException</code>
- * is thrown.
- * <p>
- * Note that this constructor requires the appropriate
- * <code>"getProperty"</code> {@link java.security.SecurityPermission} to
- * read the
- * <code>net.jini.security.policy.DynamicPolicyProvider.basePolicyClass</code>
- * security property, and may require <code>"accessClassInPackage.*"</code>
- * {@link RuntimePermission}s, depending on the package of the base policy
- * class.
- *
- * @throws PolicyInitializationException if unable to construct the base
- * policy
- * @throws SecurityException if there is a security manager and the
- * calling context does not have adequate permissions to read the
- * <code>net.jini.security.policy.DynamicPolicyProvider.basePolicyClass</code>
- * security property, or if the calling context does not have
- * adequate permissions to access the base policy class
- */
- public DynamicPolicyProvider() throws PolicyInitializationException {
- String cname = Security.getProperty(basePolicyClassProperty);
- if (cname == null) {
- cname = defaultBasePolicyClass;
- }
- try {
- basePolicy = (Policy) Class.forName(cname).newInstance();
- } catch (SecurityException e) {
- throw e;
- } catch (Exception e) {
- throw new PolicyInitializationException(
- "unable to construct base policy", e);
- }
- cacheBasePerms = !(basePolicy instanceof DynamicPolicy);
- ensureDependenciesResolved();
- }
-
- /**
- * Creates a new <code>DynamicPolicyProvider</code> instance that wraps
- * around the given non-<code>null</code> base policy object.
- *
- * @param basePolicy base policy object containing information about
- * non-dynamic grants
- * @throws NullPointerException if <code>basePolicy</code> is
- * <code>null</code>
- */
- public DynamicPolicyProvider(Policy basePolicy) {
- if (basePolicy == null) {
- throw new NullPointerException();
- }
- this.basePolicy = basePolicy;
- cacheBasePerms = !(basePolicy instanceof DynamicPolicy);
- ensureDependenciesResolved();
+ private volatile Policy basePolicy;
+ private volatile boolean cacheBasePerms;
+ private volatile boolean initialized;
+ // REMIND: do something with WeakIdentityMap and Concurrency, note
+ // this means different implementation methods not a drop in.
+ private final Map domainPerms;
+ private final Map loaderGrants;
+ private final Grants globalGrants;
+ private static final Logger logger = Logger.getLogger(
+ "net.jini.security.policy.DynamicPolicyProviderImpl");
+
+ /**
+ * A new uninitialized instance.
+ */
+ DynamicPolicyProviderImpl() {
+ domainPerms = new WeakIdentityMap();
+ loaderGrants = new WeakIdentityMap();
+ globalGrants = new Grants();
+ basePolicy = null;
+ cacheBasePerms = false;
+ initialized = false;
+ }
+
+ /**
+ * This method is only called once, on an uninitialized instance
+ * further attempts will fail.
+ * @param basePolicy
+ * @return success
+ */
+ public boolean basePolicy(Policy basePolicy){
+ if (this.basePolicy == null){
+ this.basePolicy = basePolicy;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This method completes construction of the Implementation, considered
+ * safe since it is called through the Service Provider Interface
+ * and cannot be accessed otherwise.
+ * @throws net.jini.security.policy.PolicyInitializationException
+ * @throws java.lang.InstantiationException
+ */
+ public void initialize() throws PolicyInitializationException {
+ if (initialized == true) {
+ return;
+ }
+ if (basePolicy == null) throw new PolicyInitializationException("Base Policy hasn't " +
+ "been set cannot initialize", new Exception("Failed to initialize"));
+ cacheBasePerms = !(basePolicy instanceof DynamicPolicy);
+ initialized = true;
}
/**
* Behaves as specified by {@link Policy#getPermissions(CodeSource)}.
*/
+ @Override
public PermissionCollection getPermissions(CodeSource source) {
PermissionCollection pc = basePolicy.getPermissions(source);
Permission[] pa = globalGrants.get(null);
@@ -159,6 +140,7 @@ public class DynamicPolicyProvider exten
/**
* Behaves as specified by {@link Policy#getPermissions(ProtectionDomain)}.
*/
+ @Override
public PermissionCollection getPermissions(ProtectionDomain domain) {
return getDomainPermissions(domain).getPermissions(domain);
}
@@ -248,7 +230,7 @@ public class DynamicPolicyProvider exten
* operation of the provider, which can result in deadlock as described by
* bug 4911907.
*/
- private void ensureDependenciesResolved() {
+ public void ensureDependenciesResolved() {
// force class resolution by pre-invoking method called by implies()
getDomainPermissions(sysDomain);
}
@@ -344,6 +326,8 @@ public class DynamicPolicyProvider exten
}
synchronized boolean implies(Permission p, ProtectionDomain domain) {
+// System.out.println("Permission: " + p.toString() +
+// " ProtectionDomain: " + domain.toString());
if (perms != null) {
return perms.implies(p);
}
@@ -575,4 +559,16 @@ public class DynamicPolicyProvider exten
}
}
}
+
+ public void revoke(Class cl, Principal[] principals, Permission[] permissions) {
+ throw new UnsupportedOperationException("Revoke not supported.");
+ }
+
+ public boolean revokeSupported() {
+ return false;
+ }
+
+ public Object parameters() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
}
Modified: incubator/river/jtsk/trunk/src/net/jini/security/policy/PolicyFileProvider.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/net/jini/security/policy/PolicyFileProvider.java?rev=928394&r1=928393&r2=928394&view=diff
==============================================================================
--- incubator/river/jtsk/trunk/src/net/jini/security/policy/PolicyFileProvider.java (original)
+++ incubator/river/jtsk/trunk/src/net/jini/security/policy/PolicyFileProvider.java Sun Mar 28 12:57:03 2010
@@ -104,8 +104,14 @@ public class PolicyFileProvider extends
*/
public PolicyFileProvider() throws PolicyInitializationException {
policyFile = null;
-
- String cname = Security.getProperty(basePolicyClassProperty);
+ String cname = Security.getProperty(basePolicyClassProperty);
+// @SuppressWarnings("unchecked")
+// String cname = (String) AccessController.doPrivileged(
+// new PrivilegedAction() {
+// public Object run() {
+// return System.getProperty(basePolicyClassProperty);
+// }
+// });
if (cname == null) {
cname = defaultBasePolicyClass;
}
Modified: incubator/river/jtsk/trunk/src/net/jini/security/proxytrust/BasicUntrustedObjectSecurityContext.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/net/jini/security/proxytrust/BasicUntrustedObjectSecurityContext.java?rev=928394&r1=928393&r2=928394&view=diff
==============================================================================
--- incubator/river/jtsk/trunk/src/net/jini/security/proxytrust/BasicUntrustedObjectSecurityContext.java (original)
+++ incubator/river/jtsk/trunk/src/net/jini/security/proxytrust/BasicUntrustedObjectSecurityContext.java Sun Mar 28 12:57:03 2010
@@ -62,6 +62,15 @@ public final class BasicUntrustedObjectS
* neither modified nor retained; subsequent changes to that argument
* have no effect on the instance created.
*
+ * If additional permissions are passed in these will be added to a new
+ * ProtectionDomain and set readonly, additional Permission's may be
+ * granted by a Policy, however any permissions granted at construction
+ * time will always be valid and cannot be revoked.
+ *
+ * If permissions is null or empty then all BasicUntrustedObjectSecurityContext
+ * instances will share the same ProtectionDomain with all other instances
+ * without additional permissions.
+ *
* @param permissions additional permissions to allow, or
* <code>null</code> if no additional permissions should be allowed
* @throws NullPointerException if any element of the argument is
Added: incubator/river/jtsk/trunk/src/org/apache/river/security/RevokePermission.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/RevokePermission.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/RevokePermission.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/RevokePermission.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,59 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.apache.river.security;
+
+import java.security.Permission;
+
+/**
+ * RevokePermission allows for a permission to be granted at runtime or
+ * revoked. A Thread invoking this Permission must have the permission that
+ * is to be granted. A RevokePermission cannot grant itself a permission
+ * it doesn't already have.
+ *
+ * It should cache all revokes, such that a refresh operation, doesn't add
+ * any revoked permissions. I'm not sure about grant's though, should they be
+ * refreshed and require re granting if they didn't exist in the configuration
+ * -- seems logical.
+ *
+ *
+ * @author peter
+ */
+public class RevokePermission extends Permission {
+ private static final long serialVersionUID = 1L;
+ private final String actions;
+
+ public RevokePermission(String name){
+ super(name);
+ actions = "";
+ }
+
+ public RevokePermission(String name, String actions){
+ super(name);
+ this.actions = actions;
+ }
+
+ @Override
+ public boolean implies(Permission permission) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public int hashCode() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public String getActions() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+
+}
Propchange: incubator/river/jtsk/trunk/src/org/apache/river/security/RevokePermission.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/river/jtsk/trunk/src/org/apache/river/security/RevokeablePermissionCollection.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/RevokeablePermissionCollection.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/RevokeablePermissionCollection.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/RevokeablePermissionCollection.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,89 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.apache.river.security;
+
+import java.security.Permission;
+import java.util.Enumeration;
+
+/**
+ *
+ * @author Peter Firmstone
+ * @see PermissionCollection
+ * @see Permission
+ */
+public interface RevokeablePermissionCollection {
+ /**
+ * This method is slow and to be used only when correctness is perferred
+ * over performance.
+ *
+ * In fact revokeAll() followed by add() is the preferred method.
+ *
+ * However when the new Permission set depends upon the old and there is
+ * a possibility that the old set may be updated with an add during processing
+ * this method will fail and as such not loose a particular required permission.
+ *
+ * Partial success may occur in which case some of the permissions will be
+ * revoked and others not. However only a subset of those permissions that
+ * are requested to be revoked, shall be. This should not be a problem as
+ * the required permissions will still exist in the set and a simple retry
+ * should suffice if the return was 0.
+ *
+ * Attempt to revoke a Permission, if an intervening write occurs an
+ * integer value of 0 is returned, the suggested strategy is to try again,
+ * however if several attempts are likely to fail use revokeAll instead.
+ *
+ * A return value of -1 indicates that the Permission cannot be revoked
+ * from the collection. In this case use revokeAll. It may be possible
+ * to correct this condition by utilising a finer grained permission set.
+ *
+ * A return value of 1 indicates success.
+ *
+ * @param permissions
+ * @return result, 0 for intervening write, -1 failed not possible, 1 for true
+ */
+ public int revoke(Permission ... permissions);
+
+ /**
+ * Due to the overlapping nature of Permission's, attempts to revoke
+ * a permission may fail, in wich case it is best to remove all
+ * permissions related by class and later add the required Permissions.
+ * This method should always succeed.
+ *
+ * This method should only revoke Permission's related to an individual
+ * class type.
+ *
+ * @param permission
+ */
+ public void revokeAll(Permission permission);
+
+ /**
+ * @see PermissionCollection.add(Permission permission)
+ * @param permission
+ */
+ public void add(Permission permission);
+ /**
+ * @see PermissionCollection.elements()
+ * @return
+ */
+ public Enumeration<Permission> elements();
+ /**
+ * @see PermissionCollection.implies()
+ * @param permission
+ * @return
+ */
+ public boolean implies(Permission permission);
+ /**
+ * @see PermissionCollection.isReadOnly()
+ * @return true or false
+ */
+ public boolean isReadOnly();
+ /**
+ * @see PermissionCollection.setReadOnly()
+ */
+ public void setReadOnly();
+ @Override
+ public String toString();
+}
Propchange: incubator/river/jtsk/trunk/src/org/apache/river/security/RevokeablePermissionCollection.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentDynamicPolicyProvider.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentDynamicPolicyProvider.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentDynamicPolicyProvider.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentDynamicPolicyProvider.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,440 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.river.security.concurrent;
+
+import net.jini.security.policy.*;
+import org.apache.river.util.concurrent.WeakIdentityMap;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Principal;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.security.Security;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import net.jini.security.GrantPermission;
+import org.apache.river.security.policy.spi.RevokeableDynamicPolicySpi;
+
+/**
+ * Security policy provider that supports dynamic granting of permissions at
+ * run-time. This provider is designed as a wrapper to layer dynamic grant
+ * functionality on top of an underlying policy provider. If the underlying
+ * provider does not implement the {@link DynamicPolicy} interface, then its
+ * permission mappings are assumed to change only when its {@link
+ * Policy#refresh refresh} method is called. Permissions are granted on the
+ * granularity of class loader; granting a permission requires (of the calling
+ * context) {@link GrantPermission} for that permission.
+ *
+ * @author Sun Microsystems, Inc.
+ *
+ * @since 2.0
+ */
+class ConcurrentDynamicPolicyProvider extends Policy implements RevokeableDynamicPolicySpi {
+
+ private static final String basePolicyClassProperty =
+ "net.jini.security.policy." +
+ "DynamicPolicyProvider.basePolicyClass";
+ private static final String defaultBasePolicyClass =
+ "org.apache.river.security.concurrent.ConcurrentPolicyFile";
+ @SuppressWarnings("unchecked")
+ private static final ProtectionDomain sysDomain = (ProtectionDomain) AccessController.doPrivileged(new PrivilegedAction() {
+
+ public Object run() {
+ return Object.class.getProtectionDomain();
+ }
+ });
+ private volatile Policy basePolicy;
+ private volatile boolean cacheBasePerms;
+ private volatile boolean initialized;
+ // REMIND: do something with WeakIdentityMap and Concurrency, note
+ // this means different implementation methods not a drop in.
+ private final ConcurrentMap<ProtectionDomain, DomainPermissions> domainPerms;
+ private final ConcurrentMap<ClassLoader, Grants> loaderGrants;
+ private final Grants globalGrants;
+ private static final Logger logger = Logger.getLogger(
+ "net.jini.security.policy.DynamicPolicyProviderImpl");
+
+ /**
+ * A new uninitialized instance.
+ */
+ ConcurrentDynamicPolicyProvider() {
+ domainPerms = new WeakIdentityMap<ProtectionDomain, DomainPermissions>();
+ loaderGrants = new WeakIdentityMap<ClassLoader, Grants>();
+ globalGrants = new Grants();
+ basePolicy = null;
+ cacheBasePerms = false;
+ initialized = false;
+ }
+
+ /**
+ * This method is only called once, on an uninitialized instance
+ * further attempts will fail.
+ * @param basePolicy
+ * @return success
+ */
+ public boolean basePolicy(Policy basePolicy) {
+ if (this.basePolicy == null) {
+ this.basePolicy = basePolicy;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This method completes construction of the Implementation, considered
+ * safe since it is called through the Service and cannot be accessed
+ * otherwise.
+ * @throws net.jini.security.policy.PolicyInitializationException
+ * @throws java.lang.InstantiationException
+ */
+ public void initialize() throws PolicyInitializationException {
+ if (initialized == true) {
+ return;
+ }
+ if (basePolicy == null) {
+ String cname = Security.getProperty(basePolicyClassProperty);
+ if (cname == null) {
+ cname = defaultBasePolicyClass;
+ }
+ try {
+ basePolicy = (Policy) Class.forName(cname).newInstance();
+ } catch (InstantiationException ex) {
+ if (logger.isLoggable(Level.SEVERE)) {
+ logger.log(Level.SEVERE, null, ex);
+ }
+ throw new PolicyInitializationException(
+ "Unable to create a new instance of: " +
+ cname, ex);
+ } catch (IllegalAccessException ex) {
+ if (logger.isLoggable(Level.SEVERE)) {
+ logger.logp(Level.SEVERE, "DynamicPolicyProviderImpl",
+ "initialize()", "Unable to create a new instance of: " +
+ cname, ex);
+ }
+ throw new PolicyInitializationException(
+ "Unable to create a new instance of: " +
+ cname, ex);
+ } catch (ClassNotFoundException ex) {
+ if (logger.isLoggable(Level.SEVERE)) {
+ logger.log(Level.SEVERE, "Check " + cname + " is accessable" +
+ "from your classpath", ex);
+ }
+ throw new PolicyInitializationException(
+ "Unable to create a new instance of: " +
+ cname, ex);
+ } catch (SecurityException ex) {
+ if (logger.isLoggable(Level.SEVERE)) {
+ logger.log(Level.SEVERE,
+ "You don't have sufficient permissions to create" +
+ "a new instance of" + cname, ex);
+ }
+ throw new PolicyInitializationException(
+ "Unable to create a new instance of: " +
+ cname, ex);
+ }
+ }
+ cacheBasePerms = !(basePolicy instanceof DynamicPolicy);
+ initialized = true;
+ }
+
+ /**
+ * Behaves as specified by {@link Policy#getPermissions(CodeSource)}.
+ */
+ @Override
+ public PermissionCollection getPermissions(CodeSource source) {
+ PermissionCollection pc = basePolicy.getPermissions(source);
+ Permission[] pa = globalGrants.get(null);
+ for (int i = 0; i < pa.length; i++) {
+ Permission p = pa[i];
+ if (!pc.implies(p)) {
+ pc.add(p);
+ }
+ }
+ return pc;
+ }
+
+ /**
+ * Behaves as specified by {@link Policy#getPermissions(ProtectionDomain)}.
+ */
+ @Override
+ public PermissionCollection getPermissions(ProtectionDomain domain) {
+ return getDomainPermissions(domain).getPermissions(domain);
+ }
+
+ /**
+ * Behaves as specified by {@link Policy#implies}.
+ */
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ return getDomainPermissions(domain).implies(permission, domain);
+ }
+
+ /**
+ * Behaves as specified by {@link Policy#refresh}.
+ */
+ public void refresh() {
+ basePolicy.refresh();
+ if (cacheBasePerms) {
+ domainPerms.clear();
+ }
+ }
+
+ // documentation inherited from DynamicPolicy.grantSupported
+ public boolean grantSupported() {
+ return true;
+ }
+
+ // documentation inherited from DynamicPolicy.grant
+ public void grant(Class cl,
+ Principal[] principals,
+ Permission[] permissions) {
+ if (cl != null) {
+ checkDomain(cl);
+ }
+ if (principals != null && principals.length > 0) {
+ principals = (Principal[]) principals.clone();
+ checkNullElements(principals);
+ }
+ if (permissions == null || permissions.length == 0) {
+ return;
+ }
+ permissions = (Permission[]) permissions.clone();
+ checkNullElements(permissions);
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new GrantPermission(permissions));
+ }
+
+ Grants g = (cl != null) ? getLoaderGrants(getClassLoader(cl)) : globalGrants;
+ g.add(principals, permissions);
+ }
+
+ // documentation inherited from DynamicPolicy.getGrants
+ public Permission[] getGrants(Class cl, Principal[] principals) {
+ if (cl != null) {
+ checkDomain(cl);
+ }
+ if (principals != null && principals.length > 0) {
+ principals = (Principal[]) principals.clone();
+ checkNullElements(principals);
+ }
+
+ List l = Arrays.asList(globalGrants.get(principals));
+ if (cl != null) {
+ l = new ArrayList(l);
+ l.addAll(Arrays.asList(
+ getLoaderGrants(getClassLoader(cl)).get(principals)));
+ }
+ PermissionCollection pc = new Permissions();
+ for (Iterator i = l.iterator(); i.hasNext();) {
+ Permission p = (Permission) i.next();
+ if (!pc.implies(p)) {
+ pc.add(p);
+ }
+ }
+ l = Collections.list(pc.elements());
+ return (Permission[]) l.toArray(new Permission[l.size()]);
+ }
+
+ /**
+ * Ensures that any classes depended on by this policy provider are
+ * resolved. This is to preclude lazy resolution of such classes during
+ * operation of the provider, which can result in deadlock as described by
+ * bug 4911907.
+ */
+ public void ensureDependenciesResolved() {
+ // force class resolution by pre-invoking method called by implies()
+ getDomainPermissions(sysDomain);
+ }
+
+ private DomainPermissions getDomainPermissions(ProtectionDomain pd) {
+ DomainPermissions dp = domainPerms.get(pd);
+ if (dp == null) {
+ dp = new DomainPermissions(pd);
+ DomainPermissions exists = null;
+ if (pd != null) {
+ // If a concurrent thread has created a new copy, we get that instead;
+ exists = domainPerms.putIfAbsent(pd, dp);
+ if (exists != null) {
+ dp = exists;
+ }
+ getLoaderGrants(pd.getClassLoader()).register(dp);
+ }
+ }
+
+ synchronized (domainPerms) {
+ dp = (DomainPermissions) domainPerms.get(pd);
+ }
+ if (dp == null) {
+ dp = new DomainPermissions(pd);
+ globalGrants.register(dp);
+ if (pd != null) {
+ getLoaderGrants(pd.getClassLoader()).register(dp);
+ }
+ synchronized (domainPerms) {
+ domainPerms.put(pd, dp);
+ }
+ }
+ return dp;
+ }
+
+ private Grants getLoaderGrants(ClassLoader ldr) {
+ if (ldr == null) {
+ throw new NullPointerException("ClassLoader cannot be null");
+ }
+ Grants g = loaderGrants.get(ldr);
+ if (g == null) {
+ g = new Grants();
+ Grants exists = loaderGrants.putIfAbsent(ldr, g);
+ if (exists != null) {
+ g = exists;
+ }
+ }
+ return g;
+ }
+
+ private static ClassLoader getClassLoader(final Class cl) {
+ return (ClassLoader) AccessController.doPrivileged(
+ new PrivilegedAction() {
+
+ public Object run() {
+ return cl.getClassLoader();
+ }
+ });
+ }
+
+ private static void checkDomain(final Class cl) {
+ ProtectionDomain pd = (ProtectionDomain) AccessController.doPrivileged(
+ new PrivilegedAction() {
+
+ public Object run() {
+ return cl.getProtectionDomain();
+ }
+ });
+ if (pd != sysDomain && pd.getClassLoader() == null) {
+ throw new UnsupportedOperationException(
+ "ungrantable protection domain");
+ }
+ }
+
+ private static void checkNullElements(Object[] array) {
+ for (int i = 0; i < array.length; i++) {
+ if (array[i] == null) {
+ throw new NullPointerException();
+ }
+ }
+ }
+
+ /**
+ * Class which holds permissions and principals of a ProtectionDomain. The
+ * domainPerms map associates ProtectionDomain instances to instances of
+ * this class.
+ *
+ * This isn't the best designed class, the Set escapes, it isn't immutable
+ * or synchronized although it is backed by a fixed size array, so one
+ * could potentially change the Principals, it is only read however
+ * within this implementation and is private, so we'll leave it for now,
+ * with this caution.
+ */
+ class DomainPermissions {
+
+ private final Set principals;
+ private final PermissionCollection perms;
+ private final List grants = new ArrayList();
+
+ @SuppressWarnings("unchecked")
+ DomainPermissions( ProtectionDomain pd) {
+ Principal[] pra;
+ principals = (pd != null && (pra = pd.getPrincipals()).length > 0)
+ ? new HashSet(Arrays.asList(pra)) : Collections.EMPTY_SET;
+ perms = cacheBasePerms ? basePolicy.getPermissions(pd) : null;
+ }
+
+ Set getPrincipals() {
+ return principals;
+ }
+
+ @SuppressWarnings("unchecked")
+ synchronized void add(Permission[] pa) {
+ for (int i = 0; i < pa.length; i++) {
+ Permission p = pa[i];
+ grants.add(p);
+ if (perms != null) {
+ perms.add(p);
+ }
+ }
+ }
+
+ synchronized PermissionCollection getPermissions(ProtectionDomain d) {
+ return getPermissions(true, d);
+ }
+
+ synchronized boolean implies(Permission p, ProtectionDomain domain) {
+// System.out.println("Permission: " + p.toString() +
+// " ProtectionDomain: " + domain.toString());
+ if (perms != null) {
+ return perms.implies(p);
+ }
+ if (basePolicy.implies(domain, p)) {
+ return true;
+ }
+ if (grants.isEmpty()) {
+ return false;
+ }
+ return getPermissions(false, domain).implies(p);
+ }
+
+ private PermissionCollection getPermissions(boolean compact,
+ ProtectionDomain domain) {
+ // base policy permission collection may not be enumerable
+ assert Thread.holdsLock(this);
+ PermissionCollection pc = basePolicy.getPermissions(domain);
+ for (Iterator i = grants.iterator(); i.hasNext();) {
+ Permission p = (Permission) i.next();
+ if (!(compact && pc.implies(p))) {
+ pc.add(p);
+ }
+ }
+ return pc;
+ }
+ }
+
+ public void revoke(Class cl, Principal[] principals, Permission[] permissions) {
+ throw new UnsupportedOperationException("Revoke not supported.");
+ }
+
+ public boolean revokeSupported() {
+ return false;
+ }
+
+ public Object parameters() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+}
Propchange: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentDynamicPolicyProvider.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentPermissions.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentPermissions.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentPermissions.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentPermissions.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.river.security.concurrent;
+
+import java.io.Serializable;
+import java.security.AllPermission;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.ProtectionDomain;
+import java.security.UnresolvedPermission;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import org.apache.river.security.RevokeablePermissionCollection;
+
+
+/**
+ * Example Implementation alternative of Permissions implemented for concurrency,
+ * note this couldn't extend Permissions as it is declared final.
+ *
+ * Note that if there is heavy contention for one Permission class
+ * type, due to synchronization, concurrency will
+ * suffer. This is due to the original PermissionsCollection spec requiring
+ * that all implementations do their own synchronization, this is a design
+ * mistake, similar to Vector.
+ *
+ * This is an example class without optimisation, it will be slower
+ * for single threaded applications and consume more memory.
+ *
+ * It would also be possible to create an unlock method that permitted
+ * adding or revoking permissions to this collection while it is referenced
+ * from within a PermissionDomain, however that would break the contract of
+ * setReadOnly(). This might make sense if implementing a SecurityManager or
+ * it might not, it's just an observation that Permissions defined in policy
+ * files, which are not dynamically granted, are not revokeable as a
+ * Policy only augments the PermissionCollection associated with (referenced by)
+ * a PermissionDomain. However it would probably be best to extend
+ * ProtectionDomain to alter this behaviour as it merges PermissionCollection's,
+ * so you would end up with the Permissions implementation again, unless using the
+ * constructor that sets the permissions as static, which is totally
+ * contradictory. So the best way to make a Permission revokeable is to
+ * grant it dynamically.
+ *
+ * TODO: Serialization properly
+ * @version 0.4 2009/11/10
+ *
+ * @author Peter Firmstone
+ * @serial permsMap
+ */
+public final class ConcurrentPermissions extends PermissionCollection
+implements Serializable, RevokeablePermissionCollection {
+
+ private static final long serialVersionUID=1L;
+ /* unresolved is never returned or allowed to escape, it's elements() method
+ * isn't used to return an Enumeration yet
+ * Duplicate Permissions could potentially be returned if unresolved is
+ * enumerated first.
+ * For a ProtectionDomain object, duplicates are dropped by
+ * java.security.AccessControlContext
+ * This creates issues with java.security.AccessControlContext and
+ * causes it to throw an exception.
+ */
+ private transient final PermissionPendingResolutionCollection unresolved;
+ private ConcurrentHashMap<Class<?>, PermissionCollection> permsMap;
+ private transient volatile boolean allPermission;
+
+ /* Let Permissions, UnresolvedPermission and
+ * UnresolvedPermissionCollection resolve all unresolved permission's
+ * it saves reimplementing package private methods. This is done by adding
+ * a Permissions object instance to handle all UnresolvedPermissions.
+ */
+
+ public ConcurrentPermissions(){
+ permsMap = new ConcurrentHashMap<Class<?>, PermissionCollection>();
+ // Bite the bullet, get the pain out of the way in the beginning!
+ unresolved = new PermissionPendingResolutionCollection();
+ allPermission = false;
+ }
+
+ /**
+ * Threadsafe
+ * @param permission
+ */
+ @Override
+ public void add(Permission permission) {
+ if (permission == null){return;}
+ if (super.isReadOnly()) {
+ throw new SecurityException("attempt to add a Permission to a readonly Permissions object");
+ }
+ if (permission instanceof AllPermission) {allPermission = true;}
+ if (permission instanceof UnresolvedPermission) {
+ unresolved.add(new PermissionPendingResolution((UnresolvedPermission)permission));
+ }
+ // this get saves unnecessary object creation.
+ PermissionCollection pc = permsMap.get(permission.getClass());
+ if (pc != null){
+ pc.add(permission);
+ return;
+ }
+ // A null instance occurs when a PermissionCollection was absent.
+ pc = permsMap.putIfAbsent(permission.getClass(), new MultiReadPermissionCollection(permission));
+ // If This is the first time the PermissionCollection was added
+ // we must still add the permission, see Add the permission below.
+ // The putIfAbsent will succeed, and as a result pc is null, provided another
+ // thread doesn't add the permission between this put and the last get.
+ // Just in case, we check the return value of the putIfAbsent, if
+ // pc != null then another thread has alreaddy added a PermissionCollection
+ if (pc != null){
+ pc.add(permission);
+ return;
+ }
+ // Add the permission if it missed out due to unlucky timing.
+ pc = permsMap.get(permission.getClass());
+ pc.add(permission);
+ }
+
+ /**
+ * Returns true if Permission is implied for this PermissionDomain.
+ * Threadsafe this method is also a mutator method for internal state
+ *
+ * @see Permission
+ * @param permission
+ * @return boolean
+ */
+ @Override
+ public boolean implies(Permission permission) {
+ if (permission == null){return false;}
+ if (allPermission == true){return true;}
+ if (permission instanceof UnresolvedPermission){return false;}
+ PermissionCollection pc = permsMap.get(permission.getClass()); // To stop unnecessary object creation
+ if (pc != null && pc.implies(permission)) { return true;}
+ if (unresolved.awaitingResolution() == 0 ) { return false; }
+ PermissionCollection existed = null;
+ if (pc == null){
+ pc = new MultiReadPermissionCollection(permission); // once added it cannot be removed atomically.
+ existed = permsMap.putIfAbsent(permission.getClass(), pc);
+ if (existed != null) {
+ pc = existed;
+ }
+ }
+ unresolved.resolveCollection(permission, pc); // drop the returned collection its the same as the one passed in.
+ return pc.implies(permission);
+ }
+
+ /**
+ * This Enumeration is not intended for concurrent access,
+ * PermissionCollection's underlying state is protected by defensive copying,
+ * it wont affect the thread safety of ConcurrentPermission.
+ *
+ * Any number of these Enumerations may be utilised , each accessed by
+ * a separate thread.
+ *
+ * This Enumeration may contain duplicates
+ *
+ * @return Enumeration<Permission>
+ */
+ @Override
+ public Enumeration<Permission> elements() {
+ Enumeration<PermissionCollection> perms = permsMap.elements();
+ return new PermissionEnumerator(perms);
+ }
+
+ /**
+ * Attempt to resolve any unresolved permissions whose class is visible
+ * from within this protection domain.
+ * @param pd
+ */
+ public void resolve(ProtectionDomain pd){
+ if (unresolved.awaitingResolution() == 0){return;}
+ Enumeration<Permission> perms = unresolved.resolvePermissions(pd);
+ while (perms.hasMoreElements()){
+ add(perms.nextElement());
+ }
+ }
+
+ /*
+ * This Enumeration is not intended for concurrent access,
+ * PermissionCollection's underlying state is protected by defensive copying,
+ * it wont affect the thread safety of ConcurrentPermission.
+ *
+ * This enumeration doesn't include unresolved permissions is that a problem?
+ *
+ * Any number of these Enumerations may be utilised , each accessed by
+ * a separate thread.
+ *
+ * @author Peter Firmstone
+ */
+ private final static class PermissionEnumerator implements Enumeration<Permission> {
+ private final Enumeration<PermissionCollection> epc;
+ private volatile Enumeration<Permission> currentPermSet;
+
+ PermissionEnumerator(Enumeration<PermissionCollection> epc){
+ this.epc = epc;
+ currentPermSet = getNextPermSet();
+ }
+
+ private Enumeration<Permission> getNextPermSet(){
+ Set<Permission> permissionSet = new HashSet<Permission>();
+ if (epc.hasMoreElements()){
+ PermissionCollection pc = epc.nextElement();
+ /* Local copy of the set containing a snapshot of
+ * references to Permission objects present at an instant in time,
+ * we can Enumerate over, without contention or exception.
+ * We only take what we need as we need it, minimising memory.
+ * Each object gets its own Enumeration.
+ */
+ if ( pc instanceof Permissions){
+ synchronized (pc){
+ Enumeration<Permission> e = pc.elements();
+ while (e.hasMoreElements()) {
+ permissionSet.add(e.nextElement());
+ }
+ }
+ } else {
+ Enumeration<Permission> e = pc.elements();
+ while (e.hasMoreElements()) {
+ permissionSet.add(e.nextElement());
+ }
+ }
+ }
+ return Collections.enumeration(permissionSet);
+ }
+
+ public boolean hasMoreElements() {
+ if (currentPermSet.hasMoreElements()){return true;}
+ currentPermSet = getNextPermSet();
+ return currentPermSet.hasMoreElements();
+ }
+
+ public Permission nextElement() {
+ if (hasMoreElements()){
+ return currentPermSet.nextElement();
+ } else {
+ throw new NoSuchElementException("PermissionEnumerator");
+ }
+ }
+ }
+
+ public int revoke(Permission ... permissions) {
+
+ // build a hashmap to put all permissions into, grouped by class.
+ HashMap<Class<?>, Integer> results = new HashMap<Class<?>, Integer>();
+ HashMap<Class<?>, ArrayList<Permission>> groups = new HashMap<Class<?>, ArrayList<Permission>>();
+ int size = permissions.length;
+ for (int i = 0; i < size; i++ ){
+ Permission permission = permissions[i];
+ //Make sure that any unresolved permissions have been resolved first
+ //Don't bother revoking if it doesn't imply
+ if ( !implies(permission)){
+ continue;
+ }
+ ArrayList<Permission> permissionClassGroup = groups.get(permission.getClass());
+ if (permissionClassGroup == null){
+ permissionClassGroup = new ArrayList<Permission>();
+ groups.put(permissions[i].getClass(), permissionClassGroup);
+ }
+ permissionClassGroup.add(permission); // still holds a reference
+ }
+ // process each group by class, if all succeed return 1, if one or more has
+ // an issue with an intervening write return 0.
+ // If any fail, return -1
+ Set<Class<?>> classes = groups.keySet();
+ Iterator<Class<?>> itr = classes.iterator();
+ while (itr.hasNext()){
+ Class<?> permissionClass = itr.next();
+ RevokeablePermissionCollection current =
+ (RevokeablePermissionCollection) permsMap.get(permissionClass);
+ int result = 1; // If it doesn't exist then it must succeed.
+ if (current != null){
+ ArrayList<Permission> permissionGroupToRevoke = groups.get(permissionClass);
+ Permission permissionList[] = new Permission[permissionGroupToRevoke.size()];
+ permissionGroupToRevoke.toArray(permissionList);
+ result = current.revoke( permissionList );
+ }
+ results.put(permissionClass, result);
+ }
+ // Now determine the results and return
+ int result = 1;
+ Iterator<Integer> itresult = results.values().iterator();
+ while (itresult.hasNext()){
+ int thisResult = itresult.next();
+ if (thisResult < result) { result = thisResult; }
+ }
+ return result;
+ }
+
+
+ public void revokeAll(Permission permission) {
+ PermissionCollection pc = permsMap.get(permission.getClass());
+ if ( pc == null ){
+ pc = new MultiReadPermissionCollection(permission);
+ PermissionCollection exists = permsMap.putIfAbsent(permission.getClass(),pc);
+ if ( exists != null ){
+ pc = exists;
+ }
+ unresolved.resolveCollection(permission, pc);
+ }
+ // Any permissions added by other threads in the interim will be
+ // revoked when this method completes.
+ // We won't avoid permission loss by only emptying the collection either,
+ // however since
+ // the intent will be probably to temporarily revoke a permission
+ // when a proxy is required to be refreshed, it should be more efficient.
+ //permsMap.remove(permission.getClass(), pc);
+ RevokeablePermissionCollection rpc = (RevokeablePermissionCollection) pc;
+ rpc.revokeAll(permission);
+ }
+
+}
Propchange: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentPermissions.java
------------------------------------------------------------------------------
svn:eol-style = native