Return-Path: Delivered-To: apmail-incubator-river-commits-archive@minotaur.apache.org Received: (qmail 32266 invoked from network); 28 Mar 2010 12:57:39 -0000 Received: from unknown (HELO mail.apache.org) (140.211.11.3) by 140.211.11.9 with SMTP; 28 Mar 2010 12:57:39 -0000 Received: (qmail 83646 invoked by uid 500); 28 Mar 2010 12:57:39 -0000 Delivered-To: apmail-incubator-river-commits-archive@incubator.apache.org Received: (qmail 83616 invoked by uid 500); 28 Mar 2010 12:57:39 -0000 Mailing-List: contact river-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: river-dev@incubator.apache.org Delivered-To: mailing list river-commits@incubator.apache.org Received: (qmail 83608 invoked by uid 99); 28 Mar 2010 12:57:39 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 28 Mar 2010 12:57:39 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 28 Mar 2010 12:57:29 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 926EE23889DD; Sun, 28 Mar 2010 12:57:06 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: 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... Date: Sun, 28 Mar 2010 12:57:05 -0000 To: river-commits@incubator.apache.org From: peter_firmstone@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20100328125706.926EE23889DD@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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 DynamicPolicyProvider 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 * "getProperty" {@link java.security.SecurityPermission} to * read the - * net.jini.security.policy.DynamicPolicyProvider.basePolicyClass + * net.jini.security.policy.DynamicPolicyProviderImpl.basePolicyClass * security property, and may require "accessClassInPackage.*" * {@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 - * net.jini.security.policy.DynamicPolicyProvider.basePolicyClass + * net.jini.security.policy.DynamicPolicyProviderImpl.basePolicyClass * 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 DynamicPolicyProvider instance that wraps * around the given non-null base policy object. * @@ -133,446 +178,135 @@ public class DynamicPolicyProvider exten * null */ 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. + * + *

Applications are discouraged from calling this method + * since this operation may not be supported by all policy implementations. + * Applications should rely on the implies method + * to perform policy checks. + * + *

The default implementation of this method first retrieves + * the permissions returned via getPermissions(CodeSource) + * (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 getPermissions(CodeSource) + * returns Policy.UNSUPPORTED_EMPTY_COLLECTION, then this method + * returns the permissions contained inside the specified ProtectionDomain + * in a new PermissionCollection object. + * + *

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 pred 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 DynamicPolicyProvider instance that wraps a - * default underlying policy. The underlying policy is created as follows: - * if the - * net.jini.security.policy.DynamicPolicyProvider.basePolicyClass - * 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 - * "net.jini.security.policy.PolicyFileProvider" - * 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 PolicyInitializationException - * is thrown. - *

- * Note that this constructor requires the appropriate - * "getProperty" {@link java.security.SecurityPermission} to - * read the - * net.jini.security.policy.DynamicPolicyProvider.basePolicyClass - * security property, and may require "accessClassInPackage.*" - * {@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 - * net.jini.security.policy.DynamicPolicyProvider.basePolicyClass - * 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 DynamicPolicyProvider instance that wraps - * around the given non-null base policy object. - * - * @param basePolicy base policy object containing information about - * non-dynamic grants - * @throws NullPointerException if basePolicy is - * null - */ - 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 * null 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 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 domainPerms; + private final ConcurrentMap 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(); + 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 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, 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, 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 + */ + @Override + public Enumeration elements() { + Enumeration 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 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 { + private final Enumeration epc; + private volatile Enumeration currentPermSet; + + PermissionEnumerator(Enumeration epc){ + this.epc = epc; + currentPermSet = getNextPermSet(); + } + + private Enumeration getNextPermSet(){ + Set permissionSet = new HashSet(); + 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 e = pc.elements(); + while (e.hasMoreElements()) { + permissionSet.add(e.nextElement()); + } + } + } else { + Enumeration 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, Integer> results = new HashMap, Integer>(); + HashMap, ArrayList> groups = new HashMap, ArrayList>(); + 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 permissionClassGroup = groups.get(permission.getClass()); + if (permissionClassGroup == null){ + permissionClassGroup = new ArrayList(); + 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> classes = groups.keySet(); + Iterator> 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 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 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