Author: peter_firmstone
Date: Fri Dec 23 02:26:21 2011
New Revision: 1222530

URL: http://svn.apache.org/viewvc?rev=1222530&view=rev
Log:
River-323 Creation of URLGrant to replace CodeSourceGrant's, this avoids 
dynamic lookup and other Security Manager and policy refactorings.

Added:
    
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/URIGrant.java
      - copied, changed from r1187844, 
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/CodeSourceGrant.java
Modified:
    
river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/DynamicPermissionCollection.java
    
river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/policy/DynamicPolicyProvider.java
    
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/CodeSourceSetGrant.java
    
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/DelegateCombinerSecurityManager.java
    
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PermissionGrantBuilder.java
    
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PermissionGrantBuilderImp.java
    
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/ProtectionDomainGrant.java
    
river/jtsk/skunk/peterConcurrentPolicy/test/src/org/apache/river/api/security/DelegateCombinerSecurityManagerTest.java

Modified: 
river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/DynamicPermissionCollection.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/DynamicPermissionCollection.java?rev=1222530&r1=1222529&r2=1222530&view=diff
==============================================================================
--- 
river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/DynamicPermissionCollection.java
 (original)
+++ 
river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/DynamicPermissionCollection.java
 Fri Dec 23 02:26:21 2011
@@ -71,6 +71,8 @@ final class DynamicPermissionCollection 
     @Override
     public boolean implies(Permission permission) {
         if ( ! cl.isInstance(permission)) return false;
+        Thread thread = Thread.currentThread();
+        if (thread.isInterrupted()) return false;
         Permission [] p = perms.toArray(new Permission[0]); //perms.size() may 
change
         if (comp != null){
             Arrays.sort(p, comp);

Modified: 
river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/policy/DynamicPolicyProvider.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/policy/DynamicPolicyProvider.java?rev=1222530&r1=1222529&r2=1222530&view=diff
==============================================================================
--- 
river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/policy/DynamicPolicyProvider.java
 (original)
+++ 
river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/policy/DynamicPolicyProvider.java
 Fri Dec 23 02:26:21 2011
@@ -522,6 +522,8 @@ Put the policy providers and all referen
         if ( pc != null ) {
             if (pc.implies(permission)) return true;          
         } 
+        Thread thread = Thread.currentThread();
+        if (thread.isInterrupted()) return false;
         /* Do not call implies on the base Policy, if
          * there are UnresolvedPermission's that are undergoing resolution
          * while another Permission within that collection is already
@@ -570,6 +572,8 @@ Put the policy providers and all referen
         // If the base policy doesn't imply a Permission then we should check 
for dynamic grants
         Collection<Permission> dynamicallyGrantedPermissions = new 
HashSet<Permission>(120);
        PermissionGrant[] grantsRefCopy = remotePolicyGrants; // In case the 
grants volatile reference is updated.
+        
+        if (thread.isInterrupted()) return false;
        int l = grantsRefCopy.length;
        for ( int i = 0; i < l; i++){
            if (grantsRefCopy[i].implies(domain)) {
@@ -578,6 +582,7 @@ Put the policy providers and all referen
                dynamicallyGrantedPermissions.addAll(Arrays.asList(perms));
            }
        }
+        if (thread.isInterrupted()) return false;
         Iterator<PermissionGrant> grants = dynamicPolicyGrants.iterator();
         while (grants.hasNext()){
             PermissionGrant g = grants.next();
@@ -585,6 +590,7 @@ Put the policy providers and all referen
                 dynamicallyGrantedPermissions.addAll(g.getPermissions());
             }
         }
+        if (thread.isInterrupted()) return false;
 //        if (loggable) {
 //            logger.log(Level.FINEST, "Grants: " + 
dynamicallyGrantedPermissions.toString());
 //        }
@@ -596,6 +602,7 @@ Put the policy providers and all referen
         while (dgpi.hasNext()){
             pc.add(dgpi.next());
         }
+        if (thread.isInterrupted()) return false;
         // 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.
 //        if (loggable) {

Modified: 
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/CodeSourceSetGrant.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/CodeSourceSetGrant.java?rev=1222530&r1=1222529&r2=1222530&view=diff
==============================================================================
--- 
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/CodeSourceSetGrant.java
 (original)
+++ 
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/CodeSourceSetGrant.java
 Fri Dec 23 02:26:21 2011
@@ -23,6 +23,8 @@ import java.io.ObjectInputStream;
 import java.security.CodeSource;
 import java.security.Permission;
 import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -34,21 +36,21 @@ import java.util.Set;
  */
 class CodeSourceSetGrant extends CertificateGrant {
     private static final long serialVersionUID = 1L;
-    private final Set<CodeSource> cs;
+    private final Collection<CodeSource> cs;
     private final int hashCode;
     
     @SuppressWarnings("unchecked")
     CodeSourceSetGrant(CodeSource[] csource, Principal[] pals, Permission[] 
perm, boolean inverse){
         super( null, pals, perm, inverse);
         int l = csource == null ? 0 : csource.length;
-        Set<CodeSource> set = new HashSet<CodeSource>(l);
+        Collection<CodeSource> list = new ArrayList<CodeSource>(l);
         int hash = 3;
         for (int i = 0 ; i < l ; i++ ){
             if ( csource[i] == null) 
                 throw new NullPointerException("CodeSource array must not 
contain null values");
-            set.add(normalizeCodeSource(csource[i]));
+            list.add(normalizeCodeSource(csource[i]));
         }
-        cs = Collections.unmodifiableSet(set);
+        cs = Collections.unmodifiableCollection(list);
         hash = 67 * hash + (this.cs != null ? this.cs.hashCode() : 0);
         hash = 67 * hash + (super.hashCode());
         hashCode = hash;

Modified: 
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/DelegateCombinerSecurityManager.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/DelegateCombinerSecurityManager.java?rev=1222530&r1=1222529&r2=1222530&view=diff
==============================================================================
--- 
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/DelegateCombinerSecurityManager.java
 (original)
+++ 
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/DelegateCombinerSecurityManager.java
 Fri Dec 23 02:26:21 2011
@@ -40,6 +40,7 @@ import java.util.concurrent.ExecutionExc
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import org.apache.river.api.delegates.DelegatePermission;
@@ -94,8 +95,9 @@ extends SecurityManager implements Deleg
         // Checks if Permission has already been checked for this context.
         Set<Permission> checkedPerms = checked.get(executionContext);
         if (checkedPerms == null){
-            Set<Referrer<Permission>> internal = new 
HashSet<Referrer<Permission>>(96);
-            checkedPerms = CollectionsConcurrent.multiReadSet(RC.set(internal, 
Ref.SOFT));
+            Set<Referrer<Permission>> internal = 
+                    CollectionsConcurrent.multiReadSet(new 
HashSet<Referrer<Permission>>(96));
+            checkedPerms = RC.set(internal, Ref.SOFT);
             Set<Permission> existed = checked.putIfAbsent(executionContext, 
checkedPerms);
             if (existed != null) checkedPerms = existed;
         }
@@ -228,8 +230,9 @@ extends SecurityManager implements Deleg
             CountDownLatch latch = new CountDownLatch(l);
             List<Future<Boolean>> resultList = null;
             Collection<Callable<Boolean>> tasks = new 
ArrayList<Callable<Boolean>>(l);
+            AtomicBoolean terminated = new AtomicBoolean(false);
             for ( int i = 0; i < l; i++ ){
-                tasks.add(new PermissionCheck(context[i], perm, latch, 
currentThread));
+                tasks.add(new PermissionCheck(context[i], perm, latch, 
currentThread, terminated));
             }
             try {
                 // We can change either call to add a timeout.
@@ -249,15 +252,26 @@ extends SecurityManager implements Deleg
                     
Logger.getLogger(DelegateCombinerSecurityManager.class.getName()).log(Level.SEVERE,
 null, ex);
                 }
             } catch (InterruptedException ex) {
-                // This is normal, it just means one task returned false.
-                // Swallow the interrupt, because it will likely be caused by
-                // a negative result, in which case a SecurityException will
-                // be thrown anyway.
+                // A task could return false, after the interruption and be
+                // interleaved before this check, it isn't foolproof.  This
+                // cannot be worked around since the exception clears the 
interrupt
+                // status.
+                // In that case the SecurityException will be thrown and the
+                // interrupt status swallowed.
+                boolean externalInterrupt = false;
+                if (!terminated.get()) externalInterrupt = true;
+                
                 Iterator<Future<Boolean>> it = resultList.iterator();
                 while (it.hasNext()){
                     it.next().cancel(true);
                 }
                 
Logger.getLogger(DelegateCombinerSecurityManager.class.getName()).log(Level.FINEST,
 null, ex);
+                // Task interruption is normal, it just means one task 
returned false.
+                // Swallow the interrupt, unless externally interrupted.
+                if (externalInterrupt){
+                    // Restore external interrupt.
+                    currentThread.interrupt();
+                }
             }
             return false;
         }
@@ -288,29 +302,33 @@ extends SecurityManager implements Deleg
         private final Permission p;
         private final CountDownLatch latch;
         private final Thread caller;
+        private final AtomicBoolean terminated;
         
-        PermissionCheck(ProtectionDomain pd, Permission p, CountDownLatch c, 
Thread caller){
+        PermissionCheck(ProtectionDomain pd, Permission p, CountDownLatch c, 
Thread caller, AtomicBoolean terminated){
             if (pd == null || p == null) throw new NullPointerException();
             this.pd = pd;
             this.p = p;
             latch = c;
             this.caller = caller;
+            this.terminated = terminated;
         }
 
         public Boolean call() throws Exception {
-            Boolean result = Boolean.FALSE;
-            if (pd.implies(p)) result = Boolean.TRUE;
-            if (p instanceof DelegatePermission ){
+            boolean result = false;
+            result = pd.implies(p);
+            if (result == false && p instanceof DelegatePermission ){
                 Permission candidate = ((DelegatePermission)p).getPermission();
-                if (pd.implies(candidate)){
-                    result = Boolean.TRUE;
-                }
+                result = pd.implies(candidate);
             }
             // If we need to check for any Permission that we have substituted
             // for a standard jvm permission, getPermissions and convert,
             // then test here for static ProtectionDomain's.
-            if (Boolean.FALSE.equals(result)) {
-                caller.interrupt();
+            if ( result == false ) {
+                // This only allows one task to terminate the caller,
+                // preventing late terminating tasks from resetting the 
interrupt.
+                // The first task to return false initiates early termination
+                // of the other tasks.
+                if (terminated.compareAndSet(false, true)) caller.interrupt();
             }
             latch.countDown();
             return result;

Modified: 
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PermissionGrantBuilder.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PermissionGrantBuilder.java?rev=1222530&r1=1222529&r2=1222530&view=diff
==============================================================================
--- 
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PermissionGrantBuilder.java
 (original)
+++ 
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PermissionGrantBuilder.java
 Fri Dec 23 02:26:21 2011
@@ -18,6 +18,8 @@
 
 package org.apache.river.api.security;
 
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.security.CodeSource;
 import java.security.Permission;
 import java.security.Principal;
@@ -77,6 +79,8 @@ public abstract class PermissionGrantBui
      */
     public static final int PRINCIPAL = 4;
     
+    public static final int URI = 5;
+    
     public static PermissionGrantBuilder newBuilder(){
         return new PermissionGrantBuilderImp();
     }
@@ -113,13 +117,17 @@ public abstract class PermissionGrantBui
     public abstract PermissionGrantBuilder codeSource(CodeSource cs);
     
     public abstract PermissionGrantBuilder multipleCodeSources();
+    
+    public abstract PermissionGrantBuilder uri(URI uri);
     /**
-     * Extracts the CodeSource, Certificates, ClassLoader and ProtectionDomain 
+     * Extracts ProtectionDomain
      * from the Class for use in the PermissionGrantBuilder.  The ClassLoader
      * and ProtectionDomain are weakly referenced, when collected any 
      * created PermissionGrant affected will be voided.
      * @param cl
      * @return PermissionGrantBuilder.
+     * @throws URISyntaxException - this exception may be swallowed if a URI
+     * grant is not required.
      */
     public abstract PermissionGrantBuilder clazz(Class cl);
     /**

Modified: 
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PermissionGrantBuilderImp.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PermissionGrantBuilderImp.java?rev=1222530&r1=1222529&r2=1222530&view=diff
==============================================================================
--- 
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PermissionGrantBuilderImp.java
 (original)
+++ 
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PermissionGrantBuilderImp.java
 Fri Dec 23 02:26:21 2011
@@ -22,15 +22,20 @@ import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.io.Serializable;
+import java.net.URISyntaxException;
 import java.util.Collection;
 import java.lang.ref.WeakReference;
+import java.net.URI;
 import java.security.CodeSource;
 import java.security.Permission;
 import java.security.Principal;
 import java.security.ProtectionDomain;
 import java.security.cert.Certificate;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 /**
  * PermissionGrantBuilderImp represents the serialized form of all
@@ -50,6 +55,7 @@ class PermissionGrantBuilderImp extends 
     private static final PermissionGrant nullGrant = new NullPermissionGrant();
     
     // Serial Form
+    private URI[] uri;
     private CodeSource cs;
     private CodeSource[] csources;
     private Certificate[] certs;
@@ -62,6 +68,7 @@ class PermissionGrantBuilderImp extends 
     
     // Transient Fields
     private transient Collection<CodeSource> multipleCodeSources;
+    private transient Collection<URI> uris;
     private transient WeakReference<ProtectionDomain> domain;
     
     PermissionGrantBuilderImp() {
@@ -73,7 +80,9 @@ class PermissionGrantBuilderImp extends 
      * Resets builder back to initial state, ready to receive new information
      * for building a new PermissionGrant.
      */
-    public PermissionGrantBuilder reset() {
+    public final PermissionGrantBuilder reset() {
+        uri = null;
+        if (uris != null) uris.clear();
         cs = null;
         certs = null;
         domain = null;
@@ -106,6 +115,13 @@ class PermissionGrantBuilderImp extends 
         this.context = context;
         return this;
     }
+    
+        @Override
+    public PermissionGrantBuilder uri(java.net.URI uri) {
+        if (this.uris == null) this.uris = new ArrayList<URI>(6);
+        this.uris.add(uri);
+        return this;
+    }
 
     public PermissionGrantBuilder codeSource(CodeSource cs) {
         if (hasMultipleCodeSources){
@@ -132,18 +148,6 @@ class PermissionGrantBuilderImp extends 
            if ( pd != null ){
                domain = new WeakReference<ProtectionDomain>(pd);
                 hasDomain = true;
-               CodeSource cdsrc = pd.getCodeSource();
-               if (cs == null) {
-                   cs = cdsrc;             
-               }
-               if (certs == null && cdsrc != null) {
-                   certs = cdsrc.getCertificates();
-               }
-                // No class should ever have any Principal's in it's 
ProtectionDomain
-                // Only a DomainCombiner should add principals.
-//             if (principals == null) {
-//                 principals = pd.getPrincipals();
-//             }
             }
         }
         return this;
@@ -178,6 +182,10 @@ class PermissionGrantBuilderImp extends 
                 // are treated special.
                 if (inverse) throw new UnsupportedOperationException("Inverse 
ClassLoader permissions not implemented");
                 return new ClassLoaderGrant(domain, principals, permissions );
+            case URI:
+                if (uris != null && !uris.isEmpty() ) uri = uris.toArray(new 
URI[uris.size()]);
+                if (uri == null ) uri = new URI[0];
+                return new URIGrant(uri, certs, principals, permissions, 
inverse);              
             case CODESOURCE:
                 if (hasMultipleCodeSources) {
                     if (multipleCodeSources != null) csources = 
@@ -216,6 +224,7 @@ class PermissionGrantBuilderImp extends 
                     multipleCodeSources.toArray(new 
CodeSource[multipleCodeSources.size()]);
             cs = null;
         }
+        if (uris != null && !uris.isEmpty()) uri = uris.toArray(new 
URI[uris.size()]);
         out.defaultWriteObject();
     }
     

Modified: 
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/ProtectionDomainGrant.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/ProtectionDomainGrant.java?rev=1222530&r1=1222529&r2=1222530&view=diff
==============================================================================
--- 
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/ProtectionDomainGrant.java
 (original)
+++ 
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/ProtectionDomainGrant.java
 Fri Dec 23 02:26:21 2011
@@ -21,10 +21,17 @@ package org.apache.river.api.security;
 import java.io.InvalidObjectException;
 import java.io.ObjectInputStream;
 import java.lang.ref.WeakReference;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
 import java.security.CodeSource;
 import java.security.Permission;
 import java.security.Principal;
 import java.security.ProtectionDomain;
+import java.security.cert.Certificate;
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 /**
  * ProtectionDomainGrant's become void if serialized, since ProtectionDomain's
@@ -115,7 +122,8 @@ class ProtectionDomainGrant extends Prin
         if (pd == null) return false;       
         if (domain.get() == null ) return false; // grant is void.
         if ( pd.equals(domain.get())) return true; // pd not null fast 
reference comparison
-        if ( impliesClassLoader(pd.getClassLoader()) && 
impliesCodeSource(pd.getCodeSource()))
+        if ( impliesClassLoader(pd.getClassLoader()) 
+                && impliesCodeSource(pd.getCodeSource()))
         {
             return true;
         }
@@ -139,11 +147,31 @@ class ProtectionDomainGrant extends Prin
     private boolean impliesCodeSource(CodeSource codeSource) {
         ProtectionDomain pd = domain != null ? domain.get(): null;
         if (pd == null) return false; // is void - why did I have true?
-        CodeSource cs = normalizeCodeSource(pd.getCodeSource());
+        // The CodeSource is not normalized, if the PD was created by a domain
+        // combiner it is likely that the CodeSource will have the same 
referent.
+        CodeSource cs = pd.getCodeSource(); // Don't normalise CodeSource.
         if (cs == codeSource) return true; // same reference.
-        if (cs == null && codeSource == null) return true; // if both null, 
whe pd exists.
+        if (cs == null && codeSource == null) return true; // if both null, 
when pd exists.
         if (cs == null) return false; // Null cs indicates system domain, does 
not imply.
-        return cs.implies(normalizeCodeSource(codeSource));
+        // Most won't get to here, since domain combiners shouldn't duplicate 
CodeSource.
+        // But just in case...
+        // Don't use CodeSource.equals() because that causes DNS Lookup.
+        Certificate[] myCerts = cs.getCertificates();
+        Certificate[] hisCerts = codeSource.getCertificates();
+        if ( myCerts != null && !Arrays.equals(myCerts, hisCerts)) return 
false;
+        try {
+            URI myLocation = cs.getLocation().toURI();
+            URI hisLocation = codeSource.getLocation().toURI();
+            if (myLocation.equals(hisLocation)) return true;
+        } catch (URISyntaxException ex) {
+            // We only compare URL if we can't compare URI
+            URL myLocation = cs.getLocation();
+            URL hisLocation = codeSource.getLocation();
+            // Only use string representation to compare, DNS cache poisioning
+            // presents a security risk, so URL.equals isn't used.
+            if 
(myLocation.toExternalForm().equals(hisLocation.toExternalForm())) return true;
+        }
+        return false;
     }
 
     @Override

Copied: 
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/URIGrant.java
 (from r1187844, 
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/CodeSourceGrant.java)
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/URIGrant.java?p2=river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/URIGrant.java&p1=river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/CodeSourceGrant.java&r1=1187844&r2=1222530&rev=1222530&view=diff
==============================================================================
--- 
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/CodeSourceGrant.java
 (original)
+++ 
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/URIGrant.java
 Fri Dec 23 02:26:21 2011
@@ -20,25 +20,38 @@ package org.apache.river.api.security;
 
 import java.io.InvalidObjectException;
 import java.io.ObjectInputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
 import java.security.CodeSource;
 import java.security.Permission;
 import java.security.Principal;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
 
 /**
  *
  * @author Peter Firmstone
  */
-class CodeSourceGrant extends CertificateGrant {
+class URIGrant extends CertificateGrant {
     private static final long serialVersionUID = 1L;
-    private final CodeSource cs;
+    private final Collection<URI> location;
     private final int hashCode;
     
     @SuppressWarnings("unchecked")
-    CodeSourceGrant(CodeSource cs, Principal[] pals, Permission[] perm, 
boolean inverse ){
-        super( cs != null? cs.getCertificates(): null, pals, perm, inverse);
-        this.cs = cs != null? normalizeCodeSource(cs) : null;
+    URIGrant(URI[] uri, Certificate[] certs, Principal[] pals, Permission[] 
perm, boolean inverse ){
+        super( certs, pals, perm, inverse);
+        int l = uri.length;
+        Collection<URI> uris = new ArrayList<URI>(l);
+        for ( int i = 0; i < l ; i++ ){
+            uris.add(uri[i] != null ? uri[i].normalize() : null);
+        }
+        location = Collections.unmodifiableCollection(uris);
         int hash = 3;
-        hash = 67 * hash + (this.cs != null ? this.cs.hashCode() : 0);
+        hash = 67 * hash + (this.location != null ? uri.hashCode() : 0);
         hash = 67 * hash + (super.hashCode());
         hashCode = hash;
     }
@@ -53,13 +66,11 @@ class CodeSourceGrant extends Certificat
         if (o == null) return false;
         if (o == this) return true;
        if (o.hashCode() != this.hashCode()) return false;
-        if (o instanceof CodeSourceGrant){
-            CodeSourceGrant c = (CodeSourceGrant) o;
+        if (o instanceof URIGrant){
+            URIGrant c = (URIGrant) o;
             if ( !super.equals(o)) return false;
-           if ( cs == c.cs) return true;
-           if ( cs != null ) {
-               if (cs.equals(c.cs)) return true;
-           }
+           if ( location == c.location) return true;
+           if ( location.equals(c.location)) return true;
         }
         return false;
     }
@@ -68,40 +79,321 @@ class CodeSourceGrant extends Certificat
     public String toString(){
         StringBuilder sb = new StringBuilder(500);
         return sb.append(super.toString())
-                 .append("CodeSource: \n")
-                 .append( cs == null ? "null" : cs.toString())
+                 .append("URI: \n")
+                 .append(location.toString())
                  .toString();
     }
     
     @Override
     public boolean implies(ClassLoader cl, Principal[] p){
        if ( !implies(p)) return false;
-       if ( cs == null ) return true;
+       if ( location.isEmpty() ) return true;
+        Iterator<URI> it = location.iterator();
+        while (it.hasNext()){
+            if (it.next() == null) return true;
+        }
        if ( cl == null ) return false;
        return false;  //Indeterminate.
     }
     
     
     /**
-     * Checks if passed CodeSource matches this PermissionGrant. Null 
CodeSource of
-     * PermissionGrant implies any CodeSource; non-null CodeSource forwards to 
its
-     * imply() method.
+     * Checks if passed CodeSource matches this PermissionGrant. Null URI of
+     * PermissionGrant implies any CodeSource, only if that CodeSource is not 
+     * null.
      */
     @Override
     public boolean implies(CodeSource codeSource, Principal[] p) {
         if ( !implies(p)) return false;
-        // sun.security.provider.PolicyFile compatibility for null CodeSource.
+        // sun.security.provider.PolicyFile compatibility for null CodeSource 
is false.
         // see 
com.sun.jini.test.spec.policyprovider.dynamicPolicyProvider.GrantPrincipal test.
-        if ( codeSource == null ) return false; 
-       if ( cs == null || nullCS.equals(cs)) return true;
-       return cs.implies(normalizeCodeSource(codeSource));
+        if (codeSource == null)  return false;
+        if (location.isEmpty()) return true;
+        int l = location.size();
+        URI[] uris = location.toArray(new URI[l]);
+        for (int i = 0; i<l ; i++ ){
+            if (uris[i] == null) return true;
+        }
+        for (int i = 0; i<l ; i++){
+            if (implies(uris[i], codeSource)) return true;
         }
+        return false;
+    }
+    
+    /* This section of code was copied from Apache Harmony's CodeSource
+     * SVN Revision 929252
+     * 
+     * 
+     * Indicates whether the specified code source is implied by this {@code
+     * URI}. Returns {@code true} if all of the following conditions are
+     * {@code true}, otherwise {@code false}:
+     * <p>
+     * <ul>
+     * <li>{@code cs} is not {@code null}
+     * <li>if this {@code CodeSource}'s location is not {@code null}, the
+     * following conditions are checked
+     * <ul>
+     * <li>this {@code CodeSource}'s location is not {@code null}
+     * <li>this {@code CodeSource}'s location protocol is equal to {@code cs}'s
+     * location protocol
+     * <li>if this {@code CodeSource}'s location host is not {@code null}, the
+     * following conditions are checked
+     * <ul>
+     * <li>{@code cs}'s host is not {@code null}
+     * <li>the wildcard or partial wildcard of this {@code URI}'s
+     * host matches {@code cs}'s location host.
+     * </ul>
+     * <li>if this {@code CodeSource}'s location port != -1 the port of {@code
+     * cs}'s location is equal to this {@code CodeSource}'s location port
+     * <li>this {@code CodeSource}'s location file matches {@code cs}'s file
+     * whereas special wildcard matching applies as described below
+     * <li>this {@code CodeSource}'s location reference is equal to to {@code
+     * cs}'s location reference
+     * </ul>
+     * </ul>
+     * <p>
+     * Note: If this {@code CodeSource} has a {@code null} location,
+     * this method returns {@code true}.
+     * <p>
+     * Matching rules for the {@code CodeSource}'s location file:
+     * <ul>
+     * <li>if this {@code CodeSource}'s location file ends with {@code "/-"},
+     * then {@code cs}'s file must start with {@code CodeSource}'s location 
file
+     * (exclusive the trailing '-')
+     * <li>if this {@code CodeSource}'s location file ends with {@code "/*"},
+     * then {@code cs}'s file must start with {@code CodeSource}'s location 
file
+     * (exclusive the trailing '*') and must not have any further '/'
+     * <li>if this {@code CodeSource}'s location file ends with {@code "/"},
+     * then {@code cs}'s file must start with {@code CodeSource}'s location 
file
+     * <li>if this {@code CodeSource}'s location file does not end with {@code
+     * "/"}, then {@code cs}'s file must start with {@code CodeSource}'s
+     * location file with the '/' appended to it.
+     * </ul>
+     * Examples for locations that imply the location
+     * "http://harmony.apache.org/milestones/M9/apache-harmony.jar":
+     *
+     * <pre>
+     * http:
+     * http://&#42;/milestones/M9/*
+     * http://*.apache.org/milestones/M9/*
+     * http://harmony.apache.org/milestones/-
+     * http://harmony.apache.org/milestones/M9/apache-harmony.jar
+     * </pre>
+     *
+     * @param cs
+     *            the code source to check.
+     * @return {@code true} if the argument code source is implied by this
+     *         {@code CodeSource}, otherwise {@code false}.
+     */
+    private static boolean implies(URI location, CodeSource cs) {
+        //
+        // Here, javadoc:N refers to the appropriate item in the API spec for 
+        // the CodeSource.implies()
+        // The info was taken from the 1.5 final API spec
+
+        // javadoc:1
+//        if (cs == null) {
+//            return false;
+//        }
+
+        /* Certificates can safely be ignored, they're checked by 
CertificateGrant */
+        
+        // javadoc:2
+        // with a comment: the javadoc says only about certificates and does 
+        // not explicitly mention CodeSigners' certs.
+        // It seems more convenient to use getCerts() to get the real 
+        // certificates - with a certificates got form the signers
+//        Certificate[] thizCerts = getCertificatesNoClone();
+//        if (thizCerts != null) {
+//            Certificate[] thatCerts = cs.getCertificatesNoClone();
+//            if (thatCerts == null
+//                    || !PolicyUtils.matchSubset(thizCerts, thatCerts)) {
+//                return false;
+//            }
+//        }
+
+        // javadoc:3
+        if (location != null) {
+            
+            //javadoc:3.1
+            URL otherURL = cs.getLocation();
+            if ( otherURL == null) {
+                return false;
+            }
+            URI otherURI;
+            try {
+                otherURI = otherURL.toURI();
+            } catch (URISyntaxException ex) {
+                return false;
+            }
+            //javadoc:3.2
+            if (location.equals(otherURI)) {
+                return true;
+            }
+            //javadoc:3.3
+            if 
(!location.getSchemeSpecificPart().equals(otherURI.getSchemeSpecificPart())) {
+                return false;
+            }
+            //javadoc:3.4
+            String thisHost = location.getHost();
+            if (thisHost != null) {
+                String thatHost = otherURI.getHost();
+                if (thatHost == null) {
+                    return false;
+                }
+
+                // 1. According to the spec, an empty string will be 
considered 
+                // as "localhost" in the SocketPermission
+                // 2. 'file://' URLs will have an empty getHost()
+                // so, let's make a special processing of localhost-s, I do 
+                // believe this'll improve performance of file:// code sources 
+
+                //
+                // Don't have to evaluate both the boolean-s each time.
+                // It's better to evaluate them directly under if() statement.
+                // 
+                // boolean thisIsLocalHost = thisHost.length() == 0 || 
"localhost".equals(thisHost);
+                // boolean thatIsLocalHost = thatHost.length() == 0 || 
"localhost".equals(thatHost);
+                // 
+                // if( !(thisIsLocalHost && thatIsLocalHost) &&
+                // !thisHost.equals(thatHost)) {
+
+                if (!((thisHost.length() == 0 || "localhost".equals(thisHost)) 
&& (thatHost //$NON-NLS-1$
+                        .length() == 0 || "localhost".equals(thatHost))) 
//$NON-NLS-1$
+                        && !thisHost.equals(thatHost)) {
+                    
+                    // Do wildcard matching here to replace SocketPermission 
functionality.
+                    // This section was copied from Apache Harmony 
SocketPermission
+                    boolean hostNameMatches = false;
+                    boolean isPartialWild = (thisHost.charAt(0) == '*');
+                    if (isPartialWild) {
+                        boolean isWild = (thisHost.length() == 1);
+                        if (isWild) {
+                            hostNameMatches = true;
+                        } else {
+                            // Check if thisHost matches the end of thatHost 
after the wildcard
+                            int length = thisHost.length() - 1;
+                            hostNameMatches = 
thatHost.regionMatches(thatHost.length() - length,
+                                    thisHost, 1, length);
+                        }
+                    }
+                    if (!hostNameMatches) return false; // else continue.
+                    
+                    /* Don't want to try resolving URIGrant, it either has a
+                     * matching host or it doesn't.
+                     * 
+                     * The following section is for resolving hosts, it is
+                     * not relevant here, but has been preserved for 
information
+                     * purposes only.
+                     * 
+                     * Not only is it expensive to perform DNS resolution, 
hence
+                     * the creation of URIGrant, but a CodeSource.implies
+                     * may also require another SocketPermission which may 
+                     * cause the policy to get stuck in an endless loop, since 
it
+                     * doesn't perform the implies in priviledged mode, it 
might
+                     * also allow an attacker to substitute one codebase for
+                     * another using a dns cache poisioning attack.  In any 
case
+                     * the DNS cannot be assumed trustworthy enough to supply
+                     * the policy with information at this level. The 
implications
+                     * are greater than the threat posed by SocketPermission
+                     * which simply allows a network connection, as this may
+                     * apply to any Permission, even AllPermission.
+                     * 
+                     * Typically the URI of the codebase will be a match for
+                     * the codebase annotation string that is stored as a URL
+                     * in CodeSource, then converted to a URI for comparison.
+                     */
+
+                    // Obvious, but very slow way....
+                    // 
+                    // SocketPermission thisPerm = new SocketPermission(
+                    //          this.location.getHost(), "resolve");
+                    // SocketPermission thatPerm = new SocketPermission(
+                    //          cs.location.getHost(), "resolve");
+                    // if (!thisPerm.implies(thatPerm)) { 
+                    //      return false;
+                    // }
+                    //
+                    // let's cache it: 
+
+//                    if (this.sp == null) {
+//                        this.sp = new SocketPermission(thisHost, "resolve"); 
//$NON-NLS-1$
+//                    }
+//
+//                    if (cs.sp == null) {
+//                        cs.sp = new SocketPermission(thatHost, "resolve"); 
//$NON-NLS-1$
+//                    } 
+//
+//                    if (!this.sp.implies(cs.sp)) {
+//                        return false;
+//                    }
+                    
+                } // if( ! 
this.location.getHost().equals(cs.location.getHost())
+            } // if (this.location.getHost() != null)
+
+            //javadoc:3.5
+            if (location.getPort() != -1) {
+                if (location.getPort() != otherURI.getPort()) {
+                    return false;
+                }
+            }
+
+            //javadoc:3.6
+            // for compatbility with URL.getFile, the Query is concatenated to 
the path.
+            String thisFile = location.getPath() + location.getQuery();
+            String thatFile = otherURI.getPath() + otherURI.getQuery();
+
+            if (thisFile.endsWith("/-")) { //javadoc:3.6."/-" //$NON-NLS-1$
+                if (!thatFile.startsWith(thisFile.substring(0, thisFile
+                        .length() - 2))) {
+                    return false;
+                }
+            } else if (thisFile.endsWith("/*")) { //javadoc:3.6."/*" 
//$NON-NLS-1$
+                if (!thatFile.startsWith(thisFile.substring(0, thisFile
+                        .length() - 2))) {
+                    return false;
+                }
+                // no further separators(s) allowed
+                if (thatFile.indexOf("/", thisFile.length() - 1) != -1) { 
//$NON-NLS-1$
+                    return false;
+                }
+            } else {
+                // javadoc:3.6."/"
+                if (!thisFile.equals(thatFile)) {
+                    if (!thisFile.endsWith("/")) { //$NON-NLS-1$
+                        if (!thatFile.equals(thisFile + "/")) { //$NON-NLS-1$
+                            return false;
+                        }
+                    } else {
+                        return false;
+                    }
+                }
+            }
+
+            //javadoc:3.7
+            // A URL Anchor is a URI Fragment.
+            if (location.getFragment() != null) {
+                if (!location.getFragment().equals(otherURI.getFragment())) {
+                    return false;
+                }
+            }
+            // ok, every check was made, and they all were successful. 
+            // it's ok to return true.
+        } // if this.location != null
+
+        // javadoc: a note about CodeSource with null location and null Certs 
+        // is applicable here 
+        return true;
+    }
 
     @Override
     public PermissionGrantBuilder getBuilderTemplate() {
         PermissionGrantBuilder pgb = super.getBuilderTemplate();
-        pgb.codeSource(cs)
-           .context(PermissionGrantBuilder.CODESOURCE);
+        Iterator<URI> it = location.iterator();
+        while (it.hasNext()){
+            pgb.uri(it.next());
+        }
+        pgb.context(PermissionGrantBuilder.URI);
         return pgb;
     }
     

Modified: 
river/jtsk/skunk/peterConcurrentPolicy/test/src/org/apache/river/api/security/DelegateCombinerSecurityManagerTest.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/test/src/org/apache/river/api/security/DelegateCombinerSecurityManagerTest.java?rev=1222530&r1=1222529&r2=1222530&view=diff
==============================================================================
--- 
river/jtsk/skunk/peterConcurrentPolicy/test/src/org/apache/river/api/security/DelegateCombinerSecurityManagerTest.java
 (original)
+++ 
river/jtsk/skunk/peterConcurrentPolicy/test/src/org/apache/river/api/security/DelegateCombinerSecurityManagerTest.java
 Fri Dec 23 02:26:21 2011
@@ -63,7 +63,7 @@ public class DelegateCombinerSecurityMan
         try {
             sm = new DelegateCombinerSecurityManager();
             CodeSource cs0, cs10, cs11, cs12;
-            p1 = new SocketPermission("river.apache.org:80", "connect,accept");
+            p1 = new SocketPermission("*", "connect,accept");
             p2 = DelegatePermission.get(p1);
             p3 = new RuntimePermission("readFileDescriptor");
             p4 = DelegatePermission.get(p3);


Reply via email to