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 [3/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...
Added: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentPolicyFile.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentPolicyFile.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentPolicyFile.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentPolicyFile.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,317 @@
+/*
+ * 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.
+ */
+
+ /**
+ * Default Policy implementation taken from Apache Harmony, refactored for
+ * concurrency.
+ *
+ * @author Alexey V. Varlamov
+ * @author Peter Firmstone
+ * @version $Revision$
+ */
+
+package org.apache.river.security.concurrent;
+
+import org.apache.river.security.policy.util.DefaultPolicyParser;
+import org.apache.river.security.policy.util.PolicyUtils;
+import java.io.File;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
+import org.apache.river.security.policy.util.PolicyEntry;
+import org.apache.river.security.policy.util.PolicyParser;
+import org.apache.river.util.concurrent.WeakIdentityMap;
+
+
+/**
+ * Concurrent Policy implementation based on policy configuration files,
+ * it is intended to provide concurrent implies() for greatly improved
+ * throughput at the expense of memory usage.
+ *
+ * Set the following system properties to use this Policy instead of the
+ * built in Java sun.security.provider.PolicyFile:
+ *
+ * net.jini.security.policy.PolicyFileProvider.basePolicyClass =
+ * org.apache.river.security.concurrent.ConcurrentPolicyFile
+ *
+ *
+ * This
+ * implementation recognizes text files, consisting of clauses with the
+ * following syntax:
+ *
+ * <pre>
+ * keystore "some_keystore_url" [, "keystore_type"];
+ * </pre>
+ <pre>
+ * grant [SignedBy "signer_names"] [, CodeBase "URL"]
+ * [, Principal [principal_class_name] "principal_name"]
+ * [, Principal [principal_class_name] "principal_name"] ... {
+ * permission permission_class_name [ "target_name" ] [, "action"]
+ * [, SignedBy "signer_names"];
+ * permission ...
+ * };
+ *
+ * </pre>
+ *
+ * The <i>keystore </i> clause specifies reference to a keystore, which is a
+ * database of private keys and their associated digital certificates. The
+ * keystore is used to look up the certificates of signers specified in the
+ * <i>grant </i> entries of the file. The policy file can contain any number of
+ * <i>keystore </i> entries which can appear at any ordinal position. However,
+ * only the first successfully loaded keystore is used, others are ignored. The
+ * keystore must be specified if some grant clause refers to a certificate's
+ * alias. <br>
+ * The <i>grant </i> clause associates a CodeSource (consisting of an URL and a
+ * set of certificates) of some executable code with a set of Permissions which
+ * should be granted to the code. So, the CodeSource is defined by values of
+ * <i>CodeBase </i> and <i>SignedBy </i> fields. The <i>CodeBase </i> value must
+ * be in URL format, while <i>SignedBy </i> value is a (comma-separated list of)
+ * alias(es) to keystore certificates. These fields can be omitted to denote any
+ * codebase and any signers (including case of unsigned code), respectively.
+ * <br>
+ * Also, the code may be required to be executed on behalf of some Principals
+ * (in other words, code's ProtectionDomain must have the array of Principals
+ * associated) in order to possess the Permissions. This fact is indicated by
+ * specifying one or more <i>Principal </i> fields in the <i>grant </i> clause.
+ * Each Principal is specified as class/name pair; name and class can be either
+ * concrete value or wildcard <i>* </i>. As a special case, the class value may
+ * be omitted and then the name is treated as an alias to X.509 Certificate, and
+ * the Principal is assumed to be javax.security.auth.x500.X500Principal with a
+ * name of subject's distinguished name from the certificate. <br>
+ * The order between the <i>CodeBase </i>, <i>SignedBy </i>, and <i>Principal
+ * </i> fields does not matter. The policy file can contain any number of grant
+ * clauses. <br>
+ * Each <i>grant </i> clause must contain one or more <i>permission </i> entry.
+ * The permission entry consist of a fully qualified class name along with
+ * optional <i>name </i>, <i>actions </i> and <i>signedby </i> values. Name and
+ * actions are arguments to the corresponding constructor of the permission
+ * class. SignedBy value represents the keystore alias(es) to certificate(s)
+ * used to sign the permission class. That is, this permission entry is
+ * effective (i.e., access control permission will be granted based on this
+ * entry) only if the bytecode implementation of permission class is verified to
+ * be correctly signed by the said alias(es). <br>
+ * <br>
+ * The policy content may be parameterized via property expansion. Namely,
+ * expressions like <i>${key} </i> are replaced by values of corresponding
+ * system properties. Also, the special <i>slash </i> key (i.e. ${/}) is
+ * supported, it is a shortcut to "file.separator" key. Property
+ * expansion is performed anywhere a double quoted string is allowed in the
+ * policy file. However, this feature is controlled by security properties and
+ * should be turned on by setting "policy.expandProperties" property
+ * to <i>true </i>. <br>
+ * If property expansion fails (due to a missing key), a corresponding entry is
+ * ignored. For fields of <i>keystore </i> and <i>grant </i> clauses, the whole
+ * clause is ignored, and for <i>permission </i> entry, only that entry is
+ * ignored. <br>
+ * <br>
+ * The policy also supports generalized expansion in permissions names, of
+ * expressions like <i>${{protocol:data}} </i>. Currently the following
+ * protocols supported:
+ * <dl>
+ * <dt>self
+ * <dd>Denotes substitution to a principal information of the parental Grant
+ * entry. Replaced by a space-separated list of resolved Principals (including
+ * wildcarded), each formatted as <i>class "name" </i>. If parental
+ * Grant entry has no Principals, the permission is ignored.
+ * <dt>alias: <i>name </i>
+ * <dd>Denotes substitution of a KeyStore alias. Namely, if a KeyStore has an
+ * X.509 certificate associated with the specified name, then replaced by
+ * <i>javax.security.auth.x500.X500Principal " <i>DN </i>" </i>
+ * string, where <i>DN </i> is a certificate's subject distinguished name.
+ * </dl>
+ * <br>
+ * <br>
+ * This implementation is thread-safe. The policy caches sets of calculated
+ * permissions for the requested objects (ProtectionDomains and CodeSources) via
+ * WeakHashMap; the cache is cleaned either explicitly during refresh()
+ * invocation, or naturally by garbage-collecting the corresponding objects.
+ *
+ * @see org.apache.harmony.security.PolicyUtils#getPolicyURLs(Properties, String,
+ * String)
+ */
+
+public class ConcurrentPolicyFile extends Policy {
+
+ /**
+ * System property for dynamically added policy location.
+ */
+ public static final String JAVA_SECURITY_POLICY = "java.security.policy"; //$NON-NLS-1$
+
+ /**
+ * Prefix for numbered Policy locations specified in security.properties.
+ */
+ public static final String POLICY_URL_PREFIX = "policy.url."; //$NON-NLS-1$
+
+ // A set of PolicyEntries constituting this Policy.
+ private final ReentrantReadWriteLock rwl;
+ private final ReadLock rl;
+ private final WriteLock wl;
+
+ private Set<PolicyEntry> grants = new HashSet<PolicyEntry>(); // protected by rwl
+
+ // Calculated Permissions cache, organized as
+ // Map{Object->Collection<Permission>}.
+ // The Object is a ProtectionDomain, a CodeSource or
+ // any other permissions-granted entity.
+ private final ConcurrentMap<Object, Collection<Permission>> cache =
+ new WeakIdentityMap<Object, Collection<Permission>>();
+
+ // A specific parser for a particular policy file format.
+ private final PolicyParser parser;
+
+ /**
+ * Default constructor, equivalent to
+ * <code>ConcurrentPolicyFile(new DefaultPolicyParser())</code>.
+ */
+ public ConcurrentPolicyFile() {
+ this(new DefaultPolicyParser());
+ }
+
+ /**
+ * Extension constructor for plugging-in a custom parser.
+ * @param dpr
+ */
+ public ConcurrentPolicyFile(PolicyParser dpr) {
+ parser = dpr;
+ rwl = new ReentrantReadWriteLock();
+ rl = rwl.readLock();
+ wl = rwl.writeLock();
+ refresh();
+ }
+
+ /**
+ * Returns collection of permissions allowed for the domain
+ * according to the policy. The evaluated characteristics of the
+ * domain are it's codesource and principals; they are assumed
+ * to be <code>null</code> if the domain is <code>null</code>.
+ * @param pd ProtectionDomain
+ * @see ProtectionDomain
+ */
+ @Override
+ public PermissionCollection getPermissions(ProtectionDomain pd) {
+ CodeSource cs = pd.getCodeSource();
+ Collection<Permission> pc = cache.get(cs); // saves new object creation.
+ if (pc == null){
+ // Just because the new object is contained within a ConcurrentMap
+ // doesn't mean it doesn't need to be synchronized!
+ pc = Collections.synchronizedSet( new HashSet<Permission>() );
+ Collection<Permission> existed = cache.putIfAbsent(cs, pc);
+ if ( !(existed == null) ){ pc = existed;}
+ }
+ try {
+ rl.lock();
+ Iterator<PolicyEntry> it = grants.iterator();
+ while (it.hasNext()) {
+ PolicyEntry ge = it.next();
+ if (ge.impliesPrincipals(pd == null ? null : pd.getPrincipals())
+ && ge.impliesCodeSource(pd == null ? null : pd.getCodeSource())) {
+ pc.addAll(ge.getPermissions());
+ }
+ }
+ } finally { rl.unlock(); }
+ return PolicyUtils.toPermissionCollection(pc);
+ }
+
+ /**
+ * Returns collection of permissions allowed for the codesource
+ * according to the policy.
+ * The evaluation assumes that current principals are undefined.
+ * @param cs CodeSource
+ * @see CodeSource
+ */
+ @Override
+ public PermissionCollection getPermissions(CodeSource cs) {
+ Collection<Permission> pc = cache.get(cs); // saves new object creation.
+ if (pc == null){
+ // Just because the new object is contained within a ConcurrentMap
+ // doesn't mean it doesn't need to be synchronized!
+ pc = Collections.synchronizedSet( new HashSet<Permission>() );
+ Collection<Permission> existed = cache.putIfAbsent(cs, pc);
+ if ( !(existed == null) ){ pc = existed;}
+ }
+ try {
+ rl.lock();
+ Iterator<PolicyEntry> it = grants.iterator();
+ while (it.hasNext()) {
+ PolicyEntry ge = it.next();
+ if (ge.impliesPrincipals(null)
+ && ge.impliesCodeSource(cs)) {
+ pc.addAll(ge.getPermissions()); // we still hold a reference
+ }
+ }
+ } finally { rl.unlock(); }
+ return PolicyUtils.toPermissionCollection(pc);
+ }
+
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ PermissionCollection pc = getPermissions(domain);
+ if (pc == null) {
+ return false;
+ }
+ return pc.implies(permission);
+ }
+
+ /**
+ * Gets fresh list of locations and tries to load all of them in sequence;
+ * failed loads are ignored. After processing all locations, old policy
+ * settings are discarded and new ones come into force. <br>
+ *
+ * @see PolicyUtils#getPolicyURLs(Properties, String, String)
+ */
+ @Override
+ public void refresh() {
+ Set<PolicyEntry> fresh = new HashSet<PolicyEntry>();
+ Properties system = new Properties(AccessController
+ .doPrivileged(new PolicyUtils.SystemKit()));
+ system.setProperty("/", File.separator); //$NON-NLS-1$
+ URL[] policyLocations = PolicyUtils.getPolicyURLs(system,
+ JAVA_SECURITY_POLICY,
+ POLICY_URL_PREFIX);
+ for (int i = 0; i < policyLocations.length; i++) {
+ try {
+ //TODO debug log
+ //System.err.println("Parsing policy file: " + policyLocations[i]);
+ Collection<PolicyEntry> pc = parser.parse(policyLocations[i], system);
+ fresh.addAll(pc);
+ } catch (Exception e) {
+ // TODO log warning
+ //System.err.println("Ignoring policy file: "
+ // + policyLocations[i] + ". Reason:\n"+ e);
+ }
+ }
+ try {
+ wl.lock();
+ grants = fresh;
+ cache.clear();
+ }finally {wl.unlock();}
+ }
+}
Propchange: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentPolicyFile.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/DyanamicConcurrentPolicyProvider.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/DyanamicConcurrentPolicyProvider.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/DyanamicConcurrentPolicyProvider.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/DyanamicConcurrentPolicyProvider.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,283 @@
+
+
+package org.apache.river.security.concurrent;
+
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Policy;
+import java.security.Principal;
+import java.security.ProtectionDomain;
+import java.security.Provider;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
+import net.jini.security.policy.DynamicPolicy;
+import net.jini.security.policy.PolicyInitializationException;
+import org.apache.river.security.policy.spi.RevokeableDynamicPolicySpi;
+import org.apache.river.security.policy.util.PolicyEntry;
+import org.apache.river.security.policy.util.PolicyUtils;
+import org.apache.river.util.concurrent.WeakIdentityMap;
+
+/**
+ * This is a Dynamic Policy Provider that supports concurrent access,
+ * for instances where a Policy provider is used for a distributed network
+ * of computers, or where there is a large number of ProtectionDomains and
+ * hence the opportunity for concurrency exists, concurrency comes with a
+ * cost however, that of increased memory usage.
+ *
+ * Due to the Java 2 Security system's static design, a Policy Provider
+ * can only augment the policy files utilised, that is it can only relax security
+ * by granting additional permissions, this implementation adds an experimental
+ * feature for revoking permissions, however there are some caveats:
+ *
+ * Firstly if the Policy.refresh() method is called, followed by the
+ * ProtectionDomain.toString() method, the ProtectionDomain
+ * merge the permissions, from the policy with those in the ProtectionDomain,
+ * a ProtectionDomain cannot have Permissions
+ * removed, only additional merged.
+ *
+ * So in order to prevent dynamic grants from finding
+ * their way into a ProtectionDomain's private PermissionCollection,
+ * one would have to ensure that no dynamically grantable permissions are
+ * returned via the methods:
+ *
+ * getPermissions(Codesource source) or
+ * getPermissions(ProtectionDomain domain)
+ *
+ * This is different to the behaviour of the existing Jini 2.0
+ * DynamicPolicyProvider implementation where dynamically granted Permissions
+ * are added.
+ *
+ * However when a Policy is checked via implies(ProtectionDomain d, Permission p)
+ * this implementation checks the dynamic grants.
+ *
+ * This means that if a DynamicPolicy is utilised as the base Policy class
+ * and if it returns dynamically granted permissions, then those permissions
+ * cannot be revoked.
+ *
+ * It is thus reccommeded that Static policy files only be used for files
+ * where the level of trust is relatively static. This is the only implementation
+ * where a dyanamic grant can be removed. In the case of Proxy trust, a proxy
+ * is no longer trusted when it has lost contact with it's Principal (server)
+ * because the server cannot be asked if it trusts it's proxy and the proxy
+ * cannot be given a thread of control to find it's server because it has
+ * already attained too many Permissions. In this new implementation it should
+ * be possible to revoke AllPermission and grant Permissions dynamically as
+ * trust is gained.
+ *
+ * This may cause some undesireable side effects in existing programs.
+ *
+ * There is one final reccommendation and that is adopting / utilising an OSGi
+ * Framework to enable far greater control over dynamic Permissions than this hack
+ * implementation provides.
+ *
+ * To make the best utilisation of this Policy provider, set the System property:
+ *
+ * net.jini.security.policy.PolicyFileProvider.basePolicyClass =
+ * org.apache.river.security.concurrent.ConcurrentPolicyFile
+ *
+ * @author Peter Firmstone
+ * @version 1
+ * @since 2.2
+ * @see ProtectionDomain
+ * @see Policy
+ * @see ConcurrentPolicyFile
+ * @see net.jini.security.policy.PolicyFileProvider
+ * @see ConcurrentPermissionCollection
+ */
+
+public class DyanamicConcurrentPolicyProvider implements RevokeableDynamicPolicySpi {
+
+ // A set of PolicyEntries constituting this Policy.
+ // PolicyEntry is lighter weight than PermissionCollection.
+ private final ReentrantReadWriteLock rwl;
+ private final ReadLock rl;
+ private final WriteLock wl;
+ private final Set<PolicyEntry> dynamicGrants; // protected by rwl
+ private volatile Policy basePolicy; // effectively final looks after its own sync
+ private final ConcurrentMap<ProtectionDomain, PermissionCollection> cache;
+ private volatile boolean basePolicyIsDynamic;
+ private boolean initialized = false;
+ // do something about some domain permissions for this domain so we can
+ // avoid dead locks due to bug 4911907
+
+
+ public DyanamicConcurrentPolicyProvider(){
+ rwl = new ReentrantReadWriteLock();
+ rl = rwl.readLock();
+ wl = rwl.writeLock();
+ dynamicGrants = new HashSet<PolicyEntry>();
+ basePolicy = null;
+ cache = new WeakIdentityMap<ProtectionDomain, PermissionCollection>();
+ basePolicyIsDynamic = false;
+ }
+
+ /**
+ * Idempotent method.
+ * @param basePolicy
+ * @return
+ */
+ public boolean basePolicy(Policy basePolicy) {
+ if (initialized == true) return false;
+ this.basePolicy = basePolicy;
+ return true;
+ }
+
+ /**
+ * Idempotent method.
+ */
+ public void initialize() throws PolicyInitializationException {
+ if (basePolicy == null) throw new PolicyInitializationException("Base Policy hasn't " +
+ "been set cannot initialize", new Exception("Failed to initialize"));
+ if (basePolicy instanceof DynamicPolicy) basePolicyIsDynamic = true;
+ initialized = true;
+ }
+
+ public void ensureDependenciesResolved() {
+ if (initialized == false) throw new RuntimeException("Object not initialized");
+
+ }
+
+ public void revoke(Class cl, Principal[] principals, Permission[] permissions) {
+ if (initialized == false) throw new RuntimeException("Object not initialized");
+ /* The removal begins with removal from the dynamicGrants then the cache
+ * while dynamicGrants is write locked, otherwise it could be added
+ * again due to a concurrent implies during the removal process.
+ * Actually the decision has been made to release the lock as soon as
+ * possible to prevent possible deadlocks, increase concurrency at the
+ * risk of possible positive implies() the the mean time.
+ */
+ ProtectionDomain domain = cl.getProtectionDomain();
+ CodeSource codeSource = domain.getCodeSource();
+ Collection<Permission> permToBeRemoved = Arrays.asList(permissions);
+ Collection<Permission> remainingGrants = new HashSet<Permission>();
+ try {
+ wl.lock();
+ Iterator<PolicyEntry> it = dynamicGrants.iterator();
+ while (it.hasNext()) {
+ PolicyEntry ge = it.next();
+ if (ge.impliesPrincipals(domain == null ? null : principals)
+ && ge.impliesCodeSource(domain == null ? null : codeSource)) {
+ remainingGrants.addAll( ge.getPermissions());
+ it.remove();
+ }
+ }
+ if (remainingGrants.isEmpty()) return; // nothing to do.
+ } finally { wl.unlock(); }
+ /* Now we can remove the PermissionDomain from the cache.
+ * The cache will populate itself again correctly when implies() is
+ * called on that PermissionDomain again.
+ */
+ cache.remove(cl);
+ /* We must re-enter the remaining grants if any exist. */
+ remainingGrants.removeAll(permToBeRemoved);
+ PolicyEntry policyEntry = new PolicyEntry(codeSource,
+ Arrays.asList(principals), remainingGrants);
+ try {
+ wl.lock();
+ dynamicGrants.add(policyEntry);
+ } finally { wl.unlock(); }
+ }
+
+ public boolean revokeSupported() {
+ if (initialized == false) throw new RuntimeException("Object not initialized");
+ return true;
+ }
+
+ public Provider getProvider() {
+ if (initialized == false) throw new RuntimeException("Object not initialized");
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public String getType() {
+ if (initialized == false) throw new RuntimeException("Object not initialized");
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public PermissionCollection getPermissions(CodeSource codesource) {
+ if (initialized == false) throw new RuntimeException("Object not initialized");
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public PermissionCollection getPermissions(ProtectionDomain domain) {
+ if (initialized == false) throw new RuntimeException("Object not initialized");
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ if (initialized == false) throw new RuntimeException("Object not initialized");
+ //First check the our cache
+ if (basePolicyIsDynamic && cache.get(domain).implies(permission)) return true;
+ // Then check the base policy
+ if (basePolicy.implies(domain, permission)) return true;
+ // If it doesn't then we should check if it has any grants
+ Collection<Permission> dynamicallyGrantedPermissions = null;
+ try {
+ rl.lock();
+ Iterator<PolicyEntry> it = dynamicGrants.iterator();
+ while (it.hasNext()) {
+ PolicyEntry ge = it.next();
+ if (ge.impliesPrincipals(domain == null ? null : domain.getPrincipals())
+ && ge.impliesCodeSource(domain == null ? null : domain.getCodeSource())) {
+ dynamicallyGrantedPermissions = ge.getPermissions();
+ }
+ }
+ } finally { rl.unlock(); }
+ if (dynamicallyGrantedPermissions == null) return false;
+ if (dynamicallyGrantedPermissions.isEmpty()) return false;
+ // Operation starts to get expensive
+ PermissionCollection pc = null;
+ if ( !(basePolicyIsDynamic) ) pc = cache.get(domain); // saves new object creation.
+ if (pc == null){
+ pc = basePolicy.getPermissions(domain);
+ if (pc == null) pc = new ConcurrentPermissions();
+ if (!(pc instanceof ConcurrentPermissions)) {
+ pc = PolicyUtils.toConcurrentPermissions(pc);
+ }
+ PermissionCollection existed = cache.putIfAbsent(domain, pc);
+ if ( !(existed == null) ){ pc = existed;} //Another thread might have just done it!
+ }
+ Iterator<Permission> dgpi = dynamicallyGrantedPermissions.iterator();
+ while (dgpi.hasNext()){
+ pc.add(dgpi.next());
+ }
+ // If we get refreshed the cache could be empty, which is more pedantic
+ // however the result may still be true so we'll return it anyway.
+ return pc.implies(permission);
+ }
+
+ public void refresh() {
+ if (initialized == false) throw new RuntimeException("Object not initialized");
+ cache.clear();
+ basePolicy.refresh();
+
+ }
+
+ public boolean grantSupported() {
+ if (initialized == false) throw new RuntimeException("Object not initialized");
+ return true;
+ }
+
+ public void grant(Class cl, Principal[] principals, Permission[] permissions) {
+ CodeSource cs = cl.getProtectionDomain().getCodeSource();
+ Collection<Principal> pal = Arrays.asList(principals);
+ Collection<Permission> perm = Arrays.asList(permissions);
+ PolicyEntry pe = new PolicyEntry(cs, pal, perm);
+ try {
+ wl.lock();
+ dynamicGrants.add(pe);
+ } finally {wl.unlock();}
+ }
+
+ public Permission[] getGrants(Class cl, Principal[] principals) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+}
Propchange: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/DyanamicConcurrentPolicyProvider.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/Grants.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/Grants.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/Grants.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/Grants.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,123 @@
+package org.apache.river.security.concurrent;
+
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Principal;
+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.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import org.apache.river.security.concurrent.ConcurrentDynamicPolicyProvider.DomainPermissions;
+
+class Grants {
+
+ private final Map principalGrants = new HashMap();
+ private final WeakGroup scope;
+ private final ReadWriteLock rwlock = new ReentrantReadWriteLock();
+ private final Lock rlock = rwlock.readLock();
+ private final Lock wlock = rwlock.writeLock();
+
+ @SuppressWarnings(value = "unchecked")
+ Grants() {
+ super();
+ PrincipalGrants pg = new PrincipalGrants();
+ principalGrants.put(Collections.EMPTY_SET, pg);
+ scope = pg.scope;
+ }
+
+ @SuppressWarnings(value = "unchecked")
+ void add(Principal[] pra, Permission[] pa) {
+ Set prs = (pra != null && pra.length > 0) ? new HashSet(Arrays.asList(pra)) : Collections.EMPTY_SET;
+ ArrayList l = new ArrayList();
+ wlock.lock();
+ try {
+ 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);
+ }
+ 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);
+ }
+ }
+ } finally {
+ wlock.unlock();
+ }
+ }
+
+ @SuppressWarnings(value = "unchecked")
+ Permission[] get(Principal[] pra) {
+ Set prs = (pra != null && pra.length > 0) ? new HashSet(Arrays.asList(pra)) : Collections.EMPTY_SET;
+ List l = new ArrayList();
+ rlock.lock();
+ try {
+ 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()));
+ }
+ }
+ } finally {
+ rlock.unlock();
+ }
+ return (Permission[]) l.toArray(new Permission[l.size()]);
+ }
+
+ @SuppressWarnings(value = "unchecked")
+ void register(DomainPermissions dp) {
+ Set prs = dp.getPrincipals();
+ wlock.lock();
+ try {
+ 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()]));
+ }
+ }
+ } finally {
+ wlock.unlock();
+ }
+ }
+
+ 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() {
+ super();
+ }
+ }
+}
Propchange: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/Grants.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/MultiReadPermissionCollection.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/MultiReadPermissionCollection.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/MultiReadPermissionCollection.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/MultiReadPermissionCollection.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,340 @@
+/*
+ * 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.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.UnresolvedPermission;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.apache.river.security.RevokeablePermissionCollection;
+
+/**
+ * MultiReadPermissionCollection is a wrapper class that enables mutliple
+ * reads and RevokablePermissionCollection support. It only supports
+ * a homogenous class PermissionCollection.
+ *
+ * TODO Serialization Correctly
+ * @version 0.2 2009/11/14
+ * @author Peter Firmstone
+ */
+final class MultiReadPermissionCollection extends PermissionCollection
+ implements RevokeablePermissionCollection, Serializable {
+ private final static long serialVersionUID = 1L;
+ private transient PermissionCollection permCl; // all access protected by rwl
+ private final transient ReadWriteLock rwl;
+ private final transient Lock rl;
+ private final transient Lock wl;
+ private transient int writeCounter; // all access protected by rwl
+ private boolean readOnly; // all access protected by rwl
+ private Permission[] permissions; //never instantiate for ide code completion
+
+ MultiReadPermissionCollection(Permission p){
+ permCl = newPermissionCollection(p);
+ rwl = new ReentrantReadWriteLock();
+ rl = rwl.readLock();
+ wl = rwl.writeLock();
+ writeCounter = 0;
+ readOnly = false;
+ }
+
+ @Override
+ public boolean isReadOnly(){
+ rl.lock();
+ try{
+ return readOnly;
+ }finally {rl.unlock();}
+ }
+
+ @Override
+ public void setReadOnly(){
+ wl.lock();
+ try{
+ readOnly = true;
+ }finally {wl.unlock();}
+ }
+
+
+
+ @Override
+ public String toString(){
+ rl.lock();
+ try {
+ return permCl.toString();
+ } finally { rl.unlock();}
+ }
+
+ @Override
+ public boolean equals(Object obj){
+ if (this == obj) return true;
+ if ( !(obj instanceof MultiReadPermissionCollection)) return false;
+ MultiReadPermissionCollection mrpcObj = (MultiReadPermissionCollection) obj;
+ rl.lock();
+ try {
+ if (permCl.equals(mrpcObj.permCl)) return true;
+ }finally {rl.unlock();}
+ return false;
+ }
+
+ public int hashCode(){
+ rl.lock();
+ try {
+ return permCl.hashCode();
+ }finally {rl.unlock();}
+ }
+
+ @Override
+ public void add(Permission permission) {
+ if (readOnly) {
+ throw new SecurityException("attempt to add a Permission to a readonly Permissions object");
+ }
+ wl.lock();
+ try {
+ permCl.add(permission);
+ writeCounter++;
+ }
+ finally {wl.unlock();}
+ }
+
+ @Override
+ public boolean implies(Permission permission) {
+ rl.lock();
+ try {return permCl.implies(permission);}
+ finally {rl.unlock();}
+ }
+
+ @Override
+ public Enumeration<Permission> elements() {
+ rl.lock();
+ try {return permCl.elements();}
+ finally {rl.unlock();}
+ }
+
+
+ /* Returns an empty PermissionCollection
+ */
+ private PermissionCollection newPermissionCollection(Permission permission){
+ PermissionCollection pc = permission.newPermissionCollection();
+ if (pc == null){
+ pc = new PermissionHash();
+ }
+ return pc;
+ }
+ /**
+ * Permissions may have some overlap, this method will remove any Permission
+ * objects that imply any of the Permission's supplied.
+ *
+ * If this fails it will be due to the implies method returning true
+ * due to a combination of Permission objects.
+ *
+ * Permission objects must have the same class and type for this implementation.
+ *
+ * @param permissions
+ * @return success
+ */
+ public int revoke(Permission ... permissions) {
+ int count = 0; // false
+ HashSet<Permission> permissionSet = new HashSet<Permission>();
+ rl.lock();
+ try {
+ if (readOnly) {
+ throw new SecurityException("attempt to remove a Permission from a readonly Permissions object");
+ }
+ count = writeCounter;
+ Enumeration<Permission> current = elements();
+ while (current.hasMoreElements()) {
+ permissionSet.add(current.nextElement());
+ }
+ } finally {
+ rl.unlock();
+ }
+ if (permissionSet.size() == 0) {
+ return 1; // true
+ }
+ Iterator<Permission> itr = permissionSet.iterator();
+ PermissionCollection newCollection = null;
+ int size = permissions.length;
+ PER:
+ while (itr.hasNext() ){
+ Permission p = itr.next();
+ if (newCollection == null) {
+ newCollection = p.newPermissionCollection();
+ if (newCollection == null ){
+ newCollection = new PermissionHash();
+ }
+ }
+ for (int i = 0; i < size; i++) {
+ if (p.implies(permissions[i])) {
+ continue PER;
+ }
+ }
+ newCollection.add(p); // if the wrong type is passed in it doesn't matter
+ }
+ /* Check that our modifications have been effective.
+ */
+ for (int i = 0; i < size ; i++){
+ if ( newCollection.implies(permissions[i])) { return -1;} // fail
+ }
+ return updatePermissionCollection(newCollection, count);
+ }
+
+ /**
+ * Idempotent method to remove all Permissions Contained within a
+ * PermissionCollection. Method fails if PermissionCollection is
+ * modified during method execution. This method
+ * creates an empty replacement PermissionCollection.
+ *
+ * This method also resets setReadOnly() to false;
+ *
+ * @param permission
+ */
+ public void revokeAll(Permission permission) {
+ boolean sameClassType = false;
+ int count = 0;
+ rl.lock();
+ try {
+ Enumeration<Permission> current = elements();
+ Permission currentPerm = null;
+ if (current.hasMoreElements()) {
+ currentPerm = current.nextElement();
+ }
+ if (currentPerm == null) { return; } // idempotent, already empty.
+ if (currentPerm.getClass().equals(permission.getClass())){ sameClassType = true;}
+ } finally { rl.unlock(); }
+ PermissionCollection newCollection = permission.newPermissionCollection();
+ if (newCollection == null) { newCollection = new PermissionHash(); }
+ if (sameClassType == true){
+ updatePermissionCollection(newCollection, count);
+ }
+ }
+
+ private int updatePermissionCollection( PermissionCollection pc, int writeCount ){
+ wl.lock();
+ try {
+ if ( writeCount == writeCounter || writeCount == 0 ) {
+ permCl = pc;
+ writeCounter = 0; //Reset the counter
+ if (writeCount == 0) {readOnly = false;}
+ return 1;
+ }
+ } finally {
+ wl.unlock();
+ }
+ return 0;
+ }
+
+ private static class SerializationProxy implements Serializable {
+ private static final long serialVersionUID = 1L;
+ private final Permission[] permissions;
+ private final boolean readOnly;
+ SerializationProxy(PermissionCollection pc){
+ ArrayList<Permission> collection = new ArrayList<Permission>();
+ Enumeration<Permission> en = pc.elements();
+ while (en.hasMoreElements()){
+ collection.add(en.nextElement());
+ }
+ permissions = new Permission[collection.size()];
+ collection.toArray(permissions);
+ readOnly = pc.isReadOnly();
+ }
+ }
+
+ private Object writeReplace(){
+ return new SerializationProxy(this);
+ }
+
+ private void readObject(ObjectInputStream stream) throws InvalidObjectException {
+ throw new InvalidObjectException("Proxy required");
+ }
+
+ private Object readResolve() {
+ MultiReadPermissionCollection pc = new MultiReadPermissionCollection(permissions[0]);
+ int length = permissions.length;
+ for ( int i = 0 ; i < length ; i++){
+ pc.add(permissions[i]);
+ }
+ if ( readOnly == true ) {pc.setReadOnly();}
+ return pc;
+ }
+
+ private static final class PermissionHash extends PermissionCollection {
+ // This class is never serialized.
+ private final static long serialVersionUID = 1L;
+ private HashSet<Permission> permSet;
+
+ public PermissionHash(){
+ permSet = new HashSet<Permission>();
+ }
+
+ public boolean equals(Object obj){
+ if (this == obj) return true;
+ if (!(obj instanceof PermissionHash)) return false;
+ PermissionHash phObj = (PermissionHash) obj;
+ if (this.permSet.equals(phObj.permSet)) return true;
+ return false;
+ }
+
+ public int hashCode(){
+ return permSet.hashCode();
+ }
+
+ public String toString(){
+ return permSet.toString();
+ }
+
+ @Override
+ public void add(Permission permission) {
+ permSet.add(permission);
+ }
+
+ @Override
+ public boolean implies(Permission permission) {
+ // attempt a fast lookup and implies. If that fails
+ // then enumerate through all the permissions.
+ if (permSet.contains(permission)
+ && !(permission instanceof UnresolvedPermission)) {
+ return true;
+ }
+ Iterator<Permission> itr = permSet.iterator();
+ while (itr.hasNext()) {
+ Permission p = itr.next();
+ if (p.implies(permission)) {return true;}
+ }
+ return false;
+ }
+
+ @Override
+ public Enumeration<Permission> elements() {
+ synchronized (this){
+ return Collections.enumeration(permSet);
+ }
+ }
+ }
+}
Propchange: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/MultiReadPermissionCollection.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/PermissionPendingResolution.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/PermissionPendingResolution.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/PermissionPendingResolution.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/PermissionPendingResolution.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,206 @@
+/*
+ * 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.lang.reflect.Constructor;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.UnresolvedPermission;
+import java.security.cert.Certificate;
+
+/**
+ *
+ * @author Peter Firmstone
+ */
+class PermissionPendingResolution extends Permission {
+ private static final long serialVersionUID = 1L;
+ private transient String type; //Class name of underlying permission
+ private transient String name; //Target name of underlying permission
+ private transient String actions;
+ /* We have our own array copy of certs, prevents unnecessary
+ * array creation every time .getUnresolvedCerts() is called.
+ */
+ private transient Certificate [] targetCerts;
+ private UnresolvedPermission unresolvedPermission;
+
+ PermissionPendingResolution(UnresolvedPermission up){
+ super(up.getUnresolvedType());
+ type = up.getUnresolvedType();
+ name = up.getUnresolvedName();
+ actions = up.getUnresolvedActions();
+ // don't need to defensive copy, UnresolvedPermission already does it.
+ targetCerts = up.getUnresolvedCerts();
+ unresolvedPermission = up;
+ }
+
+ Permission resolve(Class targetType) {
+ // check signers at first
+ if (matchSubset( targetCerts, targetType.getSigners())) {
+ try {
+ return instantiatePermission(targetType, name, actions);
+ } catch (Exception ignore) {
+ //TODO log warning?
+ }
+ }
+ return null;
+ }
+
+ Permission resolve(ClassLoader cl){
+ Class<?> targetType = null;
+ try {
+ targetType = cl.loadClass(type);
+ } catch (ClassNotFoundException e){
+ //TODO log warning?
+ System.err.println(type +" " + name + " " + actions +
+ ": Cannot be resolved due to ClassNotFoundException");
+ e.printStackTrace();
+ } catch (NullPointerException e){
+ //TODO log warning, this should never happen but if it does
+ //the class will not be resolved.
+ System.err.println(type +" " + name + " " + actions +
+ ": Cannot be resolved due to ClassLoader null instance");
+ e.printStackTrace();
+ }
+ if ( targetType == null ) {return null;}
+ return resolve(targetType);
+ }
+
+
+ /**
+ * Code Copied, Courtesey Apache Harmony
+ *
+ * Checks whether the objects from <code>what</code> array are all
+ * presented in <code>where</code> array.
+ *
+ * @param what first array, may be <code>null</code>
+ * @param where second array, may be <code>null</code>
+ * @return <code>true</code> if the first array is <code>null</code>
+ * or if each and every object (ignoring null values)
+ * from the first array has a twin in the second array; <code>false</code> otherwise
+ */
+ boolean matchSubset(Object[] what, Object[] where) {
+ if (what == null) {
+ return true;
+ }
+
+ for (int i = 0; i < what.length; i++) {
+ if (what[i] != null) {
+ if (where == null) {
+ return false;
+ }
+ boolean found = false;
+ for (int j = 0; j < where.length; j++) {
+ if (what[i].equals(where[j])) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ // Empty set of arguments to default constructor of a Permission.
+ private static final Class[] NO_ARGS = {};
+
+ // One-arg set of arguments to default constructor of a Permission.
+ private static final Class[] ONE_ARGS = { String.class };
+
+ // Two-args set of arguments to default constructor of a Permission.
+ private static final Class[] TWO_ARGS = { String.class, String.class };
+
+ /**
+ * Code copied, courtsey of Apache Harmony
+ *
+ * Tries to find a suitable constructor and instantiate a new Permission
+ * with specified parameters.
+ *
+ * @param targetType class of expected Permission instance
+ * @param targetName name of expected Permission instance
+ * @param targetActions actions of expected Permission instance
+ * @return a new Permission instance
+ * @throws IllegalArgumentException if no suitable constructor found
+ * @throws Exception any exception thrown by Constructor.newInstance()
+ */
+ Permission instantiatePermission(Class<?> targetType,
+ String targetName, String targetActions) throws Exception {
+
+ // let's guess the best order for trying constructors
+ Class[][] argTypes = null;
+ Object[][] args = null;
+ if (targetActions != null) {
+ argTypes = new Class[][] { TWO_ARGS, ONE_ARGS, NO_ARGS };
+ args = new Object[][] { { targetName, targetActions },
+ { targetName }, {} };
+ } else if (targetName != null) {
+ argTypes = new Class[][] { ONE_ARGS, TWO_ARGS, NO_ARGS };
+ args = new Object[][] { { targetName },
+ { targetName, targetActions }, {} };
+ } else {
+ argTypes = new Class[][] { NO_ARGS, ONE_ARGS, TWO_ARGS };
+ args = new Object[][] { {}, { targetName },
+ { targetName, targetActions } };
+ }
+
+ // finally try to instantiate actual permission
+ for (int i = 0; i < argTypes.length; i++) {
+ try {
+ Constructor<?> ctor = targetType.getConstructor(argTypes[i]);
+ return (Permission)ctor.newInstance(args[i]);
+ }
+ catch (NoSuchMethodException ignore) {}
+ }
+ throw new IllegalArgumentException(type + name + actions);//$NON-NLS-1$
+ }
+
+ @Override
+ public boolean implies(Permission permission) {
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if ( obj == this ) {return true;}
+ if ( !(obj instanceof PermissionPendingResolution)) {return false;}
+ PermissionPendingResolution ob = (PermissionPendingResolution) obj;
+ if (this.unresolvedPermission.equals(ob.unresolvedPermission)) {return true;}
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return unresolvedPermission.hashCode();
+ }
+
+ @Override
+ public String getActions() {
+ return "";
+ }
+
+ @Override
+ public PermissionCollection newPermissionCollection(){
+ return new PermissionPendingResolutionCollection();
+ }
+
+ public UnresolvedPermission asUnresolvedPermission(){
+ return unresolvedPermission;
+ }
+}
Propchange: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/PermissionPendingResolution.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/PermissionPendingResolutionCollection.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/PermissionPendingResolutionCollection.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/PermissionPendingResolutionCollection.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/PermissionPendingResolutionCollection.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,147 @@
+/*
+ * 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.security.AccessController;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ *
+ * @author Peter Firmstone
+ */
+public class PermissionPendingResolutionCollection extends PermissionCollection {
+ private static final long serialVersionUID = 1L;
+ private ConcurrentHashMap<String,Collection<PermissionPendingResolution>> klasses;
+ private AtomicInteger pending;
+ PermissionPendingResolutionCollection(){
+ klasses = new ConcurrentHashMap<String,Collection<PermissionPendingResolution>>(2);
+ pending = new AtomicInteger(0);
+ }
+
+ public int awaitingResolution(){
+ return pending.get();
+ }
+
+
+ public void add(Permission permission) {
+ if (isReadOnly()) {
+ throw new SecurityException("attempt to add a Permission to a readonly Permissions object"); //$NON-NLS-1$
+ }
+ if (permission == null) { throw new IllegalArgumentException("Null Permission");}
+ if ( permission.getClass() != PermissionPendingResolution.class || permission.getClass() != PermissionPendingResolution.class ) {
+ throw new IllegalArgumentException("Not instance of PermissionPendingResolution");
+ }
+ String klass = permission.getName();
+ Collection<PermissionPendingResolution> klassMates = klasses.get(klass);
+ if (klassMates != null){
+ klassMates.add((PermissionPendingResolution) permission);
+ pending.incrementAndGet();
+ return;
+ }
+ Collection<PermissionPendingResolution> klassMatesExists = null;
+ Set<PermissionPendingResolution> pprs = new HashSet<PermissionPendingResolution>();
+ klassMates = Collections.synchronizedSet(pprs);
+ klassMatesExists = klasses.putIfAbsent(klass, klassMates);
+ if (klassMatesExists == null){
+ klassMates.add((PermissionPendingResolution) permission);
+ pending.incrementAndGet();
+ }else{
+ klassMatesExists.add((PermissionPendingResolution) permission);
+ pending.incrementAndGet();
+ }
+ }
+
+ PermissionCollection resolveCollection(Permission target,
+ PermissionCollection holder ){
+ if (pending.get() == 0) { return holder; }
+ String klass = target.getClass().getName();
+ if (klasses.containsKey(klass)) {
+ Collection<PermissionPendingResolution> klassMates = klasses.get(klass);
+ for (Iterator<PermissionPendingResolution> iter = klassMates.iterator(); iter.hasNext();) {
+ PermissionPendingResolution element = iter.next();
+ Permission resolved = element.resolve(target.getClass());
+ if (resolved != null) {
+ if (holder == null) {
+ holder = new MultiReadPermissionCollection(target);
+ }
+ holder.add(resolved);
+ iter.remove();
+ pending.decrementAndGet();
+ }
+ }
+ // there is no possible way of atomic removal of an empty Collection.
+ }
+ return holder;
+ }
+
+ //Should I be performing a privileged action? Or should it run with
+ // the caller thread's privileges?
+ Enumeration<Permission> resolvePermissions(final ProtectionDomain pd){
+ @SuppressWarnings("unchecked")
+ ClassLoader cl = (ClassLoader) AccessController.doPrivileged(
+ new PrivilegedAction(){
+ public Object run(){
+ ClassLoader cL = pd.getClassLoader();
+ if (cL == null){
+ cL = this.getClass().getClassLoader();
+ }
+ return cL;
+ }
+ });
+
+
+ List<Permission> perms = new ArrayList<Permission>();
+ Enumeration enPending = elements();
+ while (enPending.hasMoreElements()){
+ PermissionPendingResolution pendPerm =
+ (PermissionPendingResolution) enPending.nextElement();
+ Permission resolved = pendPerm.resolve(cl);
+ if ( resolved != null ){
+ perms.add(resolved);
+ }
+ }
+ return Collections.enumeration(perms);
+ }
+
+ @Override
+ public boolean implies(Permission permission) {
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Enumeration<Permission> elements() {
+ Collection all = new ArrayList();
+ for (Iterator iter = klasses.values().iterator(); iter.hasNext();) {
+ all.addAll((Collection)iter.next());
+ }
+ return Collections.enumeration(all);
+ }
+}
Propchange: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/PermissionPendingResolutionCollection.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/WeakGroup.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/WeakGroup.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/WeakGroup.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/WeakGroup.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,114 @@
+package org.apache.river.security.concurrent;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+class WeakGroup {
+
+ private final ReferenceQueue rq = new ReferenceQueue();
+ private final Node head;
+ private final Node tail;
+
+ WeakGroup() {
+ super();
+ 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;
+ }
+
+ private Node(Object obj) {
+ super(obj);
+ }
+
+ Node(Object obj, ReferenceQueue rq) {
+ super(obj, rq);
+ }
+
+ 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;
+ }
+ }
+}
Propchange: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/WeakGroup.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/river/jtsk/trunk/src/org/apache/river/security/policy/spi/RevokeableDynamicPolicySpi.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/policy/spi/RevokeableDynamicPolicySpi.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/policy/spi/RevokeableDynamicPolicySpi.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/policy/spi/RevokeableDynamicPolicySpi.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,36 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.apache.river.security.policy.spi;
+
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import net.jini.security.policy.PolicyInitializationException;
+
+/**
+ * An implementer of this interface isn't required to extend Policy and isn't
+ * required to implement dynamic grants either.
+ * @author Peter Firmstone
+ */
+public interface RevokeableDynamicPolicySpi extends RevokeablePolicy {
+ public boolean basePolicy(Policy basePolicy);
+ public void initialize()throws PolicyInitializationException;
+ /**
+ * 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();
+ // All the java.security.Policy methods
+ public PermissionCollection getPermissions(CodeSource codesource);
+ public PermissionCollection getPermissions(ProtectionDomain domain);
+ public boolean implies(ProtectionDomain domain, Permission permission);
+ public void refresh();
+
+}
Propchange: incubator/river/jtsk/trunk/src/org/apache/river/security/policy/spi/RevokeableDynamicPolicySpi.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/river/jtsk/trunk/src/org/apache/river/security/policy/spi/RevokeablePolicy.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/policy/spi/RevokeablePolicy.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/policy/spi/RevokeablePolicy.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/policy/spi/RevokeablePolicy.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,26 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.apache.river.security.policy.spi;
+
+import java.security.Permission;
+import java.security.Principal;
+import net.jini.security.policy.DynamicPolicy;
+
+/**
+ * A DynamicPolicy that supports revoking as well as permission grants.
+ *
+ * All Policy methods are implemented so RevokeableDynamicPolicySpi
+ * implementions aren't necessarily required to extend Policy.
+ *
+ * @author Peter Firmstone
+ * @see DynamicPolicy
+ */
+public interface RevokeablePolicy extends DynamicPolicy {
+
+ public void revoke(Class cl, Principal[] principals, Permission[] permissions)
+ throws UnsupportedOperationException;
+ public boolean revokeSupported();
+}
Propchange: incubator/river/jtsk/trunk/src/org/apache/river/security/policy/spi/RevokeablePolicy.java
------------------------------------------------------------------------------
svn:eol-style = native