OAK-2437 : 'shallow' access to a node and it's properties
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/restriction/CurrentPattern.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/restriction/CurrentPattern.java
new file mode 100644
index 0000000..f20d3a6
--- /dev/null
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/restriction/CurrentPattern.java
@@ -0,0 +1,205 @@
+/*
+ * 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.jackrabbit.oak.security.authorization.restriction;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableSet;
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
+import org.apache.jackrabbit.oak.spi.namespace.NamespaceConstants;
+import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants;
+import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionPattern;
+import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.apache.jackrabbit.oak.spi.version.VersionConstants;
+import org.apache.jackrabbit.util.Text;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.jcr.NamespaceRegistry;
+import java.util.Set;
+
+/**
+ * Restriction that limits the effect of a given ACE to the target node where the entry takes effect and optionally it's
+ * properties (or a subset thereof).
+ * 
+ * The following values are allowed for the corresponding multi-valued property:
+ * <table>
+ * <tr><td>empty value array</td><td>restriction applies to the target node only, properties are never included</td></tr>    
+ * <tr><td>value with {@link NodeTypeConstants#RESIDUAL_NAME residual name '*'}</td><td>restriction applies to the target node and all it's properties</td></tr>    
+ * <tr><td>one or multiple property names</td><td>restriction applies to the target node and the specified properties</td></tr>
+ * </table>
+ */
+class CurrentPattern implements RestrictionPattern {
+
+    /**
+     * Built-in namespace prefixes
+     */
+    private static final Set<String> PREFIXES = ImmutableSet.of(
+            NamespaceConstants.PREFIX_OAK, 
+            NamespaceConstants.PREFIX_REP,
+            NamespaceRegistry.PREFIX_JCR);
+
+    /**
+     * Known names of nodes defined by built-in node type definitions, which allows for a best-effort estimate if a given
+     * name with one of the built-in namespace prefixes is a node or a property.
+     * 
+     * NOTE: {@link UserConstants#REP_MEMBERS} and {@link org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants#REP_PRIVILEGES}
+     * are ambiguous as they are defined for both node and property definitions. however, {@link UserConstants#REP_MEMBERS} 
+     * child node definition is deprecated and no longer used in Oak. {@link org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants#REP_PRIVILEGES}
+     * is used for a single node below jcr:system only, while the property name is used in every access control entry.
+     * Therefore these two names are omitted from the list.
+     */
+    private static final Set<String> NODE_NAMES = ImmutableSet.<String>builder().add(
+            JcrConstants.JCR_CHILDNODEDEFINITION, 
+            JcrConstants.JCR_CONTENT, 
+            JcrConstants.JCR_FROZENNODE, 
+            JcrConstants.JCR_PROPERTYDEFINITION, 
+            JcrConstants.JCR_ROOTVERSION,
+            JcrConstants.JCR_SYSTEM, 
+            JcrConstants.JCR_VERSIONLABELS,
+            JcrConstants.JCR_VERSIONSTORAGE,
+            NodeTypeConstants.JCR_NODE_TYPES,
+            NodeTypeConstants.REP_NAMED_CHILD_NODE_DEFINITIONS,
+            NodeTypeConstants.REP_RESIDUAL_CHILD_NODE_DEFINITIONS,
+            NodeTypeConstants.REP_NAMED_PROPERTY_DEFINITIONS,
+            NodeTypeConstants.REP_RESIDUAL_PROPERTY_DEFINITIONS,
+            NodeTypeConstants.REP_OURS,
+            VersionConstants.JCR_ACTIVITIES,
+            VersionConstants.JCR_CONFIGURATIONS,
+            AccessControlConstants.REP_POLICY,
+            AccessControlConstants.REP_REPO_POLICY,
+            AccessControlConstants.REP_RESTRICTIONS,
+            UserConstants.REP_PWD,
+            UserConstants.REP_MEMBERS_LIST,
+            IndexConstants.INDEX_DEFINITIONS_NAME,
+            "rep:cugPolicy",
+            "rep:principalPolicy").build();
+    
+    private final String treePath;
+    private final Set<String> propertyNames;
+
+    CurrentPattern(@NotNull String treePath, @NotNull Iterable<String> propertyNames) {
+        this.treePath = treePath;
+        this.propertyNames = ImmutableSet.copyOf(propertyNames);
+    }
+
+    @Override
+    public boolean matches(@NotNull Tree tree, @Nullable PropertyState property) {
+        String propName = (property == null) ? null : property.getName();
+        return matches(tree.getPath(), propName);
+    }
+
+    @Override
+    public boolean matches(@NotNull String path) {
+        // best-effort attempt to determine if the specified path points to a property. if it cannot be determined,
+        // assume that it points ot a node.
+        String propName = getPropertyNameOrNull(path);
+        String nodePath = (propName == null) ? path : PathUtils.getParentPath(path);
+        return matches(nodePath, propName);
+    }
+
+    @Override
+    public boolean matches(@NotNull String path, boolean isProperty) {
+        if (isProperty) {
+            if (PathUtils.denotesRoot(path)) {
+                return false;
+            }
+            return matches(PathUtils.getParentPath(path),  PathUtils.getName(path));
+        } else {
+            return matches(path, null);
+        }
+    }
+
+    @Override
+    public boolean matches() {
+        // pattern never matches for repository level permissions
+        return false;
+    }
+    
+    private boolean matches(@NotNull String nodePath, @Nullable String propertyName) {
+        if (!this.treePath.equals(nodePath)) {
+            return false;
+        }
+        if (propertyName == null) {
+            // no property name to compare
+            return true;
+        } else {
+            // restriction needs to be evaluated for a property
+            if (propertyNames.isEmpty()) {
+                // only node itself matches
+                return false;
+            } else if (propertyNames.contains(NodeTypeConstants.RESIDUAL_NAME)) {
+                // the node always matches and all properties match if the given name-set is empty
+                return true;
+            } else {
+                // verify that given propName is explicitly part of the restriction
+                return propertyNames.contains(propertyName);
+            }
+        }
+    }
+
+    /**
+     * Best-effort attempt to determine if the specified path points to a property or not. If it cannot be determined,
+     * this method returns {@code null} assuming that the path points to a node.
+     * 
+     * @param path The path as passed to {@link #matches(String)}
+     * @return A non-null string if the given path ends with a name that is known to belong to a property defined by 
+     * a named property definition of a built-in node type. It returns {@code null} if the name either belongs to a 
+     * named child-node definition of a built-in node type or if it is not possible to determined if the given path 
+     * points to a property.
+     */
+    @Nullable
+    private static String getPropertyNameOrNull(@NotNull String path) {
+        if (PathUtils.denotesRoot(path)) {
+            return null;
+        }
+        String name = PathUtils.getName(path);
+        String prefix = Text.getNamespacePrefix(name);
+        if (PREFIXES.contains(prefix) && !NODE_NAMES.contains(name)) {
+            return name;
+        } else {
+            return null;
+        }
+    }
+
+    //-------------------------------------------------------------< Object >---
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(treePath, propertyNames);
+    }
+
+    @Override
+    public String toString() {
+        return treePath + " : " + propertyNames;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj instanceof CurrentPattern) {
+            CurrentPattern other = (CurrentPattern) obj;
+            return treePath.equals(other.treePath) &&  propertyNames.equals(other.propertyNames);
+        }
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/restriction/RestrictionProviderImpl.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/restriction/RestrictionProviderImpl.java
index b98738d..1147f80 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/restriction/RestrictionProviderImpl.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/restriction/RestrictionProviderImpl.java
@@ -64,7 +64,7 @@
 
     private static final Logger log = LoggerFactory.getLogger(RestrictionProviderImpl.class);
 
-    private static final int NUMBER_OF_DEFINITIONS = 3;
+    private static final int NUMBER_OF_DEFINITIONS = 5;
 
     public RestrictionProviderImpl() {
         super(supportedRestrictions());
@@ -76,7 +76,8 @@
         RestrictionDefinition nts = new RestrictionDefinitionImpl(REP_NT_NAMES, Type.NAMES, false);
         RestrictionDefinition pfxs = new RestrictionDefinitionImpl(REP_PREFIXES, Type.STRINGS, false);
         RestrictionDefinition names = new RestrictionDefinitionImpl(REP_ITEM_NAMES, Type.NAMES, false);
-        return ImmutableMap.of(glob.getName(), glob, nts.getName(), nts, pfxs.getName(), pfxs, names.getName(), names);
+        RestrictionDefinition current = new RestrictionDefinitionImpl(REP_CURRENT, Type.STRINGS, false);
+        return ImmutableMap.of(glob.getName(), glob, nts.getName(), nts, pfxs.getName(), pfxs, names.getName(), names, current.getName(), current);
     }
 
     //------------------------------------------------< RestrictionProvider >---
@@ -104,6 +105,10 @@
             if (itemNames != null) {
                 patterns.add(new ItemNamePattern(itemNames.getValue(Type.NAMES)));
             }
+            PropertyState current = tree.getProperty(REP_CURRENT);
+            if (current != null) {
+                patterns.add(new CurrentPattern(oakPath, current.getValue(Type.STRINGS)));
+            }
 
             return CompositePattern.create(patterns);
         }
@@ -126,6 +131,8 @@
                     patterns.add(new PrefixPattern(r.getProperty().getValue(Type.STRINGS)));
                 } else if (REP_ITEM_NAMES.equals(name)) {
                     patterns.add(new ItemNamePattern(r.getProperty().getValue(Type.NAMES)));
+                } else if (REP_CURRENT.equals(name)) {
+                    patterns.add(new CurrentPattern(oakPath, r.getProperty().getValue(Type.STRINGS)));
                 } else {
                     log.debug("Ignoring unsupported restriction {}", name);
                 }
diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/ACLTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/ACLTest.java
index c1bec14..4bcfc5a 100644
--- a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/ACLTest.java
+++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/ACLTest.java
@@ -701,8 +701,8 @@
     public void testRestrictions() throws Exception {
         String[] names = acl.getRestrictionNames();
         assertNotNull(names);
-        assertEquals(4, names.length);
-        assertArrayEquals(new String[] {REP_GLOB, REP_NT_NAMES, REP_PREFIXES, REP_ITEM_NAMES}, names);
+        assertEquals(5, names.length);
+        assertArrayEquals(new String[] {REP_GLOB, REP_NT_NAMES, REP_PREFIXES, REP_ITEM_NAMES, REP_CURRENT}, names);
         assertEquals(PropertyType.STRING, acl.getRestrictionType(names[0]));
         assertEquals(PropertyType.NAME, acl.getRestrictionType(names[1]));
         assertEquals(PropertyType.STRING, acl.getRestrictionType(names[2]));
diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/AbstractRestrictionTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/AbstractRestrictionTest.java
new file mode 100644
index 0000000..a98314d
--- /dev/null
+++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/AbstractRestrictionTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.jackrabbit.oak.security.authorization.restriction;
+
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
+import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
+import org.apache.jackrabbit.oak.AbstractSecurityTest;
+import org.apache.jackrabbit.oak.api.ContentSession;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
+import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants;
+import org.jetbrains.annotations.NotNull;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.ValueFactory;
+import javax.jcr.security.AccessControlManager;
+import java.security.Principal;
+
+public abstract class AbstractRestrictionTest extends AbstractSecurityTest {
+
+    ValueFactory vf;
+    ContentSession testSession;
+    Principal testPrincipal;
+    
+    @Override
+    public void before() throws Exception {
+        super.before();
+
+        Tree rootTree = root.getTree("/");
+
+        // "/a/d/b/e/c/f"
+        Tree a = TreeUtil.addChild(rootTree, "a", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        a.setProperty("propA", "value");
+        Tree d = TreeUtil.addChild(a, "d", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        Tree b = TreeUtil.addChild(d, "b", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        Tree e = TreeUtil.addChild(b, "e", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        Tree c = TreeUtil.addChild(e, "c", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        Tree f = TreeUtil.addChild(c, "f", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        c.setProperty("prop", "value");
+        c.setProperty("a", "value");
+
+        testPrincipal = getTestUser().getPrincipal();
+
+        AccessControlManager acMgr = getAccessControlManager(root);
+        JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(acMgr, "/a");
+
+        vf = getValueFactory(root);
+        if (addEntry(acl)) {
+            acMgr.setPolicy(acl.getPath(), acl);
+        }
+        root.commit();
+        testSession = createTestSession();
+    }
+    
+    abstract boolean addEntry(@NotNull JackrabbitAccessControlList acl) throws RepositoryException;
+
+    @Override
+    public void after() throws Exception {
+        try {
+            testSession.close();
+            root.refresh();
+            Tree a = root.getTree("/a");
+            if (a.exists()) {
+                a.remove();
+                root.commit();
+            }
+        } finally {
+            super.after();
+        }
+    }
+}
\ No newline at end of file
diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/CurrentPatternTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/CurrentPatternTest.java
new file mode 100644
index 0000000..f009b15
--- /dev/null
+++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/CurrentPatternTest.java
@@ -0,0 +1,296 @@
+/*
+ * 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.jackrabbit.oak.security.authorization.restriction;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.atomic.AtomicCounterEditor;
+import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
+import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionPattern;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants.RESIDUAL_NAME;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+public class CurrentPatternTest {
+    
+    private static final String TEST_PATH = "/test/path";
+    private static final String PROP_NAME = "prop";
+    
+    @NotNull
+    private static RestrictionPattern createPattern(@NotNull String... propertyNames) {
+        return new CurrentPattern(TEST_PATH, Arrays.asList(propertyNames));
+    }
+    
+    @NotNull
+    private static Tree mockTree(@NotNull String path) {
+        return when(mock(Tree.class).getPath()).thenReturn(path).getMock();
+    }
+
+    @NotNull
+    private static PropertyState mockProperty(@NotNull String name) {
+        return when(mock(PropertyState.class).getName()).thenReturn(name).getMock();
+    }
+    
+    @Test
+    public void testMatches() {
+        assertFalse(createPattern().matches());
+        assertFalse(createPattern(RESIDUAL_NAME).matches());
+        assertFalse(createPattern(PROP_NAME).matches());
+        assertFalse(createPattern("a", "b", "c").matches());
+    }
+
+    @Test
+    public void testMatchesTreePropertyPathMismatch() {
+        Tree t = mockTree(TEST_PATH + "/mismatch");
+        PropertyState p = mockProperty(PROP_NAME);
+
+        RestrictionPattern rp = createPattern();
+        assertFalse(rp.matches(t, null));
+        assertFalse(rp.matches(t, p));
+
+        rp = createPattern(PROP_NAME);
+        assertFalse(rp.matches(t, null));
+        assertFalse(rp.matches(t, p));
+        
+        rp = createPattern(RESIDUAL_NAME);
+        assertFalse(rp.matches(t, null));
+        assertFalse(rp.matches(t, p));
+        
+        verify(t, times(6)).getPath();
+        verify(p, times(3)).getName();
+        verifyNoMoreInteractions(t,p);
+    }
+
+    @Test
+    public void testMatchesTreePropertyNoPropertyNames() {
+        Tree t = mockTree(TEST_PATH);
+        PropertyState p = mockProperty(PROP_NAME);
+        
+        RestrictionPattern rp = createPattern();
+        assertTrue(rp.matches(t, null));
+        assertFalse(rp.matches(t, p));
+        
+        verify(t, times(2)).getPath();
+        verify(p).getName();
+        verifyNoMoreInteractions(t, p);
+    }
+
+    @Test
+    public void testMatchesTreePropertyAllPropertyNames() {
+        Tree t = mockTree(TEST_PATH);
+        PropertyState p = mockProperty(PROP_NAME);
+
+        RestrictionPattern rp = createPattern(RESIDUAL_NAME);
+        assertTrue(rp.matches(t, null));
+        assertTrue(rp.matches(t, p));
+
+        verify(t, times(2)).getPath();
+        verify(p).getName();
+        verifyNoMoreInteractions(t, p);
+    }
+
+    @Test
+    public void testMatchesTreePropertyPropertyNames() {
+        Tree t = mockTree(TEST_PATH);
+        PropertyState p = mockProperty(PROP_NAME);
+        PropertyState p2 = mockProperty("a");
+
+        // matching property names
+        RestrictionPattern rp = createPattern(PROP_NAME, "a");
+        assertTrue(rp.matches(t, null));
+        assertTrue(rp.matches(t, p));
+        assertTrue(rp.matches(t, p2));
+
+        // property names don't match
+        rp = createPattern("other");
+        assertTrue(rp.matches(t, null));
+        assertFalse(rp.matches(t, p));
+        assertFalse(rp.matches(t, p2));
+
+        verify(t, times(6)).getPath();
+        verify(p, times(2)).getName();
+        verify(p2, times(2)).getName();
+        verifyNoMoreInteractions(t, p, p2);
+    }
+
+    @Test
+    public void testMatchesPath() {
+        List<String[]> propNames = ImmutableList.of(new String[0], new String[]{RESIDUAL_NAME}, new String[] {PROP_NAME});
+        for (String[] pn : propNames) {
+            RestrictionPattern rp = createPattern(pn);
+            assertTrue(rp.matches(TEST_PATH));
+            assertFalse(rp.matches("/another/path"));
+            assertFalse(rp.matches(PathUtils.ROOT_PATH));
+            assertFalse(rp.matches(TEST_PATH + "/" + PROP_NAME));
+        }
+    }
+
+    @Test
+    public void testMatchesPathPointsToKnownProperty() {
+        String[] knownPropertyNames = new String[] {
+                JcrConstants.JCR_PRIMARYTYPE, 
+                AccessControlConstants.REP_PRINCIPAL_NAME,
+                AtomicCounterEditor.PROP_COUNTER};
+
+        // pattern without any property-names will not match any of the paths.
+        RestrictionPattern rp = createPattern();
+        for (String pn : knownPropertyNames) {
+            assertFalse(rp.matches(PathUtils.concat(TEST_PATH, pn)));
+        }
+        
+        // pattern with * or all propnames will match
+        List<String[]> propNames = ImmutableList.of(new String[]{RESIDUAL_NAME}, knownPropertyNames);
+        for (String[] names : propNames) {
+            rp = createPattern(names);
+            for (String pn : knownPropertyNames) {
+                assertTrue(rp.matches(PathUtils.concat(TEST_PATH, pn)));
+            }
+            assertFalse(rp.matches(PathUtils.concat(TEST_PATH, "otherPrefix:propName")));
+            assertFalse(rp.matches(PathUtils.concat(TEST_PATH, "propName")));
+        }
+
+        // pattern with different set of propnames won't match
+        rp = createPattern(JcrConstants.JCR_DATA);
+        for (String pn : knownPropertyNames) {
+            assertFalse(rp.matches(PathUtils.concat(TEST_PATH, pn)));
+            assertFalse(rp.matches(PathUtils.concat(TEST_PATH, "otherPrefix:propName")));
+            assertFalse(rp.matches(PathUtils.concat(TEST_PATH, "propName")));
+        }
+    }
+
+    @Test
+    public void testMatchesPathPointsToKnownNode() {
+        String[] knownNodeNames = new String[] {
+                JcrConstants.JCR_CONTENT,
+                AccessControlConstants.REP_POLICY,
+                IndexConstants.INDEX_DEFINITIONS_NAME};
+
+        // pattern without any property-names will not match any of the paths.
+        RestrictionPattern rp = createPattern();
+        for (String pn : knownNodeNames) {
+            assertFalse(rp.matches(PathUtils.concat(TEST_PATH, pn)));
+        }
+
+        // pattern with * or all node-names will NOT match because they point to items that are known to be nodes
+        List<String[]> propNames = ImmutableList.of(new String[]{RESIDUAL_NAME}, knownNodeNames);
+        for (String[] names : propNames) {
+            rp = createPattern(names);
+            for (String pn : knownNodeNames) {
+                assertFalse(rp.matches(PathUtils.concat(TEST_PATH, pn)));
+            }
+        }
+    }
+
+    @Test
+    public void testMatchesPathIsPropertyFalse() {
+        RestrictionPattern rp = createPattern();
+        assertTrue(rp.matches(TEST_PATH, false));
+        assertFalse(rp.matches("/another/path", false));
+        assertFalse(rp.matches(PathUtils.ROOT_PATH, false));
+        assertFalse(rp.matches(TEST_PATH + "/" + PROP_NAME, false));
+
+        rp = createPattern(RESIDUAL_NAME);
+        assertTrue(rp.matches(TEST_PATH, false));
+        assertFalse(rp.matches("/another/path", false));
+        assertFalse(rp.matches(PathUtils.ROOT_PATH, false));
+        assertFalse(rp.matches(TEST_PATH + "/" + PROP_NAME, false));
+
+        rp = createPattern(PROP_NAME);
+        assertTrue(rp.matches(TEST_PATH, false));
+        assertFalse(rp.matches("/another/path", false));
+        assertFalse(rp.matches(PathUtils.ROOT_PATH, false));
+        assertFalse(rp.matches(TEST_PATH + "/" + PROP_NAME, false));
+    }
+
+    @Test
+    public void testMatchesPathIsPropertyTrue() {
+        RestrictionPattern rp = createPattern();
+        assertFalse(rp.matches(TEST_PATH, true));
+        assertFalse(rp.matches("/another/path", true));
+        assertFalse(rp.matches(PathUtils.ROOT_PATH, true));
+        assertFalse(rp.matches(TEST_PATH + "/" + PROP_NAME, true));
+
+        rp = createPattern(RESIDUAL_NAME);
+        assertFalse(rp.matches(TEST_PATH, true));
+        assertFalse(rp.matches("/another/path", true));
+        assertFalse(rp.matches(PathUtils.ROOT_PATH, true));
+        assertTrue(rp.matches(TEST_PATH + "/" + PROP_NAME, true));
+        assertTrue(rp.matches(TEST_PATH + "/another", true));
+
+        rp = createPattern(PROP_NAME);
+        assertFalse(rp.matches(TEST_PATH, true));
+        assertFalse(rp.matches("/another/path", true));
+        assertFalse(rp.matches(PathUtils.ROOT_PATH, true));
+        assertTrue(rp.matches(TEST_PATH + "/" + PROP_NAME, true));
+        assertFalse(rp.matches(TEST_PATH + "/another", true));
+    }
+
+    @Test
+    public void testToString() {
+        assertEquals(createPattern(PROP_NAME).toString(), createPattern(PROP_NAME).toString());
+        assertNotEquals(createPattern(RESIDUAL_NAME).toString(), createPattern(PROP_NAME).toString());
+        assertNotEquals(createPattern(RESIDUAL_NAME).toString(), createPattern().toString());
+    }
+
+    @Test
+    public void testHashCode() {
+        RestrictionPattern rp = createPattern(PROP_NAME);
+        assertEquals(rp.hashCode(), createPattern(PROP_NAME).hashCode());
+        assertNotEquals(rp.hashCode(), createPattern().hashCode());
+        assertNotEquals(rp.hashCode(), createPattern(RESIDUAL_NAME).hashCode());
+        assertNotEquals(rp.hashCode(), createPattern(PROP_NAME, "a", "b").hashCode());
+    }
+
+    @Test
+    public void testEquals() {
+        RestrictionPattern rp = createPattern(PROP_NAME, "a", "b");
+        assertEquals(rp, rp);
+        assertEquals(rp, createPattern(PROP_NAME, "a", "b"));
+    }
+
+    @Test
+    public void testNotEquals() {
+        RestrictionPattern rp = createPattern(RESIDUAL_NAME);
+        // different tree path
+        assertNotEquals(rp, new CurrentPattern("/another/path", Collections.singleton(RESIDUAL_NAME)));
+        // different set of prop names
+        assertNotEquals(rp, createPattern());
+        assertNotEquals(rp, createPattern(PROP_NAME));
+        // different restrictions
+        assertNotEquals(rp, new ItemNamePattern(ImmutableSet.of("a", "b")));
+        assertNotEquals(rp, new PrefixPattern(ImmutableSet.of("a", "b", "c")));
+    }
+}
diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/CurrentRestrictionTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/CurrentRestrictionTest.java
new file mode 100644
index 0000000..53f4603
--- /dev/null
+++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/CurrentRestrictionTest.java
@@ -0,0 +1,193 @@
+/*
+ * 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.jackrabbit.oak.security.authorization.restriction;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
+import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
+import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
+import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants;
+import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Test;
+
+import javax.jcr.Value;
+import javax.jcr.security.AccessControlManager;
+import java.util.Collections;
+
+import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants.JCR_ADD_CHILD_NODES;
+import static org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants.JCR_MODIFY_PROPERTIES;
+import static org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants.JCR_READ;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class CurrentRestrictionTest extends AbstractRestrictionTest {
+
+    @Override
+    boolean addEntry(@NotNull JackrabbitAccessControlList acl) {
+        return false;
+    }
+
+    @Test
+    public void testDefinedProperties() throws Exception {
+        AccessControlManager acMgr = getAccessControlManager(root);
+        JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(acMgr, "/a/d/b/e/c");
+        acl.addEntry(testPrincipal, privilegesFromNames(JCR_READ), true,
+                Collections.emptyMap(),
+                ImmutableMap.of(AccessControlConstants.REP_CURRENT, new Value[] {
+                        vf.createValue(JCR_PRIMARYTYPE),
+                        vf.createValue(JCR_MIXINTYPES)}));
+        acMgr.setPolicy(acl.getPath(), acl);
+        root.commit();
+
+        Root testRoot = testSession.getLatestRoot();
+        assertFalse(testRoot.getTree("/a").exists());
+        Tree t = testRoot.getTree("/a/d/b/e/c");
+        assertTrue(t.exists());
+        assertTrue(t.hasProperty(JCR_PRIMARYTYPE));
+        assertTrue(t.hasProperty(JCR_MIXINTYPES));
+        assertFalse(t.hasProperty("prop"));
+        assertFalse(t.hasProperty("a"));
+        assertEquals(2, t.getPropertyCount());
+        assertEquals(0, t.getChildrenCount(1));
+    }
+
+    @Test
+    public void testNoProperties() throws Exception {
+        AccessControlManager acMgr = getAccessControlManager(root);
+        JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(acMgr, "/a/d/b/e/c");
+        acl.addEntry(testPrincipal, privilegesFromNames(JCR_READ), true,
+                Collections.emptyMap(),
+                Collections.singletonMap(AccessControlConstants.REP_CURRENT, new Value[0]));
+        acMgr.setPolicy(acl.getPath(), acl);
+        root.commit();
+
+        Root testRoot = testSession.getLatestRoot();
+        assertFalse(testRoot.getTree("/a").exists());
+        Tree t = testRoot.getTree("/a/d/b/e/c");
+        assertTrue(t.exists());
+        assertEquals(0, t.getPropertyCount());
+        assertEquals(0, t.getChildrenCount(1));
+    }
+    
+    @Test
+    public void testAllProperties() throws Exception {
+        AccessControlManager acMgr = getAccessControlManager(root);
+        JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(acMgr, "/a/d/b/e/c");
+        acl.addEntry(testPrincipal, privilegesFromNames(JCR_READ), true, 
+                Collections.emptyMap(),
+                Collections.singletonMap(AccessControlConstants.REP_CURRENT, new Value[] {vf.createValue(NodeTypeConstants.RESIDUAL_NAME)
+                }));
+        acMgr.setPolicy(acl.getPath(), acl);
+        root.commit();
+        
+        Root testRoot = testSession.getLatestRoot();
+        assertFalse(testRoot.getTree("/a").exists());
+        Tree t = testRoot.getTree("/a/d/b/e/c");
+        assertTrue(t.exists());
+        assertTrue(t.hasProperty(JCR_PRIMARYTYPE));
+        assertTrue(t.hasProperty(JCR_MIXINTYPES));
+        assertTrue(t.hasProperty("prop"));
+        assertTrue(t.hasProperty("a"));
+        assertFalse(t.hasChild("f"));
+        assertEquals(4, t.getPropertyCount());
+        assertEquals(0, t.getChildrenCount(1));
+    }
+    
+    @Test
+    public void testSetProperties() throws Exception {
+        String propertyName = "prop";
+        PropertyState prop = PropertyStates.createProperty(propertyName, "value");
+        AccessControlManager acMgr = getAccessControlManager(root);
+        JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(acMgr, "/a");
+        acl.addEntry(testPrincipal, privilegesFromNames(JCR_READ), true);
+        acl.addEntry(testPrincipal, privilegesFromNames(JCR_MODIFY_PROPERTIES), true,
+                Collections.emptyMap(),
+                Collections.singletonMap(AccessControlConstants.REP_CURRENT, new Value[] {vf.createValue(propertyName)}));
+        acMgr.setPolicy(acl.getPath(), acl);
+        root.commit();
+
+        Root testRoot = testSession.getLatestRoot();
+        
+        // on /a added a property 'prop' must be allowed
+        Tree a = testRoot.getTree("/a");
+        assertTrue(a.exists());
+        a.setProperty(prop);
+        testRoot.commit();
+
+        // for any other property name jcr:modifyProperties is not granted
+        try {
+            a.setProperty(PropertyStates.createProperty("another", "value"));
+            testRoot.commit();
+            fail();
+        } catch (CommitFailedException e) {
+            assertTrue(e.isAccessViolation());
+        }
+        
+        // nor is it jcr:modifyProperties granted on another node
+        Tree c = testRoot.getTree("/a/d/b/e/c");
+        assertTrue(c.exists());
+        try {
+            c.setProperty(prop);
+            testRoot.commit();
+            fail();
+        } catch (CommitFailedException e) {
+            assertTrue(e.isAccessViolation());
+        }
+    }
+
+    @Test
+    public void testAddChildNodes() throws Exception {
+        AccessControlManager acMgr = getAccessControlManager(root);
+        JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(acMgr, "/a");
+        acl.addEntry(testPrincipal, privilegesFromNames(JCR_READ), true);
+        acl.addEntry(testPrincipal, privilegesFromNames(JCR_ADD_CHILD_NODES), true,
+                Collections.emptyMap(),
+                // NOTE: specifying property names doesn't make sense for jcr:addChildNode privilege
+                Collections.singletonMap(AccessControlConstants.REP_CURRENT, new Value[0]));
+        acMgr.setPolicy(acl.getPath(), acl);
+        root.commit();
+
+        Root testRoot = testSession.getLatestRoot();
+
+        // on /a adding a child node is allowed
+        Tree a = testRoot.getTree("/a");
+        assertTrue(a.exists());
+        TreeUtil.addChild(a, "child", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        testRoot.commit();
+
+        // however, jcr:addChildNodes is not granted in the subtree
+        Tree c = testRoot.getTree("/a/d/b/e/c");
+        assertTrue(c.exists());
+        try {
+            TreeUtil.addChild(c, "child", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+            testRoot.commit();
+            fail();
+        } catch (CommitFailedException e) {
+            assertTrue(e.isAccessViolation());
+        }
+    }
+}
diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/ItemNameRestrictionTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/ItemNameRestrictionTest.java
index 68e9fab..4ccd826 100644
--- a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/ItemNameRestrictionTest.java
+++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/ItemNameRestrictionTest.java
@@ -16,15 +16,6 @@
  */
 package org.apache.jackrabbit.oak.security.authorization.restriction;
 
-import java.security.Principal;
-import java.util.Collections;
-import java.util.List;
-import java.util.UUID;
-import javax.jcr.PropertyType;
-import javax.jcr.Value;
-import javax.jcr.ValueFactory;
-import javax.jcr.security.AccessControlManager;
-
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import org.apache.jackrabbit.JcrConstants;
@@ -33,91 +24,49 @@
 import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.api.security.user.UserManager;
 import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
-import org.apache.jackrabbit.oak.AbstractSecurityTest;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
-import org.apache.jackrabbit.oak.api.ContentSession;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.commons.PathUtils;
-import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
 import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants;
-import org.apache.jackrabbit.oak.plugins.value.jcr.ValueFactoryImpl;
 import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants;
 import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
 import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.jetbrains.annotations.NotNull;
 import org.junit.Test;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.security.AccessControlManager;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-public class ItemNameRestrictionTest extends AbstractSecurityTest {
-
-    private ValueFactory vf;
-    private ContentSession testSession;
-
-    private Principal testPrincipal;
-    private Group testGroup;
+public class ItemNameRestrictionTest extends AbstractRestrictionTest {
 
     @Override
-    public void before() throws Exception {
-        super.before();
-
-        Tree rootTree = root.getTree("/");
-
-        // "/a/d/b/e/c/f"
-        Tree a = TreeUtil.addChild(rootTree, "a", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
-        Tree d = TreeUtil.addChild(a, "d", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
-        Tree b = TreeUtil.addChild(d, "b", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
-        Tree e = TreeUtil.addChild(b, "e", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
-        Tree c = TreeUtil.addChild(e, "c", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
-        Tree f = TreeUtil.addChild(c, "f", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
-        c.setProperty("prop", "value");
-        c.setProperty("a", "value");
-
-        testPrincipal = getTestUser().getPrincipal();
-
-        AccessControlManager acMgr = getAccessControlManager(root);
-        JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(acMgr, "/a");
-
-        vf = new ValueFactoryImpl(root, NamePathMapper.DEFAULT);
-        acl.addEntry(testPrincipal,
+    boolean addEntry(@NotNull JackrabbitAccessControlList acl) throws RepositoryException {
+        return acl.addEntry(testPrincipal,
                 privilegesFromNames(
                         PrivilegeConstants.JCR_READ,
                         PrivilegeConstants.REP_ADD_PROPERTIES,
                         PrivilegeConstants.JCR_ADD_CHILD_NODES,
                         PrivilegeConstants.JCR_REMOVE_NODE), true,
-                Collections.<String, Value>emptyMap(),
+                Collections.emptyMap(),
                 ImmutableMap.of(AccessControlConstants.REP_ITEM_NAMES, new Value[] {
                         vf.createValue("a", PropertyType.NAME),
                         vf.createValue("b", PropertyType.NAME),
                         vf.createValue("c", PropertyType.NAME)}));
-        acMgr.setPolicy(acl.getPath(), acl);
-
-        UserManager uMgr = getUserManager(root);
-        testGroup = uMgr.createGroup("testGroup" + UUID.randomUUID());
-
-        root.commit();
-        testSession = createTestSession();
     }
-
-    @Override
-    public void after() throws Exception {
-        try {
-            testSession.close();
-            root.refresh();
-            Tree a = root.getTree("/a");
-            if (a.exists()) {
-                a.remove();
-                root.commit();
-            }
-        } finally {
-            super.after();
-        }
-    }
-
+    
     @Test
     public void testRead() {
         Root testRoot = testSession.getLatestRoot();
@@ -238,14 +187,17 @@
 
     @Test
     public void testModifyMembersOnly() throws Exception {
+        Group testGroup = getUserManager(root).createGroup("testGroup" + UUID.randomUUID());
+        root.commit();
+        
         AccessControlManager acMgr = getAccessControlManager(root);
         String path = PathUtils.getAncestorPath(UserConstants.DEFAULT_USER_PATH, 1);
         try {
             JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(acMgr, path);
             acl.addEntry(testPrincipal, privilegesFromNames(PrivilegeConstants.JCR_READ), true);
             acl.addEntry(testPrincipal, privilegesFromNames(PrivilegeConstants.REP_USER_MANAGEMENT), true,
-                    Collections.<String,Value>emptyMap(),
-                    ImmutableMap.<String,Value[]>of (AccessControlConstants.REP_ITEM_NAMES, new Value[] {
+                    Collections.emptyMap(),
+                    ImmutableMap.of (AccessControlConstants.REP_ITEM_NAMES, new Value[] {
                                             vf.createValue(UserConstants.REP_MEMBERS, PropertyType.NAME)}));
             acMgr.setPolicy(acl.getPath(), acl);
             root.commit();
diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/RestrictionProviderImplTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/RestrictionProviderImplTest.java
index 6baadd6..4dc3c3b 100644
--- a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/RestrictionProviderImplTest.java
+++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/restriction/RestrictionProviderImplTest.java
@@ -72,7 +72,7 @@
 
         Set<RestrictionDefinition> defs = provider.getSupportedRestrictions("/testPath");
         assertNotNull(defs);
-        assertEquals(4, defs.size());
+        assertEquals(5, defs.size());
 
         for (RestrictionDefinition def : defs) {
             if (REP_GLOB.equals(def.getName())) {
@@ -87,6 +87,9 @@
             } else if (REP_ITEM_NAMES.equals(def.getName())) {
                 assertEquals(Type.NAMES, def.getRequiredType());
                 assertFalse(def.isMandatory());
+            } else if (REP_CURRENT.equals(def.getName())) {
+                assertEquals(Type.STRINGS, def.getRequiredType());
+                assertFalse(def.isMandatory());
             } else {
                 fail("unexpected restriction " + def.getName());
             }
@@ -131,6 +134,8 @@
         map.put(PropertyStates.createProperty(REP_PREFIXES, prefixes, Type.STRINGS), new PrefixPattern(prefixes));
         List<String> itemNames = ImmutableList.of("abc", "jcr:primaryType");
         map.put(PropertyStates.createProperty(REP_ITEM_NAMES, prefixes, Type.NAMES), new ItemNamePattern(itemNames));
+        List<String> propNames = ImmutableList.of("jcr:mixinTypes", "jcr:primaryType");
+        map.put(PropertyStates.createProperty(REP_CURRENT, prefixes, Type.STRINGS), new CurrentPattern("/testPatg", propNames));
 
         Tree tree = TreeUtil.getOrAddChild(root.getTree("/"), "testPath", JcrConstants.NT_UNSTRUCTURED);
         Tree restrictions = TreeUtil.addChild(tree, REP_RESTRICTIONS, NT_REP_RESTRICTIONS);
@@ -156,9 +161,12 @@
         List<String> itemNames = ImmutableList.of("abc", "jcr:primaryType");
         map.put(PropertyStates.createProperty(REP_ITEM_NAMES, itemNames, Type.NAMES), new ItemNamePattern(itemNames));
 
+        List<String> propNames = ImmutableList.of("*");
+        map.put(PropertyStates.createProperty(REP_CURRENT, propNames, Type.STRINGS), new CurrentPattern("/testPath", propNames));
+        
         Tree tree = TreeUtil.getOrAddChild(root.getTree("/"), "testPath", JcrConstants.NT_UNSTRUCTURED);
         Tree restrictions = TreeUtil.addChild(tree, REP_RESTRICTIONS, NT_REP_RESTRICTIONS);
-
+        
         // test restrictions individually
         for (Map.Entry<PropertyState, RestrictionPattern> entry : map.entrySet()) {
             restrictions.setProperty(entry.getKey());
diff --git a/oak-doc/src/site/markdown/security/authorization/restriction.md b/oak-doc/src/site/markdown/security/authorization/restriction.md
index d3f459d..bc8db56 100644
--- a/oak-doc/src/site/markdown/security/authorization/restriction.md
+++ b/oak-doc/src/site/markdown/security/authorization/restriction.md
@@ -31,7 +31,7 @@
 > to provide finer-grained access restrictions to individual properties or
 > child nodes of the node to which the policy applies.
 
-Furthermore the restriction concept is aimed to allow for custom extensions of the
+Furthermore, the restriction concept is aimed to allow for custom extensions of the
 default access control implementation to meet project specific needs without
 having to implement the common functionality provided by JCR.
 
@@ -44,24 +44,22 @@
 - dedicated time frame
 - size of a value
 
-The set of built-in restrictions present with Jackrabbit 2.x has extended as of 
-Oak 1.0 along with some extensions of the Jackrabbit API. This covers the public 
-facing usage of restrictions i.e. access control management.
+The set of built-in restrictions available with Jackrabbit 2.x has been extended along with some extensions of the 
+Jackrabbit API. This covers the public facing usage of restrictions i.e. access control management.
 
-In addition Oak provides it's own restriction API that adds support for internal 
-validation and permission evaluation.
+In addition, Oak provides its own internal restriction API that adds support for validation and permission evaluation.
 
 <a name="jackrabbit_api"></a>
 ### Jackrabbit API
 
-The Jackrabbit API add the following extensions to JCR access control management
+The Jackrabbit API adds the following extensions to JCR access control management
 to read and create entries with restrictions:
 
 - `JackrabbitAccessControlList`
     - `getRestrictionNames()` : returns the JCR names of the supported restrictions.
-    - `getRestrictionType(String restrictionName)` : returns property type of a given restriction.
+    - `getRestrictionType(String restrictionName)` : returns the property type of a given restriction.
     - `addEntry(Principal, Privilege[], boolean, Map<String, Value>)`: the map contain the restrictions.
-    - `addEntry(Principal, Privilege[], boolean, Map<String, Value>, Map<String, Value[]>)`: allows to specify both single and multivalue restrictions (since Oak 1.0, Jackrabbit API 2.8)
+    - `addEntry(Principal, Privilege[], boolean, Map<String, Value>, Map<String, Value[]>)`: allows to specify both single and multi-value restrictions (since Oak 1.0, Jackrabbit API 2.8)
 
 
 - `JackrabbitAccessControlEntry`
@@ -90,8 +88,8 @@
 - `AbstractRestrictionProvider`: abstract base implementation of the provider interface.
 - `RestrictionDefinitionImpl`: default implementation of the `RestrictionDefinition` interface.
 - `RestrictionImpl`: default implementation of the `Restriction` interface.
-- `CompositeRestrictionProvider`: Allows to aggregate multiple provider implementations (see [Pluggability](#pluggability) below).
-- `CompositePattern`: Allows to aggregate multiple restriction patterns.
+- `CompositeRestrictionProvider`: Allows aggregating multiple provider implementations (see [Pluggability](#pluggability) below).
+- `CompositePattern`: Allows aggregating multiple restriction patterns.
 
 #### Changes wrt Jackrabbit 2.x
 
@@ -99,34 +97,35 @@
 a public API, the restriction implementation in Oak differs from Jackrabbit 2.x
 as follows:
 
-- Separate restriction management API (see below) on the OAK level that allows to ease plugging custom restrictions.
+- Separate restriction management API (see below) on the OAK level that simplifies plugging custom restrictions.
 - Changed node type definition for storing restrictions in the default implementation.
     - as of OAK restrictions are collected underneath a separate child node "rep:restrictions"
     - backwards compatible behavior for restrictions stored underneath the ACE node directly
 - Support for multi-valued restrictions (see [JCR-3637](https://issues.apache.org/jira/browse/JCR-3637), [JCR-3641](https://issues.apache.org/jira/browse/JCR-3641))
 - Validation of the restrictions is delegated to a dedicated commit hook
 - Restriction `rep:glob` limits the number of wildcard characters to 20
-- New restrictions `rep:ntNames`, `rep:prefixes` and `rep:itemNames`
+- New restrictions `rep:ntNames`, `rep:prefixes`, `rep:itemNames` and `rep:current`
    
 #### Built-in Restrictions
 
-The default implementations of the `Restriction` interface are present with
-Oak 1.0 access control management:
+The following `Restriction` implementations are supported with the default Oak access control management:
 
-* `rep:glob`: single name, path or path pattern with '*' wildcard(s).
-* `rep:ntNames`: multivalued restriction to limit the affected ACE to nodes of the specified primary node type(s) (no nt inheritence, since Oak 1.0)
-* `rep:prefixes`: multivalued restriction to limit the effect to item names that match the specified namespace prefixes (session level remapping not respected, since Oak 1.0)
-* `rep:itemNames`: multivalued restriction for property or node names (since Oak 1.3.8)
+| Restriction Name | Type   | Multi-Valued | Mandatory | Description                          | Since |
+|------------------|--------|--------------|-----------|--------------------------------------|-------|
+| `rep:glob`       | String | false        | false     | Single name, path or path pattern with '*' wildcard(s), see below for details | Oak 1.0 |
+| `rep:ntNames`    | Name   | true         | false     | Multivalued restriction to limit the effect to nodes of the specified primary node types (no nt inheritance) | Oak 1.0 |
+| `rep:prefixes`   | String | true         | false     | Multivalued restriction to limit the effect to item names that match the specified namespace prefixes (session level remapping not respected) | Oak 1.0 |
+| `rep:itemNames`  | Name   | true         | false     | Multivalued restriction for property or node names | Oak 1.3.8 |
+| `rep:current`    | String | true         | false     | Multivalued restriction that limits the effect to a single level i.e. the target node where the access control entry takes effect and optionally all or a subset of it's properties. There is no inheritance of the ACE effect to nodes in the subtree or their properties. Expanded JCR property names and namespace remapping not supported (see below for details) | Oak 1.42.0 |
 
-##### Examples
 
-###### Using rep:glob
+##### rep:glob - Details and Examples
 
 For a nodePath `/foo` the following results can be expected for the different
 values of `rep:glob`.
 
 Please note that the pattern is based on simple path concatenation and equally applies to either type of item (both nodes and properties). 
-Consequently the examples below need to be adjusted for the root node in order to produce the desired effect. In particular a path with two subsequent / is invalid and will never match any target item or path.
+Consequently, the examples below need to be adjusted for the root node in order to produce the desired effect. In particular a path with two subsequent / is invalid and will never match any target item or path.
 
 | rep:glob      | Result                                                   |
 |---------------|----------------------------------------------------------|
@@ -158,10 +157,40 @@
 
 See also [GlobPattern] for implementation details and the [GlobRestrictionTest] in the _oak-exercise_ module for training material.
 
+##### rep:current - Details and Examples
+
+The restriction limits the effect to the target node. The value of the restriction property defines which properties of 
+the target node will in addition also match the restriction:
+
+- an empty value array will make the restriction matching the target node only (i.e. equivalent to rep:glob=""). 
+- an array of qualified property names will extend the effect of the restriction to properties of the target node that match the specified names. 
+- the residual name * will match all properties of the target node.
+
+For a nodePath `/foo` the following results can be expected for the different values of `rep:current`:
+
+| rep:current       | Result                                                        |
+|-------------------|---------------------------------------------------------------|
+| []                | `/foo` only, none of it's properties                          |
+| [\*]              | `/foo` and all it's properties                                |
+| [jcr:primaryType] | `/foo` and it's `jcr:primaryType` property. no other property |
+| [a, b, c]         | `/foo` and it's properties `a`,`b`,`c`                        |
+
+###### Known Limitations
+- Due to the support of the residual name `*`, which isn't a valid JCR name, the restriction `rep:current` is defined to be 
+of `PropertyType.STRING` instead of `PropertyType.NAME`. Like the `rep:glob` restriction it will therefore not work with 
+expanded JCR names or with remapped namespace prefixes.
+- In case of permission evaluation for a path pointing to a *non-existing* JCR item (see e.g. `javax.jcr.Session#hasPermission(String, String)`),
+it's not possible to distinguish between nodes and properties. A best-effort approach is take to identify known properties 
+like e.g. `jcr:primaryType`. For all other paths the implementation of the `rep:current` restrictions assumes that they 
+point to non-existing nodes. Example: if `rep:current` is present with an ACE taking effect at `/foo` the call 
+`Session.hasPermission("/foo/non-existing",Session.ACTION_READ)` will always return `false` because the restriction
+will interpret `/foo/non-existing` as a path pointing to a node.
+
+
 <a name="representation"></a>
 ### Representation in the Repository
 
-All restrictions defined by default in a Oak repository are stored as properties 
+All restrictions defined by default in an Oak repository are stored as properties 
 in a dedicated `rep:restriction` child node of the target access control entry node. 
 Similarly, they are represented with the corresponding permission entry.
 The node type definition used to represent restriction content is as follows:
@@ -189,7 +218,7 @@
 `RestrictionProvider` implementations and will automatically combine the
 different implementations using the `CompositeRestrictionProvider`.
 
-In an OSGi setup the following steps are required in order to add a action provider
+In an OSGi setup the following steps are required in order to add an action provider
 implementation:
 
 - implement `RestrictionProvider` interface exposing your custom restriction(s).
diff --git a/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/AccessControlConstants.java b/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/AccessControlConstants.java
index 59b6a22..6d4b351 100644
--- a/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/AccessControlConstants.java
+++ b/oak-security-spi/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/accesscontrol/AccessControlConstants.java
@@ -19,6 +19,7 @@
 import java.util.Collection;
 
 import com.google.common.collect.ImmutableSet;
+import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants;
 
 /**
  * Constants for the default access control management implementation and
@@ -58,6 +59,40 @@
     String REP_ITEM_NAMES = "rep:itemNames";
 
     /**
+     * <p>Name of the optional multivalued access control restriction that limits access to a single level i.e. the target 
+     * node where the access control entry takes effect and optionally all or a subset of it's properties. 
+     * An empty value array will make this restriction matching the target node only (i.e. equivalent to rep:glob=""). 
+     * An array of property names will extend the effect of the restriction to properties of the target node that match 
+     * the specified names. The {@link org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants#RESIDUAL_NAME residual name '*'}  
+     * will match the target node and all it's properties.</p>
+     * <p>
+     * The corresponding restriction type is {@link org.apache.jackrabbit.oak.api.Type#STRINGS}
+     * </p>
+     * <p>
+     * Note: due to the support of {@link NodeTypeConstants#RESIDUAL_NAME}, which isn't a valid JCR name,
+     * this restriction is defined to be of {@link org.apache.jackrabbit.oak.api.Type#STRINGS} instead of 
+     * {@link org.apache.jackrabbit.oak.api.Type#NAMES}. Like the rep:glob restriction it will therefore not work with 
+     * expanded JCR names or with remapped namespace prefixes.
+     * </p>
+     * <p>
+     * Note: In case of permission evaluation for a path pointing to a non-existing JCR item (see e.g. 
+     * {@link javax.jcr.Session#hasPermission(String, String)}) a best-effort attempt is made to determine if the path 
+     * may point to a property, default being that the path points to a non-existing node.
+     * </p>
+     * 
+     * Example:
+     * <pre>
+     * rep:current = []                => restriction applies to the target node only
+     * rep:current = [*]               => restriction applies to the target node and all it's properties
+     * rep:current = [jcr:primaryType] => restriction applies to the target node and it's property jcr:primaryType
+     * rep:current = [a, b, prefix:c]  => restriction applies to the target node and it's properties a, b and prefix:c
+     * </pre>
+     * 
+     * @since OAK 1.42.0
+     */
+    String REP_CURRENT = "rep:current";
+
+    /**
      * @since OAK 1.0
      */
     String REP_RESTRICTIONS = "rep:restrictions";