jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ang...@apache.org
Subject svn commit: r983906 [1/2] - in /jackrabbit/trunk: jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/ jackrabbit-core/src/main/java/org/apache/jackrabbit/core...
Date Tue, 10 Aug 2010 09:58:04 GMT
Author: angela
Date: Tue Aug 10 09:58:03 2010
New Revision: 983906

URL: http://svn.apache.org/viewvc?rev=983906&view=rev
Log:
JCR-2700 : Allow for wildcard restriction in resource-based ACEs

Added:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/GlobPattern.java   (contents, props changed)
      - copied, changed from r954954, jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/GlobPattern.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/GlobPatternTest.java   (contents, props changed)
      - copied, changed from r954954, jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/principalbased/GlobPatternTest.java
Removed:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/GlobPattern.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/principalbased/GlobPatternTest.java
Modified:
    jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/JackrabbitAccessControlEntry.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlConstants.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlEntryImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplate.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/EntryFilterImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLEditor.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLProvider.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLTemplate.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/AbstractWriteTest.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/TestAll.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplateTest.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/acl/EntryTest.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/acl/ReadTest.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/principalbased/EntryTest.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/principalbased/TestAll.java

Modified: jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/JackrabbitAccessControlEntry.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/JackrabbitAccessControlEntry.java?rev=983906&r1=983905&r2=983906&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/JackrabbitAccessControlEntry.java (original)
+++ jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/JackrabbitAccessControlEntry.java Tue Aug 10 09:58:03 2010
@@ -16,6 +16,7 @@
  */
 package org.apache.jackrabbit.api.security;
 
+import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 import javax.jcr.security.AccessControlEntry;
 
@@ -36,8 +37,9 @@ public interface JackrabbitAccessControl
      * Return the names of the restrictions present with this access control entry.
      *
      * @return the names of the restrictions
+     * @throws RepositoryException if an error occurs.
      */
-    String[] getRestrictionNames();
+    String[] getRestrictionNames() throws RepositoryException;
 
     /**
      * Return the value of the restriction with the specified name or
@@ -47,6 +49,7 @@ public interface JackrabbitAccessControl
      * {@link #getRestrictionNames()}.
      * @return value of the restriction with the specified name or
      * <code>null</code> if no such restriction exists
+     * @throws RepositoryException if an error occurs.
      */
-    Value getRestriction(String restrictionName);
+    Value getRestriction(String restrictionName) throws RepositoryException;
 }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlConstants.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlConstants.java?rev=983906&r1=983905&r2=983906&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlConstants.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlConstants.java Tue Aug 10 09:58:03 2010
@@ -51,6 +51,12 @@ public interface AccessControlConstants 
      */
     Name P_PRINCIPAL_NAME = NF.create(Name.NS_REP_URI, "principalName");
 
+    /**
+     * rep:glob property name used to restrict the number of child nodes
+     * or properties that are affected by an ACL inherited from a parent node.
+     */
+    Name P_GLOB = NF.create(Name.NS_REP_URI, "glob");
+
     //----------------------------------------------------< node type names >---
     /**
      * rep:AccessControllable nodetype

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlEntryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlEntryImpl.java?rev=983906&r1=983905&r2=983906&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlEntryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlEntryImpl.java Tue Aug 10 09:58:03 2010
@@ -16,18 +16,21 @@
  */
 package org.apache.jackrabbit.core.security.authorization;
 
-import java.security.Principal;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.commons.conversion.NameResolver;
+import org.apache.jackrabbit.value.ValueHelper;
 
+import javax.jcr.NamespaceException;
+import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 import javax.jcr.ValueFactory;
 import javax.jcr.security.AccessControlException;
 import javax.jcr.security.Privilege;
-
-import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry;
-import org.apache.jackrabbit.value.ValueHelper;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * Simple, immutable implementation of the
@@ -61,12 +64,7 @@ public abstract class AccessControlEntry
      * Jackrabbit specific extension: the list of additional restrictions to be
      * included in the evaluation.
      */
-    private final Map<String, Value> restrictions;
-
-    /**
-     * Value factory
-     */
-    private final ValueFactory valueFactory;
+    private final Map<Name, Value> restrictions;
 
     /**
      * Hash code being calculated on demand.
@@ -79,10 +77,11 @@ public abstract class AccessControlEntry
      * @param principal Principal for this access control entry.
      * @param privileges Privileges for this access control entry.
      * @throws AccessControlException if either principal or privileges are invalid.
+     * @throws RepositoryException if another error occurs.
      */
     protected AccessControlEntryImpl(Principal principal, Privilege[] privileges)
-            throws AccessControlException {
-        this(principal, privileges, true, null, null);
+            throws AccessControlException, RepositoryException {
+        this(principal, privileges, true, null);
     }
 
     /**
@@ -95,13 +94,12 @@ public abstract class AccessControlEntry
      * @param restrictions A map of restriction name (String) to restriction
      * (Value). See {@link org.apache.jackrabbit.api.security.JackrabbitAccessControlList#getRestrictionNames()}
      * and {@link org.apache.jackrabbit.api.security.JackrabbitAccessControlList#getRestrictionType(String)}.
-     * @param valueFactory the value factory.
      * @throws AccessControlException if either principal or privileges are invalid.
+     * @throws RepositoryException if another error occurs.
      */
     protected AccessControlEntryImpl(Principal principal, Privilege[] privileges,
-                                     boolean isAllow, Map<String, Value> restrictions,
-                                     ValueFactory valueFactory)
-            throws AccessControlException {
+                                     boolean isAllow, Map<String, Value> restrictions)
+            throws AccessControlException, RepositoryException {
         if (principal == null) {
             throw new IllegalArgumentException();
         }
@@ -115,17 +113,48 @@ public abstract class AccessControlEntry
         this.privileges = privileges;
         this.privilegeBits = PrivilegeRegistry.getBits(privileges);
         this.allow = isAllow;
-        this.valueFactory = valueFactory;
-        
+
         if (restrictions == null) {
             this.restrictions = Collections.emptyMap();
         } else {
-            this.restrictions = new HashMap<String, Value>(restrictions.size());
+            this.restrictions = new HashMap<Name, Value>(restrictions.size());
             // validate the passed restrictions and fill the map
             for (String key : restrictions.keySet()) {
                 Value value = restrictions.get(key);
-                value = ValueHelper.copy(value, valueFactory);
-                this.restrictions.put(key, value);
+                value = ValueHelper.copy(value, getValueFactory());
+                this.restrictions.put(getResolver().getQName(key), value);
+            }
+        }
+    }
+
+    /**
+     * 
+     * @param base
+     * @param privileges
+     * @param isAllow
+     * @throws AccessControlException
+     */
+    protected AccessControlEntryImpl(AccessControlEntryImpl base, Privilege[] privileges, boolean isAllow) throws AccessControlException {
+        // make sure no abstract privileges are passed.
+        for (Privilege privilege : privileges) {
+            if (privilege.isAbstract()) {
+                throw new AccessControlException("Privilege " + privilege + " is abstract.");
+            }
+        }
+        this.principal = base.principal;
+        this.privileges = privileges;
+        this.privilegeBits = PrivilegeRegistry.getBits(privileges);
+        this.allow = isAllow;
+
+        if (base.restrictions == null) {
+            this.restrictions = Collections.emptyMap();
+        } else {
+            this.restrictions = new HashMap<Name, Value>(base.restrictions.size());
+            // validate the passed restrictions and fill the map
+            for (Name name : restrictions.keySet()) {
+                Value value = restrictions.get(name);
+                value = ValueHelper.copy(value, getValueFactory());
+                this.restrictions.put(name, value);
             }
         }
     }
@@ -138,6 +167,33 @@ public abstract class AccessControlEntry
     }
 
     /**
+     * Returns the restrictions defined for this entry.
+     *
+     * @return the restrictions defined for this entry.
+     */
+    public Map<Name,Value> getRestrictions() {
+        return Collections.unmodifiableMap(restrictions);
+    }
+
+    /**
+     * @param restrictionName
+     * @return The restriction with the specified name or <code>null</code>.
+     */
+    public Value getRestriction(Name restrictionName) {
+        return ValueHelper.copy(restrictions.get(restrictionName), getValueFactory());
+    }
+
+    /**
+     * @return Returns the name resolver used to convert JCR names to Name and vice versa.
+     */
+    protected abstract NameResolver getResolver();
+
+    /**
+     * @return The value factory to be used.
+     */
+    protected abstract ValueFactory getValueFactory();
+
+    /**
      * Build the hash code.
      *
      * @return the hash code.
@@ -178,19 +234,21 @@ public abstract class AccessControlEntry
     /**
      * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry#getRestrictionNames()
      */
-    public String[] getRestrictionNames() {
-        return restrictions.keySet().toArray(new String[restrictions.size()]);
+    public String[] getRestrictionNames() throws NamespaceException {
+        String[] restrNames = new String[restrictions.size()];
+        int i = 0;
+        for (Name n : restrictions.keySet()) {
+            restrNames[i] = getResolver().getJCRName(n);
+            i++;
+        }
+        return restrNames;
     }
 
     /**
      * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry#getRestriction(String)
      */
-    public Value getRestriction(String restrictionName) {
-        if (restrictions.containsKey(restrictionName)) {
-            return ValueHelper.copy(restrictions.get(restrictionName), valueFactory);
-        } else {
-            return null;
-        }
+    public Value getRestriction(String restrictionName) throws RepositoryException {
+        return getRestriction(getResolver().getQName(restrictionName));
     }
 
     //-------------------------------------------------------------< Object >---

Copied: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/GlobPattern.java (from r954954, jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/GlobPattern.java)
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/GlobPattern.java?p2=jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/GlobPattern.java&p1=jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/GlobPattern.java&r1=954954&r2=983906&rev=983906&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/GlobPattern.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/GlobPattern.java Tue Aug 10 09:58:03 2010
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.jackrabbit.core.security.authorization.principalbased;
+package org.apache.jackrabbit.core.security.authorization;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -24,57 +24,99 @@ import javax.jcr.Item;
 import javax.jcr.RepositoryException;
 
 /**
- * <code>GlobPattern</code>...
+ * <code>GlobPattern</code> defines a simplistic pattern matching. It consists
+ * of a mandatory (leading) path and an optional "glob" that may contain one or
+ * more wildcard characters ("<code>*</code>") according to the glob matching
+ * defined by {@link javax.jcr.Node#getNodes(String[])}. In contrast to that
+ * method the <code>GlobPattern</code> operates on path (not only names).
+ * <p/>
+ *
+ * <p>
+ * Please note the following special cases:
+ * <pre>
+ * NodePath     |   Restriction   |   Matches
+ * -----------------------------------------------------------------------------
+ * /foo         |   null          |   matches /foo and all children of /foo
+ * /foo         |   ""            |   matches /foo only
+ * </pre>
+ * </p>
+ *
+ * <p>
+ * Examples including wildcard char:
+ * <pre>
+ * NodePath = "/foo"
+ * Restriction   |   Matches
+ * -----------------------------------------------------------------------------
+ * &#42;         |   all siblings of foo and foo's and the siblings' descendants
+ * /&#42;cat     |   all children of /foo whose path ends with "cat"
+ * /&#42;/cat    |   all non-direct descendants of /foo named "cat"
+ * /cat&#42;     |   all descendant path of /foo that have the direct foo-descendant segment starting with "cat"
+ * &#42;cat      |   all siblings and descendants of foo that have a name ending with cat
+ * &#42;/cat     |   all descendants of /foo and foo's siblings that have a name segment "cat"
+ * cat/&#42;     |   all descendants of '/foocat'
+ * /cat/&#42;    |   all descendants of '/foo/cat'
+ * &#42;cat/&#42;    |   all descendants of /foo that have an intermediate segment ending with 'cat'
+ * </pre>
+ * </p>
  */
-class GlobPattern {
-
-    //TODO: add proper impl.
+public final class GlobPattern {
 
     private static Logger log = LoggerFactory.getLogger(GlobPattern.class);
 
-    private static final char ALL = '*';
-    public static final String WILDCARD_ALL = "*";
-
-    private final String pattern;
-    private final char[] patternChars;
-
-    private final boolean matchesAll;
-    private final boolean containsWildcard;
-
-    private GlobPattern(String pattern)  {
-        this.pattern = pattern;
+    private static final char WILDCARD_CHAR = '*';
+    private static final String WILDCARD_STRING = "*";
 
-        matchesAll = WILDCARD_ALL.equals(pattern);
-        containsWildcard = pattern.indexOf(ALL) > -1;
+    private final String nodePath;
+    private final String restriction;
+    
+    private final Pattern pattern;
 
-        patternChars = pattern.toCharArray();
+    private GlobPattern(String nodePath, String restriction)  {
+        if (nodePath == null) {
+            throw new IllegalArgumentException();
+        }
 
+        this.nodePath = nodePath;
+        this.restriction = restriction;
+        
+        if (restriction != null && restriction.length() > 0) {
+            StringBuilder b = new StringBuilder(nodePath);
+            b.append(restriction);
+
+            int lastPos = restriction.lastIndexOf(WILDCARD_CHAR);
+            if (lastPos >= 0) {
+                String end;
+                if (lastPos != restriction.length()-1) {
+                    end = restriction.substring(lastPos + 1);
+                } else {
+                    end = null;
+                }
+                pattern = new WildcardPattern(b.toString(), end);
+            } else {
+                pattern = new PathPattern(b.toString());
+            }
+        } else {
+            pattern = new PathPattern();
+        }
     }
 
-    static GlobPattern create(String pattern) {
-        if (pattern == null) {
-            throw new IllegalArgumentException();
-        }
-        return new GlobPattern(pattern);
+    public static GlobPattern create(String nodePath, String restrictions) {
+        return new GlobPattern(nodePath, restrictions);
     }
 
-    boolean matches(String toMatch) {
-        // shortcut
-        if (matchesAll) {
-            return true;
-        }
+    public static GlobPattern create(String nodePath) {
+        return create(nodePath, null);
+    }
+    
+    public boolean matches(String toMatch) {
         if (toMatch == null) {
             return false;
-        }
-
-        if (containsWildcard) {
-            return matches(patternChars, toMatch.toCharArray());
         } else {
-            return Text.isDescendantOrEqual(pattern, toMatch);
+            return pattern.matches(toMatch);
         }
     }
 
-    boolean matches(Item itemToMatch) {
+    public boolean matches(Item itemToMatch) {
         try {
             // TODO: missing proper impl
             return matches(itemToMatch.getPath());
@@ -84,28 +126,17 @@ class GlobPattern {
         }
     }
 
-    private static boolean matches(char[] patternChars, char[] toMatch) {
-        // TODO: add proper impl
-        for (int i = 0; i < patternChars.length; i++) {
-            if (patternChars[i] == ALL) {
-                return true;
-            }
-            if (i >= toMatch.length || patternChars[i] != toMatch[i]) {
-                return false;
-            }
-        }
-
-        return false;
-    }
-
     //-------------------------------------------------------------< Object >---
-
     /**
      * @see Object#hashCode()
      */
     @Override
     public int hashCode() {
-        return pattern.hashCode();
+        int h = 37 * 17 + nodePath.hashCode();
+        if (restriction  != null) {
+            h = 37 * h + restriction.hashCode();
+        }
+        return h;
     }
 
     /**
@@ -113,7 +144,7 @@ class GlobPattern {
      */
     @Override
     public String toString() {
-        return pattern;
+        return nodePath + " : " + restriction;
     }
 
     /**
@@ -125,8 +156,134 @@ class GlobPattern {
             return true;
         }
         if (obj instanceof GlobPattern) {
-            return pattern.equals(((GlobPattern)obj).pattern);
+            GlobPattern other = (GlobPattern) obj;
+            return nodePath.equals(other.nodePath) &&
+                    (restriction == null) ? other.restriction == null : restriction.equals(other.restriction);
         }
         return false;
     }
+
+    //------------------------------------------------------< inner classes >---
+    /**
+     * Base for PathPattern and WildcardPattern
+     */
+    private abstract class Pattern {
+        abstract boolean matches(String toMatch);
+    }
+
+    /**
+     * Path pattern: The restriction is missing or doesn't contain any wildcard character.
+     */
+    private final class PathPattern extends Pattern {
+
+        private final String patternStr;
+
+        private PathPattern() {
+            this(null);
+        }
+        private PathPattern(String patternStr) {
+            this.patternStr = patternStr;
+        }
+        
+        @Override
+        boolean matches(String toMatch) {
+            if (restriction == null) {
+                return Text.isDescendantOrEqual(nodePath, toMatch);
+            } else if (restriction.length() == 0) {
+                return nodePath.equals(toMatch);
+            } else {
+                // no wildcard contained in restriction: use path defined
+                // by nodePath + restriction to calculate the match
+                return Text.isDescendantOrEqual(patternStr, toMatch);
+            }
+        }
+    }
+
+    /**
+     * Wildcard pattern: The specified restriction contains one or more wildcard character(s).
+     */
+    private final class WildcardPattern extends Pattern {
+
+        private final String patternEnd;
+        private final char[] patternChars;        
+
+        private WildcardPattern(String patternStr, String patternEnd) {
+            patternChars = patternStr.toCharArray();
+            this.patternEnd = patternEnd;
+        }
+
+        @Override
+        boolean matches(String toMatch) {
+            if (patternEnd != null && !toMatch.endsWith(patternEnd)) {
+                // shortcut: verify if end of pattern matches end of toMatch
+                return false;
+            }
+            // shortcut didn't reveal mismatch -> need to process the internal match method.
+            return matches(patternChars, 0, toMatch.toCharArray(), 0);
+        }
+
+        /**
+         *
+         * @param pattern The pattern
+         * @param pOff
+         * @param s
+         * @param sOff
+         * @return <code>true</code> if matches, <code>false</code> otherwise
+         */
+        private boolean matches(char[] pattern, int pOff,
+                                char[] s, int sOff) {
+
+            /*
+            NOTE: code has been copied (and slightly modified) from
+            ChildrenCollectorFilter#internalMatches.
+            TODO: move them to a common utility.
+            */
+
+            int pLength = pattern.length;
+            int sLength = s.length;
+
+            while (true) {
+                // end of pattern reached: matches only if sOff points at the end
+                // of the string to match.
+                if (pOff >= pLength) {
+                    return sOff >= sLength;
+                }
+
+                // the end of the string to match has been reached but pattern
+                // doesn't have '*' at patternIndex -> no match
+                if (sOff >= sLength && pattern[pOff] != WILDCARD_CHAR) {
+                    return false;
+                }
+
+                // the next character of the pattern is '*'
+                // -> recursively test if the rest of the specified string matches
+                if (pattern[pOff] == WILDCARD_CHAR) {
+                    if (++pOff >= pLength) {
+                        return true;
+                    }
+
+                    while (true) {
+                        if (matches(pattern, pOff, s, sOff)) {
+                            return true;
+                        }
+                        if (sOff >= sLength) {
+                            return false;
+                        }
+                        sOff++;
+                    }
+                }
+
+                // not yet reached end of patter nor string and not wildcard character.
+                // the 2 strings don't match in case the characters at the current
+                // position are not the same.
+                if (pOff < pLength && sOff < sLength) {
+                    if (pattern[pOff] != s[sOff]) {
+                        return false;
+                    }
+                }
+                pOff++;
+                sOff++;
+            }
+        }
+    }
 }
\ No newline at end of file

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/GlobPattern.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/GlobPattern.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java?rev=983906&r1=983905&r2=983906&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java Tue Aug 10 09:58:03 2010
@@ -16,13 +16,13 @@
  */
 package org.apache.jackrabbit.core.security.authorization.acl;
 
-import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry;
 import org.apache.jackrabbit.api.security.JackrabbitAccessControlPolicy;
 import org.apache.jackrabbit.core.NodeImpl;
 import org.apache.jackrabbit.core.ProtectedItemModifier;
 import org.apache.jackrabbit.core.SessionImpl;
 import org.apache.jackrabbit.core.security.authorization.AccessControlConstants;
 import org.apache.jackrabbit.core.security.authorization.AccessControlEditor;
+import org.apache.jackrabbit.core.security.authorization.AccessControlEntryImpl;
 import org.apache.jackrabbit.core.security.authorization.AccessControlUtils;
 import org.apache.jackrabbit.core.security.authorization.Permission;
 import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry;
@@ -47,6 +47,7 @@ import javax.jcr.security.AccessControlE
 import javax.jcr.security.AccessControlPolicy;
 import javax.jcr.security.Privilege;
 import java.security.Principal;
+import java.util.Set;
 
 /**
  * <code>ACLEditor</code>...
@@ -133,7 +134,7 @@ public class ACLEditor extends Protected
             String mixin = session.getJCRName(NT_REP_ACCESS_CONTROLLABLE);
             if (controlledNode.isNodeType(mixin) || controlledNode.canAddMixin(mixin)) {
                 acl = new ACLTemplate(nodePath, session.getPrincipalManager(),
-                        privilegeRegistry, session.getValueFactory());
+                        privilegeRegistry, session.getValueFactory(), session);
             }
         } // else: acl already present -> getPolicies must be used.
         return (acl != null) ? new AccessControlPolicy[] {acl} : new AccessControlPolicy[0];
@@ -171,7 +172,7 @@ public class ACLEditor extends Protected
         
         AccessControlEntry[] entries = ((ACLTemplate) policy).getAccessControlEntries();
         for (AccessControlEntry entry : entries) {
-            JackrabbitAccessControlEntry ace = (JackrabbitAccessControlEntry) entry;
+            AccessControlEntryImpl ace = (AccessControlEntryImpl) entry;
 
             Name nodeName = getUniqueNodeName(aclNode, ace.isAllow() ? "allow" : "deny");
             Name ntName = (ace.isAllow()) ? NT_REP_GRANT_ACE : NT_REP_DENY_ACE;
@@ -188,6 +189,13 @@ public class ACLEditor extends Protected
             Privilege[] pvlgs = ace.getPrivileges();
             Value[] names = getPrivilegeNames(pvlgs, vf);
             setProperty(aceNode, P_PRIVILEGES, names);
+
+            // store the restrictions:
+            Set<Name> restrNames = ace.getRestrictions().keySet();
+            for (Name restrName : restrNames) {
+                Value value = ace.getRestriction(restrName);
+                setProperty(aceNode, restrName, value);
+            }
         }
 
         // mark the parent modified.

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java?rev=983906&r1=983905&r2=983906&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java Tue Aug 10 09:58:03 2010
@@ -310,6 +310,7 @@ public class ACLProvider extends Abstrac
      *
      * @param node the Node to collect the ACLs for, which must NOT be part of the
      * structure defined by mix:AccessControllable.
+     * @param permissions
      * @param acls List used to collect the effective acls.
      * @throws RepositoryException if an error occurs
      */
@@ -382,7 +383,7 @@ public class ACLProvider extends Abstrac
 
     /**
      * Test if the given node is access controlled. The node is access
-     * controlled if it is of nodetype
+     * controlled if it is of node type
      * {@link AccessControlConstants#NT_REP_ACCESS_CONTROLLABLE "rep:AccessControllable"}
      * and if it has a child node named
      * {@link AccessControlConstants#N_POLICY "rep:ACL"}.
@@ -403,7 +404,8 @@ public class ACLProvider extends Abstrac
     private class AclPermissions extends AbstractCompiledPermissions implements AccessControlListener {
 
         private final List<String> principalNames;
-        private final Map<NodeId, Boolean> readCache = new LRUMap(1000);
+        // TODO find optimal cache size and ev. make it configurable (see also JCR-2573).
+        private final Map<ItemId, Boolean> readCache = new LRUMap(5000);
         private final Object monitor = new Object();
 
         private AclPermissions(Set<Principal> principals) throws RepositoryException {
@@ -425,7 +427,7 @@ public class ACLProvider extends Abstrac
             }
         }
 
-        private Result buildResult(NodeImpl node, boolean existingNode, boolean isAcItem, EntryFilter filter) throws RepositoryException {
+        private Result buildResult(NodeImpl node, boolean isExistingNode, boolean isAcItem, EntryFilterImpl filter) throws RepositoryException {
             // retrieve all ACEs at path or at the direct ancestor of path that
             // apply for the principal names.
             Iterator<AccessControlEntry> entries = retrieveResultEntries(getNode(node, isAcItem), filter);
@@ -444,17 +446,22 @@ public class ACLProvider extends Abstrac
             int parentAllows = PrivilegeRegistry.NO_PRIVILEGE;
             int parentDenies = PrivilegeRegistry.NO_PRIVILEGE;
 
+            String parentPath = Text.getRelativeParent(filter.getPath(), 1);
+
             while (entries.hasNext()) {
                 ACLTemplate.Entry ace = (ACLTemplate.Entry) entries.next();
                 /*
-                 Determine if the ACE is defined on the node at absPath (locally):
-                 Except for READ-privileges the permissions must be determined
-                 from privileges defined for the parent. Consequently aces
-                 defined locally must be treated different than inherited entries.
+                 Determine if the ACE also takes effect on the parent:
+                 Some permissions (e.g. add-node or removal) must be determined
+                 from privileges defined for the parent.
+                 A 'local' entry defined on the target node never effects the
+                 parent. For inherited ACEs determine if the ACE matches the
+                 parent path.
                  */
                 int entryBits = ace.getPrivilegeBits();
-                boolean isLocal = existingNode && ace.isLocal(node.getNodeId());
-                if (!isLocal) {
+                boolean isLocal = isExistingNode && ace.isLocal(node.getNodeId());
+                boolean matchesParent = (!isLocal && ace.matches(parentPath));
+                if (matchesParent) {
                     if (ace.isAllow()) {
                         parentAllows |= Permission.diff(entryBits, parentDenies);
                     } else {
@@ -516,7 +523,7 @@ public class ACLProvider extends Abstrac
             }
 
             boolean isAcItem = isAcItem(absPath);
-            return buildResult(node, existingNode, isAcItem, new EntryFilterImpl(principalNames));
+            return buildResult(node, existingNode, isAcItem, new EntryFilterImpl(principalNames, absPath, session));
         }
 
         /**
@@ -545,32 +552,22 @@ public class ACLProvider extends Abstrac
          */
         public boolean canRead(Path path, ItemId itemId) throws RepositoryException {
             ItemId id = (itemId == null) ? session.getHierarchyManager().resolvePath(path) : itemId;
-            /* currently READ access cannot be denied to individual properties.
-               if the parent node is readable the properties are as well.
-               this simplifies the canRead test as well as the caching.
-             */
-            boolean existingNode = false;
-            NodeId nodeId;
-            if (id.denotesNode()) {
-                nodeId = (NodeId) id;
-                // since method may only be called for existing nodes the
-                // flag be set to true if the id identifies a node.
-                existingNode = true;
-            } else {
-                nodeId = ((PropertyId) id).getParentId();
-            }
-
+            // no extra check for existence as method may only be called for existing items.
+            boolean isExistingNode = id.denotesNode();
             boolean canRead;
             synchronized (monitor) {
-                if (readCache.containsKey(nodeId)) {
-                    canRead = readCache.get(nodeId);
+                if (readCache.containsKey(id)) {
+                    canRead = readCache.get(id);
                 } else {
                     ItemManager itemMgr = session.getItemManager();
+                    NodeId nodeId = (isExistingNode) ? (NodeId) id : ((PropertyId) id).getParentId();
                     NodeImpl node = (NodeImpl) itemMgr.getItem(nodeId);
-                    Result result = buildResult(node, existingNode, isAcItem(node), new EntryFilterImpl(principalNames));
+                    // TODO: check again if retrieving the path can be avoided
+                    Path absPath = (path == null) ? session.getHierarchyManager().getPath(id) : path;
+                    Result result = buildResult(node, isExistingNode, isAcItem(node), new EntryFilterImpl(principalNames, absPath, session));
 
                     canRead = result.grants(Permission.READ);
-                    readCache.put(nodeId, canRead);
+                    readCache.put(id, canRead);
                 }
             }
             return canRead;

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplate.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplate.java?rev=983906&r1=983905&r2=983906&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplate.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplate.java Tue Aug 10 09:58:03 2010
@@ -22,6 +22,7 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
+import javax.jcr.NamespaceException;
 import javax.jcr.NodeIterator;
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
@@ -39,12 +40,14 @@ import org.apache.jackrabbit.core.Sessio
 import org.apache.jackrabbit.core.id.NodeId;
 import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
 import org.apache.jackrabbit.core.security.authorization.AbstractACLTemplate;
-import org.apache.jackrabbit.core.security.authorization.AccessControlConstants;
 import org.apache.jackrabbit.core.security.authorization.AccessControlEntryImpl;
 import org.apache.jackrabbit.core.security.authorization.Permission;
 import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry;
+import org.apache.jackrabbit.core.security.authorization.GlobPattern;
 import org.apache.jackrabbit.core.security.principal.PrincipalImpl;
 import org.apache.jackrabbit.core.security.principal.UnknownPrincipal;
+import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
+import org.apache.jackrabbit.spi.commons.conversion.NameResolver;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -62,8 +65,7 @@ class ACLTemplate extends AbstractACLTem
     private static final Logger log = LoggerFactory.getLogger(ACLTemplate.class);
 
     /**
-     * List containing the entries of this ACL Template with maximal one
-     * grant and one deny ACE per principal.
+     * List containing the entries of this ACL Template.
      */
     private final List<Entry> entries = new ArrayList<Entry>();
 
@@ -78,6 +80,11 @@ class ACLTemplate extends AbstractACLTem
     private final PrivilegeRegistry privilegeRegistry;
 
     /**
+     * The name resolver
+     */
+    private final NameResolver resolver;
+
+    /**
      * The id of the access controlled node or <code>null</code> if this
      * ACLTemplate isn't created for an existing access controlled node.
      * Used for the Entry#isLocal(NodeId) call only in order to avoid calls
@@ -86,19 +93,30 @@ class ACLTemplate extends AbstractACLTem
     private final NodeId id;
 
     /**
+     *
+     */
+    private final String jcrRepGlob;
+
+    /**
      * Construct a new empty {@link ACLTemplate}.
      *
      * @param path path
      * @param privilegeRegistry registry
      * @param valueFactory value factory
+     * @param resolver
      * @param principalMgr manager
+     * @throws javax.jcr.NamespaceException
      */
     ACLTemplate(String path, PrincipalManager principalMgr, 
-                PrivilegeRegistry privilegeRegistry, ValueFactory valueFactory) {
+                PrivilegeRegistry privilegeRegistry, ValueFactory valueFactory,
+                NamePathResolver resolver) throws NamespaceException {
         super(path, valueFactory);
         this.principalMgr = principalMgr;
         this.privilegeRegistry = privilegeRegistry;
+        this.resolver = resolver;
         this.id = null;
+
+        jcrRepGlob = resolver.getJCRName(P_GLOB);
     }
 
     /**
@@ -111,14 +129,16 @@ class ACLTemplate extends AbstractACLTem
      */
     ACLTemplate(NodeImpl aclNode, PrivilegeRegistry privilegeRegistry) throws RepositoryException {
         super((aclNode != null) ? aclNode.getParent().getPath() : null, (aclNode != null) ? aclNode.getSession().getValueFactory() : null);
-        if (aclNode == null || !AccessControlConstants.NT_REP_ACL.equals(((NodeTypeImpl)aclNode.getPrimaryNodeType()).getQName())) {
+        if (aclNode == null || !NT_REP_ACL.equals(((NodeTypeImpl)aclNode.getPrimaryNodeType()).getQName())) {
             throw new IllegalArgumentException("Node must be of type 'rep:ACL'");
         }
         SessionImpl sImpl = (SessionImpl) aclNode.getSession();
         principalMgr = sImpl.getPrincipalManager();
 
         this.privilegeRegistry = privilegeRegistry;
+        this.resolver = sImpl;
         this.id = aclNode.getParentId();
+        jcrRepGlob = sImpl.getJCRName(P_GLOB);
 
         // load the entries:
         AccessControlManager acMgr = sImpl.getAccessControlManager();
@@ -126,23 +146,29 @@ class ACLTemplate extends AbstractACLTem
         while (itr.hasNext()) {
             NodeImpl aceNode = (NodeImpl) itr.nextNode();
             try {
-                String principalName = aceNode.getProperty(AccessControlConstants.P_PRINCIPAL_NAME).getString();
+                String principalName = aceNode.getProperty(P_PRINCIPAL_NAME).getString();
                 Principal princ = principalMgr.getPrincipal(principalName);
                 if (princ == null) {
                     log.debug("Principal with name " + principalName + " unknown to PrincipalManager.");
                     princ = new PrincipalImpl(principalName);
                 }
 
-                Value[] privValues = aceNode.getProperty(AccessControlConstants.P_PRIVILEGES).getValues();
+                Value[] privValues = aceNode.getProperty(P_PRIVILEGES).getValues();
                 Privilege[] privs = new Privilege[privValues.length];
                 for (int i = 0; i < privValues.length; i++) {
                     privs[i] = acMgr.privilegeFromName(privValues[i].getString());
                 }
+
+                Map<String,Value> restrictions = null;
+                if (aceNode.hasProperty(P_GLOB)) {
+                    restrictions = Collections.singletonMap(jcrRepGlob, aceNode.getProperty(P_GLOB).getValue());
+                }
                 // create a new ACEImpl (omitting validation check)
                 Entry ace = createEntry(
                         princ,
                         privs,
-                        AccessControlConstants.NT_REP_GRANT_ACE.equals(((NodeTypeImpl) aceNode.getPrimaryNodeType()).getQName()));
+                        NT_REP_GRANT_ACE.equals(((NodeTypeImpl) aceNode.getPrimaryNodeType()).getQName()),
+                        restrictions);
                 // add the entry
                 internalAdd(ace);
             } catch (RepositoryException e) {
@@ -157,10 +183,15 @@ class ACLTemplate extends AbstractACLTem
      * @param principal
      * @param privileges
      * @param isAllow
-     * @return
+     * @param restrictions
+     * @return A new entry
      */
-    Entry createEntry(Principal principal, Privilege[] privileges, boolean isAllow) throws AccessControlException {
-        return new Entry(principal, privileges, isAllow);
+    Entry createEntry(Principal principal, Privilege[] privileges, boolean isAllow, Map<String,Value> restrictions) throws RepositoryException {
+        return new Entry(principal, privileges, isAllow, restrictions);
+    }
+
+    Entry createEntry(Entry base, Privilege[] newPrivileges, boolean isAllow) throws RepositoryException {
+        return new Entry(base, newPrivileges, isAllow);
     }
 
     private List<Entry> internalGetEntries(Principal principal) {
@@ -174,7 +205,7 @@ class ACLTemplate extends AbstractACLTem
         return entriesPerPrincipal;
     }
 
-    private synchronized boolean internalAdd(Entry entry) throws AccessControlException {
+    private synchronized boolean internalAdd(Entry entry) throws RepositoryException {
         Principal principal = entry.getPrincipal();
         List<Entry> entriesPerPrincipal = internalGetEntries(principal);
         if (entriesPerPrincipal.isEmpty()) {
@@ -191,26 +222,29 @@ class ACLTemplate extends AbstractACLTem
             Entry complementEntry = null;
 
             for (Entry e : entriesPerPrincipal) {
-                if (entry.isAllow() == e.isAllow()) {
-                    int existingPrivs = e.getPrivilegeBits();
-                    if ((existingPrivs | ~entry.getPrivilegeBits()) == -1) {
-                        // all privileges to be granted/denied are already present
-                        // in the existing entry -> not modified
-                        return false;
+                if (equalRestriction(entry, e)) {
+                    if (entry.isAllow() == e.isAllow()) {
+                        // need to update an existing entry
+                        int existingPrivs = e.getPrivilegeBits();
+                        if ((existingPrivs | ~entry.getPrivilegeBits()) == -1) {
+                            // all privileges to be granted/denied are already present
+                            // in the existing entry -> not modified
+                            return false;
+                        }
+
+                        // remember the index of the existing entry to be updated later on.
+                        updateIndex = entries.indexOf(e);
+
+                        // remove the existing entry and create a new that includes
+                        // both the new privileges and the existing ones.
+                        entries.remove(e);
+                        int mergedBits = e.getPrivilegeBits() | entry.getPrivilegeBits();
+                        Privilege[] mergedPrivs = privilegeRegistry.getPrivileges(mergedBits);
+                        // omit validation check.
+                        entry = createEntry(entry, mergedPrivs, entry.isAllow());
+                    } else {
+                        complementEntry = e;
                     }
-
-                    // remember the index of the existing entry to be updated later on.
-                    updateIndex = entries.indexOf(e);
-
-                    // remove the existing entry and create a new that includes
-                    // both the new privileges and the existing ones.
-                    entries.remove(e);
-                    int mergedBits = e.getPrivilegeBits() | entry.getPrivilegeBits();
-                    Privilege[] mergedPrivs = privilegeRegistry.getPrivileges(mergedBits);
-                    // omit validation check.
-                    entry = createEntry(entry.getPrincipal(), mergedPrivs, entry.isAllow());
-                } else {
-                    complementEntry = e;
                 }
             }
 
@@ -232,7 +266,7 @@ class ACLTemplate extends AbstractACLTem
                     // replace the existing entry having the privileges adjusted
                     int index = entries.indexOf(complementEntry);
                     entries.remove(complementEntry);
-                    Entry tmpl = createEntry(entry.getPrincipal(),
+                    Entry tmpl = createEntry(entry,
                             privilegeRegistry.getPrivileges(resultPrivs),
                             !entry.isAllow());
                     entries.add(index, tmpl);
@@ -250,6 +284,13 @@ class ACLTemplate extends AbstractACLTem
         }
     }
 
+    private boolean equalRestriction(Entry entry1, Entry entry2) throws RepositoryException {
+        Value v1 = entry1.getRestriction(jcrRepGlob);
+        Value v2 = entry2.getRestriction(jcrRepGlob);
+
+        return (v1 == null) ? v2 == null : v1.equals(v2);
+    }
+
     //------------------------------------------------< AbstractACLTemplate >---
     /**
      * @see AbstractACLTemplate#checkValidEntry(java.security.Principal, javax.jcr.security.Privilege[], boolean, java.util.Map) 
@@ -258,9 +299,6 @@ class ACLTemplate extends AbstractACLTem
     protected void checkValidEntry(Principal principal, Privilege[] privileges,
                                  boolean isAllow, Map<String, Value> restrictions)
             throws AccessControlException {
-        if (restrictions != null && !restrictions.isEmpty()) {
-            throw new AccessControlException("This AccessControlList does not allow for additional restrictions.");
-        }
         // validate principal
         if (principal instanceof UnknownPrincipal) {
             log.debug("Consider fallback principal as valid: {}", principal.getName());
@@ -295,32 +333,36 @@ class ACLTemplate extends AbstractACLTem
 
     //----------------------------------------< JackrabbitAccessControlList >---
     /**
-     * Returns an empty String array.
-     *
-     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#getRestrictionType(String)
+     * @see JackrabbitAccessControlList#getRestrictionNames()
      */
     public String[] getRestrictionNames() {
-        return new String[0];
+        return new String[] {jcrRepGlob};
     }
 
     /**
-     * Always returns {@link PropertyType#UNDEFINED} as no restrictions are
-     * supported.
-     *
      * @see JackrabbitAccessControlList#getRestrictionType(String)
      */
     public int getRestrictionType(String restrictionName) {
-        return PropertyType.UNDEFINED;
+        if (jcrRepGlob.equals(restrictionName) || P_GLOB.toString().equals(restrictionName)) {
+            return PropertyType.STRING;
+        } else {
+            return PropertyType.UNDEFINED;
+        }
     }
 
     /**
+     * The only known restriction is:
+     * <pre>
+     *   rep:glob (optional)  value-type: STRING
+     * </pre>
+     *
      * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#addEntry(Principal, Privilege[], boolean, Map)
      */
     public boolean addEntry(Principal principal, Privilege[] privileges,
                             boolean isAllow, Map<String, Value> restrictions)
             throws AccessControlException, RepositoryException {
         checkValidEntry(principal, privileges, isAllow, restrictions);
-        Entry ace = createEntry(principal, privileges, isAllow);
+        Entry ace = createEntry(principal, privileges, isAllow, restrictions);
         return internalAdd(ace);
     }
 
@@ -363,11 +405,29 @@ class ACLTemplate extends AbstractACLTem
      */
     class Entry extends AccessControlEntryImpl {
 
-        private Entry(Principal principal, Privilege[] privileges, boolean allow)
-                throws AccessControlException {
-            super(principal, privileges, allow, Collections.<String, Value>emptyMap(), valueFactory);
+        private final GlobPattern pattern;
+
+        private Entry(Principal principal, Privilege[] privileges, boolean allow, Map<String,Value> restrictions)
+                throws RepositoryException {
+            super(principal, privileges, allow, restrictions);
+            Value glob = getRestrictions().get(P_GLOB);
+            if (glob != null) {
+                pattern = GlobPattern.create(path, glob.getString());
+            } else {
+                pattern = GlobPattern.create(path);
+            }
         }
 
+        private Entry(Entry base, Privilege[] newPrivileges, boolean isAllow) throws RepositoryException {
+            super(base, newPrivileges, isAllow);
+            Value glob = getRestrictions().get(P_GLOB);
+            if (glob != null) {
+                pattern = GlobPattern.create(path, glob.getString());
+            } else {
+                pattern = GlobPattern.create(path);
+            }
+        }
+        
         /**
          * @param nodeId
          * @return <code>true</code> if this entry is defined on the node
@@ -376,5 +436,24 @@ class ACLTemplate extends AbstractACLTem
         boolean isLocal(NodeId nodeId) {
             return id != null && id.equals(nodeId);
         }
+
+        /**
+         * 
+         * @param jcrPath
+         * @return
+         */
+        boolean matches(String jcrPath) {
+            return pattern.matches(jcrPath);
+        }
+
+        @Override
+        protected NameResolver getResolver() {
+            return resolver;
+        }
+
+        @Override
+        protected ValueFactory getValueFactory() {
+            return valueFactory;
+        }
     }
 }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/EntryFilterImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/EntryFilterImpl.java?rev=983906&r1=983905&r2=983906&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/EntryFilterImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/EntryFilterImpl.java Tue Aug 10 09:58:03 2010
@@ -16,9 +16,12 @@
  */
 package org.apache.jackrabbit.core.security.authorization.acl;
 
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.commons.conversion.PathResolver;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.jcr.NamespaceException;
 import javax.jcr.security.AccessControlEntry;
 import java.security.acl.Group;
 import java.util.Collection;
@@ -35,9 +38,15 @@ class EntryFilterImpl implements EntryFi
     private static final Logger log = LoggerFactory.getLogger(EntryFilterImpl.class);
 
     private final Collection<String> principalNames;
+    private final Path path;
+    private final PathResolver resolver;
 
-    EntryFilterImpl(Collection<String> principalNames) {
+    private String itemPath;
+
+    EntryFilterImpl(Collection<String> principalNames, Path path, PathResolver resolver) {
         this.principalNames = principalNames;
+        this.path = path;
+        this.resolver = resolver;
     }
 
     /**
@@ -59,7 +68,7 @@ class EntryFilterImpl implements EntryFi
             // first collect aces present on the given aclNode.
             for (AccessControlEntry ace : entries) {
                 // only process ace if 'principalName' is contained in the given set
-                if (principalNames == null || principalNames.contains(ace.getPrincipal().getName())) {
+                if (matches(ace)) {
                     // add it to the proper list (e.g. separated by principals)
                     /**
                      * NOTE: access control entries must be collected in reverse
@@ -76,4 +85,32 @@ class EntryFilterImpl implements EntryFi
             log.warn("Filtering aborted. Expected 2 result lists.");
         }
     }
+
+    private boolean matches(AccessControlEntry ace) {
+        if (principalNames == null || principalNames.contains(ace.getPrincipal().getName())) {
+            if (((ACLTemplate.Entry) ace).getRestrictions().isEmpty()) {
+                // short cut: there is no glob-restriction -> the entry matches
+                // because it is either defined on the node or inherited.
+                return true;
+            } else {
+                // there is a glob-restriction: check if the target path matches
+                // this entry.
+                try {
+                    return ((ACLTemplate.Entry) ace).matches(getPath());
+                } catch (NamespaceException e) {
+                    log.error("Cannot determine ACE match.", e);
+                }
+            }
+        }
+
+        // doesn't match this filter -> ignore
+        return false;
+    }
+
+    String getPath() throws NamespaceException {
+        if (itemPath == null) {
+            itemPath = resolver.getJCRPath(path);
+        }
+        return itemPath;
+    }
 }
\ No newline at end of file

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLEditor.java?rev=983906&r1=983905&r2=983906&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLEditor.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLEditor.java Tue Aug 10 09:58:03 2010
@@ -21,7 +21,6 @@ import javax.jcr.security.AccessControlE
 import javax.jcr.security.Privilege;
 import javax.jcr.security.AccessControlPolicy;
 import org.apache.jackrabbit.api.security.JackrabbitAccessControlPolicy;
-import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry;
 import org.apache.jackrabbit.api.security.user.Authorizable;
 import org.apache.jackrabbit.api.security.user.UserManager;
 import org.apache.jackrabbit.core.NodeImpl;
@@ -29,6 +28,7 @@ import org.apache.jackrabbit.core.Protec
 import org.apache.jackrabbit.core.SessionImpl;
 import org.apache.jackrabbit.core.security.authorization.AccessControlConstants;
 import org.apache.jackrabbit.core.security.authorization.AccessControlEditor;
+import org.apache.jackrabbit.core.security.authorization.AccessControlEntryImpl;
 import org.apache.jackrabbit.core.security.authorization.Permission;
 import org.apache.jackrabbit.api.security.principal.ItemBasedPrincipal;
 import org.apache.jackrabbit.core.security.principal.PrincipalImpl;
@@ -48,6 +48,7 @@ import javax.jcr.ValueFactory;
 import javax.jcr.PropertyType;
 import javax.jcr.NodeIterator;
 import java.security.Principal;
+import java.util.Set;
 
 /**
  * <code>ACLEditor</code>...
@@ -206,7 +207,7 @@ public class ACLEditor extends Protected
         /* add all new entries defined on the template */
         AccessControlEntry[] aces = acl.getAccessControlEntries();
         for (AccessControlEntry ace1 : aces) {
-            JackrabbitAccessControlEntry ace = (JackrabbitAccessControlEntry) ace1;
+            AccessControlEntryImpl ace = (AccessControlEntryImpl) ace1;
 
             // create the ACE node
             Name nodeName = getUniqueNodeName(aclNode, "entry");
@@ -225,11 +226,10 @@ public class ACLEditor extends Protected
             setProperty(aceNode, P_PRIVILEGES, vs);
 
             // store the restrictions:
-            String[] restrNames = ace.getRestrictionNames();
-            for (String restrName : restrNames) {
-                Name pName = session.getQName(restrName);
+            Set<Name> restrNames = ace.getRestrictions().keySet();
+            for (Name restrName : restrNames) {
                 Value value = ace.getRestriction(restrName);
-                setProperty(aceNode, pName, value);
+                setProperty(aceNode, restrName, value);
             }
         }
 

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLProvider.java?rev=983906&r1=983905&r2=983906&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLProvider.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLProvider.java Tue Aug 10 09:58:03 2010
@@ -109,7 +109,6 @@ public class ACLProvider extends Abstrac
                 ValueFactory vf = session.getValueFactory();
                 Map<String, Value> restrictions = new HashMap<String, Value>();
                 restrictions.put(session.getJCRName(ACLTemplate.P_NODE_PATH), vf.createValue(root.getPath(), PropertyType.PATH));
-                restrictions.put(session.getJCRName(ACLTemplate.P_GLOB), vf.createValue(GlobPattern.WILDCARD_ALL));
 
                 PrincipalManager pMgr = session.getPrincipalManager();
                 AccessControlManager acMgr = session.getAccessControlManager();
@@ -307,7 +306,8 @@ public class ACLProvider extends Abstrac
 
         private boolean canReadAll;
 
-        private final Map<ItemId, Boolean> readCache = new LRUMap(2000);
+        // TODO find optimal cache size and ev. make it configurable (see also JCR-2573).        
+        private final Map<ItemId, Boolean> readCache = new LRUMap(5000);
         private final Object monitor = new Object();
 
         /**
@@ -351,8 +351,7 @@ public class ACLProvider extends Abstrac
             // and retrieve the entries from the entry-collector.
             entries = entriesCache.getEntries(principals);
             
-            // in addition: trivial check if read access is deny somewhere
-            // as as shortcut in #canRead(Path)
+            // in addition: trivial check if read access is denied somewhere
             canReadAll = canRead(session.getQPath("/"));            
             if (canReadAll) {
                 for (AccessControlEntry entry : entries) {

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLTemplate.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLTemplate.java?rev=983906&r1=983905&r2=983906&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLTemplate.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLTemplate.java Tue Aug 10 09:58:03 2010
@@ -21,8 +21,10 @@ import org.apache.jackrabbit.core.NodeIm
 import org.apache.jackrabbit.core.SessionImpl;
 import org.apache.jackrabbit.core.security.authorization.AccessControlEntryImpl;
 import org.apache.jackrabbit.core.security.authorization.AbstractACLTemplate;
+import org.apache.jackrabbit.core.security.authorization.GlobPattern;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
+import org.apache.jackrabbit.spi.commons.conversion.NameResolver;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -61,12 +63,6 @@ class ACLTemplate extends AbstractACLTem
      * node itself).
      */
     static final Name P_NODE_PATH = NF.create(Name.NS_REP_URI, "nodePath");
-    /**
-     * rep:glob property name used to restrict the number of child nodes
-     * or properties that are affected by the privileges applied at
-     * rep:nodePath
-     */
-    static final Name P_GLOB = NF.create(Name.NS_REP_URI, "glob");
 
     private final Principal principal;
 
@@ -75,6 +71,8 @@ class ACLTemplate extends AbstractACLTem
     private final String jcrNodePathName;
     private final String jcrGlobName;
 
+    private final NameResolver resolver;
+
     ACLTemplate(Principal principal, String path, NamePathResolver resolver, ValueFactory vf)
             throws RepositoryException {
         this(principal, path, null, resolver, vf);
@@ -95,6 +93,8 @@ class ACLTemplate extends AbstractACLTem
         jcrNodePathName = resolver.getJCRName(P_NODE_PATH);
         jcrGlobName = resolver.getJCRName(P_GLOB);
 
+        this.resolver = resolver;
+
         if (acNode != null && acNode.hasNode(N_POLICY)) {
             // build the list of policy entries;
             NodeImpl aclNode = acNode.getNode(N_POLICY);
@@ -132,23 +132,34 @@ class ACLTemplate extends AbstractACLTem
     }
 
     AccessControlEntry createEntry(Principal princ, Privilege[] privileges,
-                                   boolean allow, Map<String, Value> restrictions)
-            throws RepositoryException {
-        checkValidEntry(princ, privileges, allow, restrictions);
+                                   boolean allow, Map<String, Value> restrictions) throws RepositoryException {
+        // adjust restrictions if necessary
+        Map<String, Value> rest = adjustRestrictions(restrictions);
+        checkValidEntry(princ, privileges, allow, rest);
+        return new Entry(princ, privileges, allow, rest);
+    }
 
+    private Map<String, Value> adjustRestrictions(Map<String, Value> restrictions) throws RepositoryException {
         // make sure the nodePath restriction is of type PATH
         Value v = restrictions.get(jcrNodePathName);
-        if (v.getType() != PropertyType.PATH) {
+        if (v == null) {
+            v = restrictions.get(P_NODE_PATH.toString());
+        }
+        if (v != null && v.getType() != PropertyType.PATH) {
             v = valueFactory.createValue(v.getString(), PropertyType.PATH);
             restrictions.put(jcrNodePathName, v);
         }
         // ... and glob is of type STRING.
         v = restrictions.get(jcrGlobName);
+        if (v == null) {
+            v = restrictions.get(P_GLOB.toString());
+        }
         if (v != null && v.getType() != PropertyType.STRING) {
             v = valueFactory.createValue(v.getString(), PropertyType.STRING);
             restrictions.put(jcrGlobName, v);
         }
-        return new Entry(princ, privileges, allow, restrictions);
+
+        return restrictions;
     }
 
     //------------------------------------------------< AbstractACLTemplate >---
@@ -164,7 +175,7 @@ class ACLTemplate extends AbstractACLTem
         }
 
         Set<String> rNames = restrictions.keySet();
-        if (!rNames.contains(jcrNodePathName)) {
+        if (!rNames.contains(jcrNodePathName) && !rNames.contains(P_NODE_PATH.toString())) {
             throw new AccessControlException("Missing mandatory restriction: " + jcrNodePathName);
         }
     }
@@ -189,9 +200,9 @@ class ACLTemplate extends AbstractACLTem
      * @see JackrabbitAccessControlList#getRestrictionType(String)
      */
     public int getRestrictionType(String restrictionName) {
-        if (jcrNodePathName.equals(restrictionName)) {
+        if (jcrNodePathName.equals(restrictionName) || P_NODE_PATH.toString().equals(restrictionName)) {
             return PropertyType.PATH;
-        } else if (jcrGlobName.equals(restrictionName)) {
+        } else if (jcrGlobName.equals(restrictionName) || P_GLOB.toString().equals(restrictionName)) {
             return PropertyType.STRING;
         } else {
             return PropertyType.UNDEFINED;
@@ -294,16 +305,13 @@ class ACLTemplate extends AbstractACLTem
         private Entry(Principal principal, Privilege[] privileges, boolean allow,
                       Map<String, Value> restrictions)
                 throws AccessControlException, RepositoryException {
-            super(principal, privileges, allow, restrictions, valueFactory);
+            super(principal, privileges, allow, restrictions);
 
-            // TODO: review again
-            Value np = getRestriction(jcrNodePathName);
-            nodePath = getRestriction(jcrNodePathName).getString();
-            Value glob = getRestriction(jcrGlobName);
+            Map<Name, Value> rstr = getRestrictions();
+            nodePath = rstr.get(P_NODE_PATH).getString();
+            Value glob = rstr.get(P_GLOB);
             if (glob != null) {
-                StringBuffer b = new StringBuffer(nodePath);
-                b.append(glob.getString());
-                pattern = GlobPattern.create(b.toString());
+                pattern = GlobPattern.create(nodePath, glob.getString());
             } else {
                 pattern = GlobPattern.create(nodePath);
             }
@@ -320,5 +328,15 @@ class ACLTemplate extends AbstractACLTem
         boolean matchesNodePath(String jcrPath) {
             return nodePath.equals(jcrPath);
         }
+
+        @Override
+        protected NameResolver getResolver() {
+            return resolver;
+        }
+
+        @Override
+        protected ValueFactory getValueFactory() {
+            return valueFactory;
+        }
     }
 }
\ No newline at end of file

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/AbstractWriteTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/AbstractWriteTest.java?rev=983906&r1=983905&r2=983906&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/AbstractWriteTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/AbstractWriteTest.java Tue Aug 10 09:58:03 2010
@@ -32,6 +32,8 @@ import javax.jcr.PathNotFoundException;
 import javax.jcr.Property;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
 import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.observation.Event;
 import javax.jcr.observation.ObservationManager;
@@ -39,9 +41,11 @@ import javax.jcr.security.AccessControlM
 import javax.jcr.security.AccessControlPolicy;
 import javax.jcr.security.Privilege;
 import java.security.Principal;
+import java.util.HashMap;
 import java.util.List;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Map;
 import java.util.UUID;
 
 /**
@@ -1065,6 +1069,207 @@ public abstract class AbstractWriteTest 
         testSession.save();
     }
 
+    /**
+     * Test the rep:glob restriction
+     * 
+     * @throws Exception
+     */
+    public void testGlobRestriction() throws Exception {
+        Session testSession = getTestSession();
+        AccessControlManager testAcMgr = getTestACManager();
+        ValueFactory vf = superuser.getValueFactory();
+        /*
+          precondition:
+          testuser must have READ-only permission on test-node and below
+        */
+        checkReadOnly(path);
+        checkReadOnly(childNPath);
+
+        Node child = superuser.getNode(childNPath).addNode(nodeName3);
+        superuser.save();
+        String childchildPath = child.getPath();
+
+        Privilege[] write = privilegesFromName(PrivilegeRegistry.REP_WRITE);
+        String writeActions = Session.ACTION_ADD_NODE +","+Session.ACTION_REMOVE +","+ Session.ACTION_SET_PROPERTY;
+
+
+        // permissions defined @ path
+        // restriction: grants write priv to all nodeName3 children
+        Map<String, Value> restrictions = new HashMap(getRestrictions(superuser, path));        
+        restrictions.put(AccessControlConstants.P_GLOB.toString(), vf.createValue("/*"+nodeName3));
+        givePrivileges(path, write, restrictions);
+
+        assertFalse(testAcMgr.hasPrivileges(path, write));
+        assertFalse(testSession.hasPermission(path, javax.jcr.Session.ACTION_SET_PROPERTY));
+
+        assertFalse(testAcMgr.hasPrivileges(childNPath, write));
+        assertFalse(testSession.hasPermission(childNPath, javax.jcr.Session.ACTION_SET_PROPERTY));
+
+        assertTrue(testAcMgr.hasPrivileges(childNPath2, write));
+        assertTrue(testSession.hasPermission(childNPath2, Session.ACTION_SET_PROPERTY));
+        assertFalse(testSession.hasPermission(childNPath2, writeActions)); // removal req. rmchildnode privilege on parent.
+
+        assertTrue(testAcMgr.hasPrivileges(childchildPath, write));
+    }
+
+    /**
+     * Test the rep:glob restriction
+     *
+     * @throws Exception
+     */
+    public void testGlobRestriction2() throws Exception {
+        Session testSession = getTestSession();
+        AccessControlManager testAcMgr = getTestACManager();
+        ValueFactory vf = superuser.getValueFactory();
+        /*
+          precondition:
+          testuser must have READ-only permission on test-node and below
+        */
+        checkReadOnly(path);
+        checkReadOnly(childNPath);
+
+        Node child = superuser.getNode(childNPath).addNode(nodeName3);
+        superuser.save();
+        String childchildPath = child.getPath();
+
+        Privilege[] write = privilegesFromName(PrivilegeRegistry.REP_WRITE);
+        Privilege[] addNode = privilegesFromName(Privilege.JCR_ADD_CHILD_NODES);
+        Privilege[] rmNode = privilegesFromName(Privilege.JCR_REMOVE_NODE);
+
+        Map<String, Value> restrictions = new HashMap(getRestrictions(superuser, path));
+
+        // permissions defined @ path
+        // restriction: grants write-priv to nodeName3 grand-children but not direct nodeName3 children.
+        restrictions.put(AccessControlConstants.P_GLOB.toString(), vf.createValue("/*/"+nodeName3));
+        givePrivileges(path, write, restrictions);
+
+        assertFalse(testAcMgr.hasPrivileges(path, write));
+        assertFalse(testAcMgr.hasPrivileges(path, rmNode));
+
+        assertFalse(testAcMgr.hasPrivileges(childNPath, addNode));
+
+        assertFalse(testAcMgr.hasPrivileges(childNPath2, write));
+
+        assertTrue(testAcMgr.hasPrivileges(childchildPath, write));
+    }
+
+    /**
+     * Test the rep:glob restriction
+     *
+     * @throws Exception
+     */
+    public void testGlobRestriction3() throws Exception {
+        Session testSession = getTestSession();
+        AccessControlManager testAcMgr = getTestACManager();
+        ValueFactory vf = superuser.getValueFactory();
+        /*
+          precondition:
+          testuser must have READ-only permission on test-node and below
+        */
+        checkReadOnly(path);
+        checkReadOnly(childNPath);
+
+        Node child = superuser.getNode(childNPath).addNode(nodeName3);
+        superuser.save();
+        String childchildPath = child.getPath();
+
+        Privilege[] write = privilegesFromName(PrivilegeRegistry.REP_WRITE);
+        Privilege[] addNode = privilegesFromName(Privilege.JCR_ADD_CHILD_NODES);
+        String writeActions = Session.ACTION_ADD_NODE +","+Session.ACTION_REMOVE +","+ Session.ACTION_SET_PROPERTY;
+
+        Map<String, Value> restrictions = new HashMap(getRestrictions(superuser, path));
+
+        // permissions defined @ path
+        // restriction: allows write to nodeName3 children
+        restrictions.put(AccessControlConstants.P_GLOB.toString(), vf.createValue("/*/"+nodeName3));
+        givePrivileges(path, write, restrictions);
+        // and grant add-node only at path (no glob restriction)
+        givePrivileges(path, addNode, getRestrictions(superuser, path));
+
+        assertFalse(testAcMgr.hasPrivileges(path, write));
+        assertTrue(testAcMgr.hasPrivileges(path, addNode));
+
+        assertFalse(testAcMgr.hasPrivileges(childNPath, write));
+        assertTrue(testAcMgr.hasPrivileges(childNPath, addNode));
+
+        assertFalse(testAcMgr.hasPrivileges(childNPath2, write));
+        assertTrue(testAcMgr.hasPrivileges(childchildPath, write));
+    }
+
+    /**
+     * Test the rep:glob restriction
+     *
+     * @throws Exception
+     */
+    public void testGlobRestriction4() throws Exception {
+        Session testSession = getTestSession();
+        AccessControlManager testAcMgr = getTestACManager();
+        ValueFactory vf = superuser.getValueFactory();
+        /*
+          precondition:
+          testuser must have READ-only permission on test-node and below
+        */
+        checkReadOnly(path);
+        checkReadOnly(childNPath);
+
+        Node child = superuser.getNode(childNPath).addNode(nodeName3);
+        superuser.save();
+        String childchildPath = child.getPath();
+
+        Privilege[] write = privilegesFromName(PrivilegeRegistry.REP_WRITE);
+        Privilege[] addNode = privilegesFromName(Privilege.JCR_ADD_CHILD_NODES);
+
+        Map<String, Value> restrictions = new HashMap(getRestrictions(superuser, path));
+        restrictions.put(AccessControlConstants.P_GLOB.toString(), vf.createValue("/*"+nodeName3));
+        givePrivileges(path, write, restrictions);
+
+        withdrawPrivileges(childNPath2, addNode, getRestrictions(superuser, childNPath2));
+
+        assertFalse(testAcMgr.hasPrivileges(path, write));
+        assertFalse(testSession.hasPermission(path, javax.jcr.Session.ACTION_REMOVE));
+
+        assertFalse(testAcMgr.hasPrivileges(childNPath, write));
+        assertFalse(testSession.hasPermission(childNPath, javax.jcr.Session.ACTION_REMOVE));
+
+        assertFalse(testAcMgr.hasPrivileges(childNPath2, write));
+
+        assertTrue(testAcMgr.hasPrivileges(childchildPath, write));
+    }
+
+
+    /**
+     * Test the rep:glob restriction
+     *
+     * @throws Exception
+     */
+    public void testCancelInheritanceRestriction() throws Exception {
+        Session testSession = getTestSession();
+        AccessControlManager testAcMgr = getTestACManager();
+        ValueFactory vf = superuser.getValueFactory();
+        /*
+          precondition:
+          testuser must have READ-only permission on test-node and below
+        */
+        checkReadOnly(path);
+        checkReadOnly(childNPath);
+
+        Privilege[] write = privilegesFromName(PrivilegeRegistry.REP_WRITE);
+        Privilege[] addNode = privilegesFromName(Privilege.JCR_ADD_CHILD_NODES);
+
+        Map<String, Value> restrictions = new HashMap(getRestrictions(superuser, path));
+        restrictions.put(AccessControlConstants.P_GLOB.toString(), vf.createValue(""));
+        givePrivileges(path, write, restrictions);
+
+        assertTrue(testAcMgr.hasPrivileges(path, write));
+        assertTrue(testSession.hasPermission(path, Session.ACTION_SET_PROPERTY));
+
+        assertFalse(testAcMgr.hasPrivileges(childNPath, write));
+        assertFalse(testSession.hasPermission(childNPath, Session.ACTION_SET_PROPERTY));
+
+        assertFalse(testAcMgr.hasPrivileges(childNPath2, write));
+        assertFalse(testSession.hasPermission(childNPath2, Session.ACTION_SET_PROPERTY));
+    }
+
     private static Node findPolicyNode(Node start) throws RepositoryException {
         Node policyNode = null;
         if (start.isNodeType("rep:Policy")) {



Mime
View raw message