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 2012/06/29 15:01:38 UTC

svn commit: r1355351 [2/2] - in /river/jtsk/trunk: ./ qa/src/com/sun/jini/qa/harness/ qa/src/com/sun/jini/test/spec/security/basicproxypreparer/ src/com/sun/jini/start/ src/net/jini/security/policy/ src/org/apache/river/api/security/ test/src/org/apach...

Added: river/jtsk/trunk/src/org/apache/river/api/security/DelegatePermission.java
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/src/org/apache/river/api/security/DelegatePermission.java?rev=1355351&view=auto
==============================================================================
--- river/jtsk/trunk/src/org/apache/river/api/security/DelegatePermission.java (added)
+++ river/jtsk/trunk/src/org/apache/river/api/security/DelegatePermission.java Fri Jun 29 13:01:32 2012
@@ -0,0 +1,395 @@
+/*
+ * 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.api.security;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentSkipListMap;
+import au.net.zeus.collection.RC;
+import au.net.zeus.collection.Ref;
+import au.net.zeus.collection.Referrer;
+import java.io.StreamTokenizer;
+import java.io.StringReader;
+import java.util.ArrayList;
+import org.apache.river.api.security.DefaultPolicyScanner.PermissionEntry;
+
+/**
+ * A DelegatePermission represents any other Permission, called a candidate
+ * Permission.  A user granted a DelegatePermission does not have the privilege
+ * of the candidate Permission, although a user with a candidate Permission
+ * has the privilege of the DelegatePermission that represents the candidate, 
+ * while the @ref DelegateSecurityManager is in force.
+ * 
+ * A DelegatePermission requires a security delegate to be of any
+ * use or value.  A security delegates ProtectionDomain is granted the 
+ * candidate permission, the security delegate allows any user granted the
+ * DelegatePermission to utilise the functions that the candidate Permission
+ * guards, when the user no longer has the DelegatePermission, the security
+ * delegate no longer allows the user to access the functions guarded by the
+ * candidate permission.  A security delegate has the responsibility to
+ * prevent security sensitive objects guarded by the candidate permission from
+ * escaping.  In order to do so, a security delegate utilises Li Gong's 
+ * method guard pattern.
+ *
+ * Security Delegates enable sensitive objects to be used by code that isn't
+ * fully trusted you may want to monitor, such as a 
+ * file write that is limited by the number of bytes written, or a Permission
+ * to write a file, that we might decide to retract or revoke if a user
+ * does something we don't like, such as exceed a pre set limit or behave
+ * in a manner we would like to avoid, such as hogging network bandwidth.
+ * 
+ * If the SecurityManager installed doesn't implement DelegateSecurityManager,
+ * the DelegatePermission Guard's will be disabled.  This allows delegate's
+ * to be included in code, the decision to utilise delegate functionality may
+ * delayed until runtime or deployment.
+ * 
+ * The DelegatePermissionCollection returned by newPermissionCollection() is not
+ * synchronized, this decision was made because PermissionCollection's are 
+ * usually accessed from within a heterogenous PermissionCollection like 
+ * Permissions that synchronizes anyway.  The decision made for the
+ * PermissionCollection contract to be synchronized has been broken deliberately
+ * in this case, existing PermissionCollection implementatations don't cleanly
+ * protect their internal state with synchronization, since the Enumeration
+ * returned by elements() will throw a ConcurrentModificationException if in a 
+ * loop when Permission's are being added to a PermissionCollection.  In this
+ * case external synchronization must be used.
+ * 
+ * PermissionCollection's are used mostly read only.
+ * 
+ * Serialization has been implemented so the implementation is not
+ * tied to the serialized form, instead serialization proxy's are used.
+ * 
+ * @author Peter Firmstone
+ */
+public final class DelegatePermission extends Permission{
+    private static final long serialVersionUID = 1L;
+    /* Object Pool ensures that equals performs very well in collections for 
+     * optimum AccessControlContext result caching and minimises memory 
+     * consumption.
+     */
+    @SuppressWarnings("unchecked")
+    private static final ConcurrentMap instances 
+        = RC.concurrentMap( new ConcurrentSkipListMap( 
+            RC.comparator( new PermissionComparator()))
+            , Ref.WEAK, Ref.WEAK, 1000L, 1000L ); // Value weak too, because it references key.
+        
+    /**
+     * Factory method to obtain a DelegatePermission, this is essential to 
+     * overcome broken equals contract in some jvm Permission implementations
+     * like SocketPermission and to allow caching.
+     * 
+     * @param p Permission to be represented.
+     * @return DelegatePermission
+     */
+    public static Permission get(Permission p){
+	Permission del = (Permission) instances.get(p);
+	if ( del == null ){
+	    del = new DelegatePermission(p);
+            @SuppressWarnings("unchecked")
+	    Permission existed = (Permission) instances.putIfAbsent(p, del);
+	    if ( existed != null ){
+		del = existed;
+	    }
+	}
+	return del;
+    }
+    
+    private final Permission permission;
+//    private final transient int hashCode;
+    
+    private DelegatePermission(Permission p){
+	super(p.getName());
+	permission = p;
+//	int hash = 5;
+//	hash = 41 * hash + (this.permission != null ? this.permission.hashCode() : 0);
+//	hashCode = hash;
+    }
+    
+    /**
+     * Parses permission information from given GrantPermission name string.
+     * Throws an IllegalArgumentException if the name string is misformatted.
+     */
+    private static PermissionEntry[] parsePermissions(String s) {
+	try {
+	    ArrayList l = new ArrayList();
+	    StreamTokenizer st = createTokenizer(s);
+	    char delim = '"';
+
+	    if (st.nextToken() == StreamTokenizer.TT_WORD &&
+		st.sval.equals("delim"))
+	    {
+		if (st.nextToken() == '=') {
+		    if (st.nextToken() == StreamTokenizer.TT_WORD) {
+			if (st.sval.length() > 1) {
+			    throw new IllegalArgumentException(
+				"excess delimiter characters");
+			}
+			delim = st.sval.charAt(0);
+		    } else {
+			delim = (char) st.ttype;
+		    }
+		    if (delim == ';') {
+			throw new IllegalArgumentException(
+			    "illegal delimiter ';'");
+		    }
+		} else {	// rewind
+		    st = createTokenizer(s);
+		}
+		st.nextToken();
+	    }
+	    st.quoteChar(delim);
+
+	    do {
+		String type, name = null, actions = null;
+
+		if (st.ttype != StreamTokenizer.TT_WORD) {
+		    throw new IllegalArgumentException(
+			"expected permission type");
+		}
+		type = st.sval;
+		
+		// REMIND: allow unquoted name/actions?
+		st.nextToken();
+		if (st.ttype == StreamTokenizer.TT_EOF || st.ttype == ';') {
+		    l.add(new PermissionEntry(type, null, null, null));
+		    continue;
+		} else if (st.ttype == delim) {
+		    name = st.sval;
+		} else {
+		    throw new IllegalArgumentException(
+			"expected permission name or ';'");
+		}
+		
+		st.nextToken();
+		if (st.ttype == StreamTokenizer.TT_EOF || st.ttype == ';') {
+		    l.add(new PermissionEntry(type, name, null, null));
+		    continue;
+		} else if (st.ttype != ',') {
+		    throw new IllegalArgumentException("expected ',' or ';'");
+		}
+
+		if (st.nextToken() != delim) {
+		    throw new IllegalArgumentException(
+			"expected permission actions");
+		}
+		actions = st.sval;
+		
+		st.nextToken();
+		if (st.ttype == StreamTokenizer.TT_EOF || st.ttype == ';') {
+		    l.add(new PermissionEntry(type, name, actions, null));
+		    continue;
+		} else {
+		    throw new IllegalArgumentException("expected ';'");
+		}
+
+	    } while (st.nextToken() != StreamTokenizer.TT_EOF);
+
+	    return (PermissionEntry[]) l.toArray(new PermissionEntry[l.size()]);
+	} catch (IOException ex) {
+	    throw (Error) new InternalError().initCause(ex);
+	}
+    }
+
+    /**
+     * Returns tokenizer for parsing given string.  The tokenizer is configured
+     * similarly to that used by sun.security.provider.PolicyParser, except
+     * that comments are disabled and no quote character is set (yet).
+     */
+    private static StreamTokenizer createTokenizer(String s) {
+	StreamTokenizer st = new StreamTokenizer(new StringReader(s));
+	st.resetSyntax();
+	st.wordChars('a', 'z');
+	st.wordChars('A', 'Z');
+	st.wordChars('.', '.');
+	st.wordChars('0', '9');
+	st.wordChars('_', '_');
+	st.wordChars('$', '$');
+	st.wordChars(128 + 32, 255);
+	st.whitespaceChars(0, ' ');
+	st.lowerCaseMode(false);
+	st.ordinaryChar('/');
+	st.slashSlashComments(false);
+	st.slashStarComments(false);
+	return st;
+    }
+    
+    public void checkGuard(Object object) throws SecurityException {
+	SecurityManager sm = System.getSecurityManager();
+	if (sm instanceof DelegateSecurityManager) sm.checkPermission(this);
+    }
+
+    @Override
+    // This is implemented but never called.
+    public boolean implies(Permission permission) {
+	if (permission == null) return false;
+	if (!(permission instanceof DelegatePermission)) return false;
+	if ( permission.getClass() != this.getClass()) return false;
+	return this.permission.implies(((DelegatePermission) permission).getPermission());
+    }
+    
+    public Permission getPermission(){
+	return permission;
+    }
+
+    // Don't override equals so all Delegates can be used in Collections
+    // including those containing SocketPermission.
+    @Override
+    public boolean equals(Object obj) {
+        return obj == this;
+//	if (obj == this) return true;
+//	if (obj == null) return false;
+//	if ( obj.hashCode() != hashCode ) return false;
+//	if (!(obj instanceof DelegatePermission)) return false;
+//	if ( obj.getClass() != this.getClass() ) return false;
+//	return permission.equals(((DelegatePermission) permission).getPermission());
+    }
+
+    @Override
+    public int hashCode() {
+//	return hashCode;
+        // Not in constructor so we don't let this escape.
+        return System.identityHashCode(this);
+    }
+
+    @Override
+    public String getActions() {
+	return "";
+    }
+    
+    @Override
+    public PermissionCollection newPermissionCollection() {
+	return new DelegatePermissionCollection();
+    }
+    
+    /* Serialization Proxy */
+    private static class SerializationProxy implements Serializable {
+	private static final long serialVersionUID = 1L;
+	private Permission perm;
+	
+	SerializationProxy(Permission p){
+	    perm = p;
+	}
+        
+        private void writeObject(ObjectOutputStream out) throws IOException{
+            out.defaultWriteObject();
+        }
+        
+        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{
+            in.defaultReadObject();
+        }
+        
+        private Object readResolve() {
+            // perm is the field from the Serialization proxy.
+            return get(perm);
+        }
+    }
+    
+    /* Serialization */
+    
+    private Object writeReplace() {
+	return new SerializationProxy(permission);
+    }
+    
+    private void readObject(ObjectInputStream in) throws InvalidObjectException{
+	throw new InvalidObjectException("Proxy required");
+    }
+    
+    /* PermissionCollection */
+    
+    private static class DelegatePermissionCollection extends PermissionCollection {
+	private static final long serialVersionUID = 1L;
+	private final transient PermissionCollection candidates;
+	private final Set<Permission> delegates;
+	
+	DelegatePermissionCollection(){
+	    candidates = new Permissions();
+	    delegates = new HashSet<Permission>(32);
+	}
+
+	@Override
+	public void add(Permission permission) {
+	    if (! (permission instanceof DelegatePermission))
+	    throw new IllegalArgumentException("invalid permission: "+ permission);
+	if (isReadOnly())
+	    throw new SecurityException("attempt to add a Permission to a " +
+		    "readonly PermissionCollection");
+	    delegates.add(permission);
+	    candidates.add(((DelegatePermission) permission).getPermission());
+	}
+
+	@Override
+	public boolean implies(Permission permission) {
+	    if ( !(permission instanceof DelegatePermission)) return false;
+	    return candidates.implies(
+		    ((DelegatePermission )permission).getPermission());
+	}
+
+	@Override
+	public Enumeration<Permission> elements() {
+	    return Collections.enumeration(delegates);
+	}
+	
+	/* Serialization Proxy */
+	 private static class CollectionSerializationProxy implements Serializable {
+	    private static final long serialVersionUID = 1L;
+	    private Permission[] perms;
+
+	    CollectionSerializationProxy(Set<Permission> p){
+		perms = p.toArray(new Permission[p.size()]);
+	    }
+            
+            private Object readResolve() {
+                // perms is the field from the Serialization proxy.
+                DelegatePermissionCollection dpc = new DelegatePermissionCollection();
+                int l = perms.length;
+                for (int i = 0; i < l; i++ ){
+                    dpc.add(perms[i]);
+                }
+                return dpc;
+            }
+
+	}
+
+	/* Serialization */
+
+	private Object writeReplace() {
+	    return new CollectionSerializationProxy(delegates);
+	}
+
+	private void readObject(ObjectInputStream in) throws InvalidObjectException{
+	    throw new InvalidObjectException("Proxy required");
+	}
+
+	
+	
+    }
+
+}

Propchange: river/jtsk/trunk/src/org/apache/river/api/security/DelegatePermission.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: river/jtsk/trunk/src/org/apache/river/api/security/DelegateSecurityManager.java
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/src/org/apache/river/api/security/DelegateSecurityManager.java?rev=1355351&view=auto
==============================================================================
--- river/jtsk/trunk/src/org/apache/river/api/security/DelegateSecurityManager.java (added)
+++ river/jtsk/trunk/src/org/apache/river/api/security/DelegateSecurityManager.java Fri Jun 29 13:01:32 2012
@@ -0,0 +1,57 @@
+/*
+ * 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.api.security;
+
+import java.security.Permission;
+import java.security.ProtectionDomain;
+
+/**
+ * The DelegateSecurityManager is designed to enable the use of 
+ * Delegate decorators to encapsulate security sensitive objects using
+ * Li Gong's method guard pattern.
+ * 
+ * In this manner we can prevent references to security sensitive object's from 
+ * escaping.
+ * 
+ * See "Inside Java 2 Platform Security" 2nd Edition, ISBN:0-201-78791-1, page 176.
+ * 
+ * Delegate implementations are available separately from the Apache River
+ * release.
+ * 
+ * Delegates can be enabled at runtime by using the DelegateSecurityManager,
+ * but only for code that utilises delegates.
+ * 
+ * @see DelegatePermission.
+ * @author Peter Firmstone
+ */
+public class DelegateSecurityManager extends CombinerSecurityManager {
+
+    public DelegateSecurityManager(){
+        super();
+    }
+    
+    protected boolean checkPermission(ProtectionDomain pd, Permission p){
+        boolean result = pd.implies(p);
+        if (!result && p instanceof DelegatePermission ){
+            Permission candidate = ((DelegatePermission)p).getPermission();
+            result = pd.implies(candidate);
+        }
+        return result;
+    }
+}

Propchange: river/jtsk/trunk/src/org/apache/river/api/security/DelegateSecurityManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: river/jtsk/trunk/src/org/apache/river/api/security/PermissionGrantBuilder.java
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/src/org/apache/river/api/security/PermissionGrantBuilder.java?rev=1355351&r1=1355350&r2=1355351&view=diff
==============================================================================
--- river/jtsk/trunk/src/org/apache/river/api/security/PermissionGrantBuilder.java (original)
+++ river/jtsk/trunk/src/org/apache/river/api/security/PermissionGrantBuilder.java Fri Jun 29 13:01:32 2012
@@ -18,11 +18,13 @@
 
 package org.apache.river.api.security;
 
+import java.lang.ref.WeakReference;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.security.CodeSource;
 import java.security.Permission;
 import java.security.Principal;
+import java.security.ProtectionDomain;
 import java.security.cert.Certificate;
 import javax.security.auth.Subject;
 
@@ -186,4 +188,11 @@ public abstract class PermissionGrantBui
      * @return an appropriate PermissionGrant.
      */
     public abstract PermissionGrant build();
+
+    /**
+     * 
+     * @param domain
+     * @return
+     */
+    public abstract PermissionGrantBuilder setDomain(WeakReference<ProtectionDomain> domain);
 }

Modified: river/jtsk/trunk/src/org/apache/river/api/security/PermissionGrantBuilderImp.java
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/src/org/apache/river/api/security/PermissionGrantBuilderImp.java?rev=1355351&r1=1355350&r2=1355351&view=diff
==============================================================================
--- river/jtsk/trunk/src/org/apache/river/api/security/PermissionGrantBuilderImp.java (original)
+++ river/jtsk/trunk/src/org/apache/river/api/security/PermissionGrantBuilderImp.java Fri Jun 29 13:01:32 2012
@@ -143,7 +143,7 @@ class PermissionGrantBuilderImp extends 
         return this;
     }
 
-    PermissionGrantBuilder setDomain(WeakReference<ProtectionDomain> pd) {
+    public PermissionGrantBuilder setDomain(WeakReference<ProtectionDomain> pd) {
         domain = pd;
         if ( domain != null) hasDomain = true;
         return this;

Modified: river/jtsk/trunk/src/org/apache/river/api/security/PrincipalGrant.java
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/src/org/apache/river/api/security/PrincipalGrant.java?rev=1355351&r1=1355350&r2=1355351&view=diff
==============================================================================
--- river/jtsk/trunk/src/org/apache/river/api/security/PrincipalGrant.java (original)
+++ river/jtsk/trunk/src/org/apache/river/api/security/PrincipalGrant.java Fri Jun 29 13:01:32 2012
@@ -31,6 +31,7 @@ import java.security.CodeSource;
 import java.security.Permission;
 import java.security.Principal;
 import java.security.ProtectionDomain;
+import java.security.UnresolvedPermission;
 import java.security.cert.Certificate;
 import java.util.Arrays;
 import java.util.Collection;
@@ -83,8 +84,9 @@ class PrincipalGrant implements Permissi
         Iterator<Permission> i = perms.iterator();
         while (i.hasNext()){
             Permission p = i.next();
-            if (p != null){
-                // REMIND: hash might not be unique for UnresolvedPermission.
+            if (p instanceof UnresolvedPermission){
+                hash = 97 * hash + p.hashCode();
+            } else if (p != null){
                 Class c = p.getClass();
                 String name = p.getName();
                 String actions = p.getActions();

Modified: river/jtsk/trunk/src/org/apache/river/api/security/RemotePolicy.java
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/src/org/apache/river/api/security/RemotePolicy.java?rev=1355351&r1=1355350&r2=1355351&view=diff
==============================================================================
--- river/jtsk/trunk/src/org/apache/river/api/security/RemotePolicy.java (original)
+++ river/jtsk/trunk/src/org/apache/river/api/security/RemotePolicy.java Fri Jun 29 13:01:32 2012
@@ -21,7 +21,6 @@ package org.apache.river.api.security;
 import java.io.IOException;
 import net.jini.security.GrantPermission;
 import net.jini.security.policy.UmbrellaGrantPermission;
-import org.apache.river.api.security.PermissionGrant;
 
 /**
  * <p>
@@ -69,7 +68,7 @@ import org.apache.river.api.security.Per
  * A node may join more than one djinn group, in this case RemotePolicy's may
  * be used as nested basePolicy's.
  * </p><p>
- * The intent of RemotePolicy is to make granting of DowloadPermission to
+ * The intent of RemotePolicy is for granting of DowloadPermission to
  * new signer Certificates and adding new Principals and Permission's to
  * distributed policy providers.
  * </p><p>
@@ -80,8 +79,7 @@ import org.apache.river.api.security.Per
  * will no longer be implied by the policy.
  * </p><p>
  * DefaultPolicyParser has been provided for an administrator client to
- * parse standard java format policy file's, to create PermissionGrant's
- * custom policy file formats may be used by extending DefaultPolicyScanner.
+ * parse standard java format policy file's, to create PermissionGrant's.
  * </p>
  * @author Peter Firmstone
  * @since 2.2.1

Added: river/jtsk/trunk/src/org/apache/river/api/security/RemotePolicyProvider.java
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/src/org/apache/river/api/security/RemotePolicyProvider.java?rev=1355351&view=auto
==============================================================================
--- river/jtsk/trunk/src/org/apache/river/api/security/RemotePolicyProvider.java (added)
+++ river/jtsk/trunk/src/org/apache/river/api/security/RemotePolicyProvider.java Fri Jun 29 13:01:32 2012
@@ -0,0 +1,334 @@
+/*
+ * 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.api.security;
+
+import java.io.IOException;
+import java.rmi.RemoteException;
+import java.security.AccessController;
+import java.security.AllPermission;
+import java.security.CodeSource;
+import java.security.Guard;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.security.UnresolvedPermission;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.NavigableSet;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import net.jini.security.GrantPermission;
+
+/**
+ *
+ * @author Peter Firmstone
+ */
+public class RemotePolicyProvider extends AbstractPolicy implements RemotePolicy,
+        ScalableNestedPolicy{
+    
+    private static final Logger logger = Logger.getLogger("net.jini.security.policy");
+    private static final ProtectionDomain policyDomain = 
+            AccessController.doPrivileged(new PrivilegedAction<ProtectionDomain>(){
+            
+            public ProtectionDomain run() {
+                return RemotePolicyProvider.class.getProtectionDomain();
+            }
+        });
+    /* 
+     * Copy referent before use.
+     * 
+     * Reference update Protected by grantLock, this array reference must only 
+     * be copied or replaced, it must never be read directly or operated on 
+     * unless holding grantLock.
+     * Local methods must first copy the reference before using the array in
+     * loops etc in case the reference is updated.
+     * This is important, to prevent the update of the remotePolicyGrant's from
+     * causing executing threads from being blocked.
+     */
+    private volatile PermissionGrant[] remotePolicyGrants; // Write protected by grantLock.
+    /* This lock protects write updating of remotePolicyGrants reference */
+    private final Object grantLock;
+    private final Permission remotePolicyPermission;
+    private final Guard protectionDomainPermission;
+    private final Policy basePolicy; // refresh protected by transactionWriteLock
+    private final boolean basePolicyIsRemote;
+    private final boolean basePolicyIsConcurrent;
+    private final PermissionCollection policyPermissions;
+    private final boolean loggable;
+    
+    /**
+     * Creates a new <code>DynamicPolicyProvider</code> instance that wraps
+     * around the given non-<code>null</code> base policy object.
+     *
+     * @param   basePolicy base policy object containing information about
+     *          non-dynamic grants
+     * @throws  NullPointerException if <code>basePolicy</code> is
+     * 		<code>null</code>
+     */
+    public RemotePolicyProvider(Policy basePolicy){
+        this.basePolicy = basePolicy;
+	remotePolicyGrants = new PermissionGrant[0];
+        loggable = logger.isLoggable(Level.FINEST);
+	grantLock = new Object();
+        remotePolicyPermission = new PolicyPermission("Remote");
+        protectionDomainPermission = new RuntimePermission("getProtectionDomain");
+        basePolicyIsRemote = basePolicy instanceof RemotePolicy ?true: false;
+        basePolicyIsConcurrent = basePolicy instanceof ScalableNestedPolicy ;
+        policyPermissions = basePolicy.getPermissions(policyDomain);
+        policyPermissions.setReadOnly();
+    }
+    
+        public void replace(PermissionGrant[] grants) throws IOException {
+        /* If the base policy is also remote, each will manage their own
+         * permissions independantly, so we do not delegate to the underlying policy.  
+         * Any underlying local policy file permissions should be propagated up
+         * into each policy, which means there will be duplication of some 
+         * policy information.
+         * It seems logical in the case of multiple remote policies that each
+         * could be the responsiblity of a different administrator.  If these
+         * separate policy's were to be combined, there may be some cases
+         * where two permissions combined also implied a third permission, that
+         * neither administrator intended to grant.
+         */ 
+        // because PermissionGrant's are given references to ProtectionDomain's
+        // we must check the caller has this permission.
+        try {
+        protectionDomainPermission.checkGuard(null); 
+        // Delegating to the underlying policy is not supported.
+	processRemotePolicyGrants(grants);
+        // If we get to here, the caller has permission.
+        } catch (SecurityException e){
+            throw new RemoteException("Policy update failed", (Throwable) e);
+        } catch (NullPointerException e) {
+            throw new RemoteException("Policy update failed", (Throwable) e);
+        }
+    }
+    
+    /**
+     * Any grants must first be checked for PermissionGrants, checkCallerHasGrants has
+     * been provided for this purpose, then prior to calling this method,
+     * the PermissionGrant's must be added to the grantsCache.
+     * 
+     * processRemotePolicyGrants places the PermissionGrant's in the remotePolicyGrants array. It is
+     * recommended that only this method be used to update the remotePolicyGrants
+     * reference.
+     * 
+     * @param grants
+     */
+    private void processRemotePolicyGrants(PermissionGrant[] grants) {
+	// This is slightly naughty calling a remotePolicyGrants method, however if it
+	// changes between now and gaining the lock, only the length of the
+	// HashSet is potentially not optimal, keeping the HashSet creation
+	// outside of the lock reduces the lock held duration.
+        Set<ProtectionDomain> domains = new HashSet<ProtectionDomain>();
+        int l = grants.length;
+        for (int i = 0; i < l; i++ ){
+            if (grants[i] == null ) throw new NullPointerException("null PermissionGrant prohibited");
+            // This causes a ProtectionDomain security check.
+            final Class c = grants[i].getClass();
+            List<ProtectionDomain> doms = AccessController.doPrivileged(
+                new PrivilegedAction<List<ProtectionDomain>>() {
+                    public List<ProtectionDomain> run() {
+                        Class[] classes = c.getDeclaredClasses();
+                        List<ProtectionDomain> domains = new ArrayList<ProtectionDomain>();
+                        int l = classes.length;
+                        for ( int i = 0; i < l; i++ ){
+                            domains.add(classes[i].getProtectionDomain());
+                        }
+                        return domains;
+                    }
+                });
+            domains.addAll(doms);
+        }
+        Iterator<ProtectionDomain> it = domains.iterator();
+        while (it.hasNext()){
+            if ( ! it.next().implies(remotePolicyPermission)) {
+                throw new SecurityException("Missing permission: " 
+                        + remotePolicyPermission.toString());
+            }
+        }
+	HashSet<PermissionGrant> holder 
+		    = new HashSet<PermissionGrant>(grants.length);
+	    holder.addAll(Arrays.asList(grants));
+            checkCallerHasGrants(holder);
+        PermissionGrant[] old = null;
+	synchronized (grantLock) {
+            old = remotePolicyGrants;
+	    PermissionGrant[] updated = new PermissionGrant[holder.size()];
+	    remotePolicyGrants = holder.toArray(updated);
+	}
+        Collection<PermissionGrant> oldGrants = new HashSet<PermissionGrant>(old.length);
+        oldGrants.addAll(Arrays.asList(old));
+        oldGrants.removeAll(holder);
+        // Collect removed Permission's to notify CachingSecurityManager.
+        Set<Permission> removed = new HashSet<Permission>(120);
+        Iterator<PermissionGrant> rgi = oldGrants.iterator();
+        while (rgi.hasNext()){
+            PermissionGrant g = rgi.next();
+                    removed.addAll(g.getPermissions());
+        }
+        
+        SecurityManager sm = System.getSecurityManager();
+        if (sm instanceof CachingSecurityManager) {
+            ((CachingSecurityManager) sm).clearCache();
+        }
+        // oldGrants now only has the grants which have been removed.
+    }
+    
+    @Override
+    public PermissionCollection getPermissions(CodeSource codesource) {
+        return basePolicy.getPermissions(codesource);
+    }
+    
+    public PermissionCollection getPermissions(ProtectionDomain domain){
+        Collection<PermissionGrant> grants = getPermissionGrants(domain);
+        NavigableSet<Permission> perms = new TreeSet<Permission>(comparator);
+        processGrants(grants, null, true, perms);
+        return convert(perms, perms.size(), 0.75F, 1, 16);
+    }
+    
+    @Override
+    public boolean implies(ProtectionDomain domain, Permission permission) {
+        if (domain == policyDomain) return policyPermissions.implies(permission);
+        if (basePolicyIsRemote){
+            if (basePolicy.implies(domain, permission)) return true;
+        }
+	if (permission == null) throw new NullPointerException("permission not allowed to be null");
+        /* If com.sun.security.provider.PolicyFile:
+         * 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
+         * resolved, the Enumeration will cause a ConcurrentModificationException.
+         */ 
+        
+        /* Be mindful of static Permissions held by the 
+         * ProtectionDomain, a Permission may be implied by the 
+         * the combination of Permission's in the ProtectionDomain and 
+         * the base policy, but not by either individually.
+         * The ProtectionDomain merge is only perfomed if
+         * ProtectionDomain.toString() is called, this is purely for debugging
+         * the policy permissions are never merged back into the
+         * ProtectionDomain, the underlying policy
+         * performs the merge.
+         * 
+         * Furthermore it is commonly understood that when
+         * ProtectionDomain.implies(Permission) is called, it first checks
+         * it's own private Permissions, then calls Policy.implies, however
+         * this is incorrect, the Policy is checked first.
+         */ 
+       /* Don't use the underlying policy permission collection otherwise
+        * we can leak grants in to the underlying policy from our cache,
+        * this could then be inadvertantly cached and passed to a ProtectionDomain
+        * constructor, preventing Revocation.
+        */
+        NavigableSet<Permission> permissions = new TreeSet<Permission>(comparator); // Keep as small as possible.
+        /* If GrantPermission is being requested, we must get all Permission objects
+         * and add them to the underlying collection.
+         * 
+         */
+        Class permClass = permission instanceof GrantPermission ? null : permission.getClass();
+        if (!basePolicyIsConcurrent) {
+            PermissionCollection pc = basePolicy.getPermissions(domain);
+            Enumeration<Permission> enu = pc.elements();
+            while (enu.hasMoreElements()){
+                Permission p = enu.nextElement();
+                if (p instanceof AllPermission) return true; // Return early.
+                if ( permClass == null){
+                    permissions.add(p);
+                } else if ( permClass.isInstance(permission) || permission instanceof UnresolvedPermission){
+                    permissions.add(p);
+                }
+            }
+        }else{
+            Collection<PermissionGrant> grants = ((ScalableNestedPolicy) basePolicy).getPermissionGrants(domain);
+            processGrants(grants, permClass, true, permissions);
+            if (permissions.contains(ALL_PERMISSION)) return true;
+        }
+	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)) {
+		Collection<Permission> perms = grantsRefCopy[i].getPermissions();
+		Iterator<Permission> it = perms.iterator();
+                while (it.hasNext()){
+                    Permission p = it.next();
+                    if ( permClass == null){
+                        permissions.add(p);
+                    } else if ( permClass.isInstance(permission) || permission instanceof UnresolvedPermission){
+                        permissions.add(p);
+                    }
+                }
+	    }
+	}
+//        if (thread.isInterrupted()) return false;
+        
+        PermissionCollection pc = null;
+        if (permClass != null){
+            pc =convert(permissions, 1, 0.75F, 1, 16);
+        } else {
+            // GrantPermission
+            pc = convert(permissions, 24, 0.75F, 1, 16);
+            expandUmbrella(pc);
+        }
+        return pc.implies(permission);
+    }
+
+    @Override
+    public Collection<PermissionGrant> getPermissionGrants(ProtectionDomain domain) {
+        Collection<PermissionGrant> grants = null;
+        if (basePolicy instanceof ScalableNestedPolicy){
+            grants = ((ScalableNestedPolicy) basePolicy).getPermissionGrants(domain);
+        } else {
+            grants = new LinkedList<PermissionGrant>();
+            grants.add(extractGrantFromPolicy(basePolicy, domain));
+        }
+        PermissionGrant[] rpg = remotePolicyGrants; // Copy volatile reference.
+        grants.addAll(Arrays.asList(rpg));
+        return grants;
+    }
+
+//    @Override
+//    public Collection<PermissionGrant> getPermissionGrants(boolean recursive) throws UnsupportedOperationException {
+//        Collection<PermissionGrant> grants = null;
+//        if ( recursive ){ 
+//            if (!(basePolicy instanceof ScalableNestedPolicy)){
+//                throw new UnsupportedOperationException
+//                        ("base policy doesn't implement ScalableNestedPolicy");
+//            }
+//            grants = ((ScalableNestedPolicy)basePolicy).getPermissionGrants(recursive);
+//        } else {
+//            grants = new LinkedList<PermissionGrant>();
+//        }
+//        PermissionGrant[] rpg = remotePolicyGrants; // Copy volatile reference.
+//        grants.addAll(Arrays.asList(rpg));
+//        return grants;
+//    }
+}

Propchange: river/jtsk/trunk/src/org/apache/river/api/security/RemotePolicyProvider.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: river/jtsk/trunk/src/org/apache/river/api/security/RevocablePolicy.java
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/src/org/apache/river/api/security/RevocablePolicy.java?rev=1355351&r1=1355350&r2=1355351&view=diff
==============================================================================
--- river/jtsk/trunk/src/org/apache/river/api/security/RevocablePolicy.java (original)
+++ river/jtsk/trunk/src/org/apache/river/api/security/RevocablePolicy.java Fri Jun 29 13:01:32 2012
@@ -70,8 +70,7 @@ import net.jini.security.policy.DynamicP
  */
 public interface RevocablePolicy extends DynamicPolicy {
     /**
-     * Revoke, removes all DynamicPolicy grants specific to the classLoader of
-     * the class passed in.  This is for removing dynamic grant's from proxy's.
+     * Revokes a specific PermissionGrant from the Policy.
      * 
      * Caveat: Not all Permission's once granted can be revoked.  When a Permission
      * is checked, prior to passing a reference to a caller, that reference
@@ -82,13 +81,16 @@ public interface RevocablePolicy extends
      * to determine if the proxy has no or minimal permissions.  If the
      * proxy still retains permission, the caller should throw a SecurityException.
      * 
-     * @param cl
-     * @return Array of Permission's that have been revoked, the caller should
-     * check to see if any of these allow references to escape, in which case
-     * the proxy still has the functionality the Permission is supposed to 
-     * protect against.
+     * @param p
+     * @return true if successfully
      */
-    public Permission[] revoke(Class cl, Principal[] principals);
+    public boolean revoke(PermissionGrant p);
+    
+    /**
+     * A dynamic grant.
+     * @param p
+     */
+    public boolean grant(PermissionGrant p);
     /**
      * 
      * @return true - If Revoke supported by underlying policy.

Copied: river/jtsk/trunk/src/org/apache/river/api/security/ScalableNestedPolicy.java (from r1337505, river/jtsk/trunk/src/org/apache/river/api/security/ConcurrentPolicy.java)
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/src/org/apache/river/api/security/ScalableNestedPolicy.java?p2=river/jtsk/trunk/src/org/apache/river/api/security/ScalableNestedPolicy.java&p1=river/jtsk/trunk/src/org/apache/river/api/security/ConcurrentPolicy.java&r1=1337505&r2=1355351&rev=1355351&view=diff
==============================================================================
--- river/jtsk/trunk/src/org/apache/river/api/security/ConcurrentPolicy.java (original)
+++ river/jtsk/trunk/src/org/apache/river/api/security/ScalableNestedPolicy.java Fri Jun 29 13:01:32 2012
@@ -18,37 +18,43 @@
 
 package org.apache.river.api.security;
 
-import java.security.CodeSource;
 import java.security.ProtectionDomain;
-import org.apache.river.api.security.PermissionGrant;
+import java.util.Collection;
 
 /**
- *
- * @author peter
+ * Policy providers can implement this interface to provide nested policies
+ * a common interface to allow delayed creation of PermissionCollection
+ * instances until all after all Permission objects are collected, allowing
+ * the implementer to add Permission objects to a PermissionCollection in
+ * an order that avoids unnecessary reverse DNS calls for example.
+ * 
+ * @author Peter Firmstone
  */
-public interface ConcurrentPolicy {
-    
-    public boolean isConcurrent();
+public interface ScalableNestedPolicy {
     
     /**
-     * Returns a new array containing immutable PermissionGrant's, the array
-     * returned is not shared.
+     * Returns a new Collection containing immutable PermissionGrant's, the
+     * Collection returned is not synchronised and must not be shared with policy 
+     * internal state.
      * 
      * Only those PermissionGrant's that imply the domain will be returned.
      * 
      * This allows the top level policy to gather all PermissionGrant's,
      * retrieve all relevant permissions, then sort them using PermissionComparator
-     * or any other Comparator, so Permission's are added to a PermissionCollection
+     * or any other Comparator, so Permission objects are added to a PermissionCollection
      * in the most efficient order.
      * 
+     * If a nested base policy doesn't support ScalableNestedPolicy, then the
+     * implementer should create a PermissionGrant for the domain, containing
+     * the Permission objects returned from the policy.
+     * 
+     * The first nested base policy (usually a file policy provider) is
+     * responsible for merging any static Permission objects from the
+     * ProtectionDomain.
+     * 
      * @param domain 
-     * @return PermissionGrant []
-     */
-    public PermissionGrant[] getPermissionGrants(ProtectionDomain domain);
-    
-    /**
-     * Retrieves all PermissionGrant's from the underlying policy.
-     * @return
+     * @return Collection<PermissionGrant>  
      */
-    public PermissionGrant[] getPermissionGrants();
+    public Collection<PermissionGrant> getPermissionGrants(
+                                                ProtectionDomain domain);
 }

Modified: river/jtsk/trunk/src/org/apache/river/api/security/package.html
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/src/org/apache/river/api/security/package.html?rev=1355351&r1=1355350&r2=1355351&view=diff
==============================================================================
--- river/jtsk/trunk/src/org/apache/river/api/security/package.html (original)
+++ river/jtsk/trunk/src/org/apache/river/api/security/package.html Fri Jun 29 13:01:32 2012
@@ -30,9 +30,8 @@ policy file provider to replace Java's s
 is in theory much faster after policy file parsing is complete, it uses an immutable data 
 structure that supports highly scalable concurrent access and does not require
 further disk access and minimises network and DNS requirements.
-</p><p>
-Delegates and DelegatePermission were intended inclusions in this release,
-however they have been removed and will be included in a future release of River.
+    </p><p>
+Disk access of policy files is performed serially.
 </p>
 </body>
 </html>

Added: river/jtsk/trunk/test/src/org/apache/river/api/security/DelegatePermissionTest.java
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/test/src/org/apache/river/api/security/DelegatePermissionTest.java?rev=1355351&view=auto
==============================================================================
--- river/jtsk/trunk/test/src/org/apache/river/api/security/DelegatePermissionTest.java (added)
+++ river/jtsk/trunk/test/src/org/apache/river/api/security/DelegatePermissionTest.java Fri Jun 29 13:01:32 2012
@@ -0,0 +1,103 @@
+/*
+ * 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.api.security;
+
+import java.net.SocketPermission;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ * Tests fail when run from IDE.
+ * 
+ * @author peter
+ */
+public class DelegatePermissionTest {
+   
+    public DelegatePermissionTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+       
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+    
+    @Before
+    public void setUp() {
+    }
+
+    /**
+     * Test of get method, of class DelegatePermission.
+     */
+    @Test
+    public void testGet() {
+        System.out.println("get");
+        Permission expResult = DelegatePermission.get(new SocketPermission("Localhost", "accept"));
+        Permission result = DelegatePermission.get(new SocketPermission("Localhost","accept"));
+        assertTrue(expResult == result);
+    }
+
+    /**
+     * Test of implies method, of class DelegatePermission.
+     */
+    @Test
+    public void testImplies() {
+        System.out.println("implies");
+        Permission permission = new SocketPermission("Localhost", "connect, accept");
+        Permission instance = DelegatePermission.get(permission);
+        boolean expResult = false;
+        boolean result = instance.implies(permission);
+        assertEquals(expResult, result);
+        permission = DelegatePermission.get(new SocketPermission("Localhost", "connect"));
+        assertTrue(instance.implies(permission));
+    }
+
+    /**
+     * Test of getPermission method, of class DelegatePermission.
+     */
+    @Test
+    public void testGetPermission() {
+        System.out.println("getPermission");
+        Permission instance = DelegatePermission.get(new SocketPermission("Localhost", "accept"));
+        Permission expResult = new SocketPermission("Localhost", "accept");
+        Permission result = ((DelegatePermission)instance).getPermission();
+        assertEquals(expResult, result);
+    }
+
+    /**
+     * Test of newPermissionCollection method, of class DelegatePermission.
+     */
+//    @Test
+//    public void testNewPermissionCollection() {
+//        System.out.println("newPermissionCollection");
+//        DelegatePermission instance = null;
+//        PermissionCollection expResult = null;
+//        PermissionCollection result = instance.newPermissionCollection();
+//        assertEquals(expResult, result);
+//        // TODO review the generated test code and remove the default call to fail.
+//        fail("The test case is a prototype.");
+//    }
+}

Propchange: river/jtsk/trunk/test/src/org/apache/river/api/security/DelegatePermissionTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: river/jtsk/trunk/test/src/org/apache/river/api/security/DelegateSecurityManagerTest.java
URL: http://svn.apache.org/viewvc/river/jtsk/trunk/test/src/org/apache/river/api/security/DelegateSecurityManagerTest.java?rev=1355351&view=auto
==============================================================================
--- river/jtsk/trunk/test/src/org/apache/river/api/security/DelegateSecurityManagerTest.java (added)
+++ river/jtsk/trunk/test/src/org/apache/river/api/security/DelegateSecurityManagerTest.java Fri Jun 29 13:01:32 2012
@@ -0,0 +1,71 @@
+/*
+ * 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.api.security;
+
+import java.net.SocketPermission;
+import java.security.cert.Certificate;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.Permissions;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.ProtectionDomain;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author peter
+ */
+public class DelegateSecurityManagerTest {
+    
+    public DelegateSecurityManagerTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+    
+    @Before
+    public void setUp() {
+    }
+
+    /**
+     * Test of checkPermission method, of class DelegateSecurityManager.
+     */
+    @Test
+    public void testCheckPermission() throws MalformedURLException {
+        System.out.println("checkPermission");
+        Permissions perms = new Permissions();
+        Permission p1 = new SocketPermission("localhost", "accept" );
+        perms.add(p1);
+        Permission p = DelegatePermission.get(p1);
+        ProtectionDomain pd = new ProtectionDomain(new CodeSource(new URL("file:///foo"), (Certificate[]) null), perms);
+        DelegateSecurityManager instance = new DelegateSecurityManager();
+        boolean expResult = true;
+        boolean result = instance.checkPermission(pd, p);
+        assertEquals(expResult, result);
+    }
+}

Propchange: river/jtsk/trunk/test/src/org/apache/river/api/security/DelegateSecurityManagerTest.java
------------------------------------------------------------------------------
    svn:eol-style = native