Rename sling-oak-restrictions folder to oak-restrictions

The sling prefix is redundant.

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1762555 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..271c4e5
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>26</version>
+        <relativePath/>
+    </parent>
+
+    <artifactId>org.apache.sling.oak.restrictions</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+
+    <name>Apache Sling Oak Restrictions</name>
+    <description>
+        Supports additional restrictions for OAK (e.g. for resource type).      
+    </description>
+
+    <scm>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/contrib/extensions/oak-restrictions</connection>
+        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/contrib/extensions/oak-restrictions</developerConnection>
+        <url>http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/oak-restrictions</url>
+    </scm>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <!--
+                            org.apache.jackrabbit.oak.api: Only the classes Tree, Type and PropertyState are used in a
+                            very basic version to traverse the tree
+                            org.apache.jackrabbit.oak.util: Has changed to "0.0.0" in newer versions (only TreeUtil is used)
+                        -->
+                        <Import-Package>
+                            org.apache.jackrabbit.oak.api;version="[1.0,4)",
+                            org.apache.jackrabbit.oak.util;version="0.0.0",
+                            *
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.jackrabbit</groupId>
+            <artifactId>oak-core</artifactId>
+            <version>1.2.7</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.api</artifactId>
+            <version>2.9.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.jcr</groupId>
+            <artifactId>jcr</artifactId>
+            <version>2.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.findbugs</groupId>
+            <artifactId>jsr305</artifactId>
+            <version>2.0.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <version>2.5</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <version>1.10.19</version>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+</project>
diff --git a/src/main/java/org/apache/sling/oak/restrictions/impl/ResourceTypePattern.java b/src/main/java/org/apache/sling/oak/restrictions/impl/ResourceTypePattern.java
new file mode 100644
index 0000000..1ab2ac2
--- /dev/null
+++ b/src/main/java/org/apache/sling/oak/restrictions/impl/ResourceTypePattern.java
@@ -0,0 +1,199 @@
+/*
+ * 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.sling.oak.restrictions.impl;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionPattern;
+import org.apache.jackrabbit.oak.util.TreeUtil;
+import org.apache.sling.api.SlingConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** Implementation of the {@link RestrictionPattern} interface that returns {@code true} if the resource type of the target tree (or the
+ * parent of a target property) is contained in the configured resource type. */
+public class ResourceTypePattern implements RestrictionPattern {
+    private static final Logger LOG = LoggerFactory.getLogger(ResourceTypePattern.class);
+
+    static final String DEFAULT_PATH = ".";
+    static final String PATH_MARKER = "@";
+    
+    static final String SLING_RESOURCE_TYPE = SlingConstants.NAMESPACE_PREFIX + ":" + SlingConstants.PROPERTY_RESOURCE_TYPE;
+
+
+    private final String limitedToPath;
+    private final boolean matchChildren;
+    
+    private final Map<String,Set<String>> resourceTypesByPath;
+    
+  
+    ResourceTypePattern(@Nonnull Iterable<String> resourceTypesRaw, String limitedToPath, boolean matchChildren) {
+        
+        this.limitedToPath = limitedToPath;
+        this.matchChildren = matchChildren;
+        
+        Map<String,Set<String>> resourceTypesByPath = new LinkedHashMap<String,Set<String>>();
+        for (String resourceTypeRaw : resourceTypesRaw) {
+            String path;
+            String resourceType;
+            if(resourceTypeRaw.contains(PATH_MARKER)) {
+                String[] bits = resourceTypeRaw.trim().split(PATH_MARKER, 2);
+                path = bits[1];
+                resourceType = bits[0];
+            } else {
+                path = DEFAULT_PATH;
+                resourceType = resourceTypeRaw;
+            }
+                
+            Set<String> resourceTypesForPath = resourceTypesByPath.get(path);
+            if(resourceTypesForPath==null) {
+                resourceTypesForPath = new HashSet<String>();
+                resourceTypesByPath.put(path, resourceTypesForPath);
+            }
+            resourceTypesForPath.add(resourceType);
+        }
+        
+        this.resourceTypesByPath = Collections.unmodifiableMap(resourceTypesByPath);
+        LOG.trace("pattern setup with resourceTypesByPath={}",  this.resourceTypesByPath);
+    }
+    
+    String getLimitedToPath() {
+        return limitedToPath;
+    }
+
+    boolean isMatchChildren() {
+        return matchChildren;
+    }
+
+    @Override
+    public boolean matches(@Nonnull Tree tree, @Nullable PropertyState property) {
+        boolean isMatch = matchesAtTree(tree);
+        if(!isMatch && matchChildren) { // try parent hierarchy
+            Tree treeCursor = tree;
+            while(!isMatch && !treeCursor.isRoot()) {
+                treeCursor = treeCursor.getParent();
+                if(!treeCursor.getPath().startsWith(limitedToPath)) {
+                    if(LOG.isTraceEnabled()) {
+                        LOG.trace("Breaking parent traversal loop: tree={}, limitedToPath={}", treeCursor.getPath(), limitedToPath);
+                    }
+                    break; 
+                }
+                isMatch = matchesAtTree(treeCursor);
+            }
+        }
+        if(LOG.isDebugEnabled()) {
+            LOG.debug("Match for "+tree.getPath()+": "+ (isMatch ? "YES":"NO") + " ("+this+")");
+        }
+        return isMatch;
+    }
+
+    private boolean matchesAtTree(Tree tree) {
+        boolean isResourceTypeMatch = false;
+        for (String path : resourceTypesByPath.keySet()) {
+
+                Tree treeToCheck = tree; // the default if e.g. just the resource type without @path is given
+                if(!DEFAULT_PATH.equals(path)) {
+                    try {
+                        String[] segments = path.split("/");
+                        for (String string : segments) {
+                            treeToCheck = treeToCheck.getChild(string);
+                        }
+                    } catch (IllegalArgumentException e) {
+                        continue; // continue and ignore if path is not found
+                    } 
+                }
+                
+                Set<String> resourceTypesForPath = resourceTypesByPath.get(path);
+                String actualResourceType = TreeUtil.getString(treeToCheck, SLING_RESOURCE_TYPE);
+                isResourceTypeMatch = resourceTypesForPath.contains(actualResourceType);
+                
+                if(LOG.isTraceEnabled()) {
+                    LOG.trace("isResourceTypeMatch={} (checked at path {} at sub path {})", new Object[]{isResourceTypeMatch, tree.getPath(), path});
+                }
+                if(isResourceTypeMatch) {
+                    break; // return as quickly as possible
+                }
+
+        }
+        return isResourceTypeMatch;
+    }
+
+
+    @Override
+    public boolean matches(@Nonnull String path) {
+        return false;
+    }
+
+    @Override
+    public boolean matches() {
+        // node type pattern never matches for repository level permissions
+        return false;
+    }
+
+    // -------------------------------------------------------------< Object >---
+
+    @Override
+    public String toString() {
+        return "ResourceTypePattern [limitedToPath=" + limitedToPath + ", matchChildren=" + matchChildren + ", resourceTypesByPath="
+                + resourceTypesByPath + "]";
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((limitedToPath == null) ? 0 : limitedToPath.hashCode());
+        result = prime * result + (matchChildren ? 1231 : 1237);
+        result = prime * result + ((resourceTypesByPath == null) ? 0 : resourceTypesByPath.hashCode());
+        return result;
+    }
+
+
+    @Override
+    public boolean equals(Object obj) {
+        if(this == obj)
+            return true;
+        if(obj == null)
+            return false;
+        if(getClass() != obj.getClass())
+            return false;
+        ResourceTypePattern other = (ResourceTypePattern) obj;
+        if(limitedToPath == null) {
+            if(other.limitedToPath != null)
+                return false;
+        } else if(!limitedToPath.equals(other.limitedToPath))
+            return false;
+        if(matchChildren != other.matchChildren)
+            return false;
+        if(resourceTypesByPath == null) {
+            if(other.resourceTypesByPath != null)
+                return false;
+        } else if(!resourceTypesByPath.equals(other.resourceTypesByPath))
+            return false;
+        return true;
+    }
+    
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/oak/restrictions/impl/SlingRestrictionProviderImpl.java b/src/main/java/org/apache/sling/oak/restrictions/impl/SlingRestrictionProviderImpl.java
new file mode 100644
index 0000000..b2867c9
--- /dev/null
+++ b/src/main/java/org/apache/sling/oak/restrictions/impl/SlingRestrictionProviderImpl.java
@@ -0,0 +1,120 @@
+/*
+ * 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.sling.oak.restrictions.impl;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.AbstractRestrictionProvider;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.Restriction;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionDefinition;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionDefinitionImpl;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionPattern;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** Sling restriction provider implementation that supports the following restrictions:
+ *
+ * <ul>
+ * <li>{@link #SLING_RESOURCE_TYPES}: A restriction that allows to match against resource types (matches are exact and do not include children).</li>
+ * <li>{@link #SLING_RESOURCE_TYPES_WITH_DESCENDANTS}: A restriction that allows to match against resource types and all sub nodes of matching resource types.</li>
+ * </ul>
+ * 
+ * Further sling restriction can be added here in future.
+*/
+@Component
+@Service(RestrictionProvider.class)
+public class SlingRestrictionProviderImpl extends AbstractRestrictionProvider {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SlingRestrictionProviderImpl.class);
+
+    public static final String SLING_RESOURCE_TYPES = "sling:resourceTypes";
+    public static final String SLING_RESOURCE_TYPES_WITH_DESCENDANTS = "sling:resourceTypesWithDescendants";
+
+    public SlingRestrictionProviderImpl() {
+        super(supportedRestrictions());
+    }
+
+    private static Map<String, RestrictionDefinition> supportedRestrictions() {
+        RestrictionDefinition slingResourceTypes = new RestrictionDefinitionImpl(SLING_RESOURCE_TYPES, Type.STRINGS, false);
+        RestrictionDefinition slingResourceTypesWithChildren = new RestrictionDefinitionImpl(SLING_RESOURCE_TYPES_WITH_DESCENDANTS, Type.STRINGS, false);
+        Map<String, RestrictionDefinition> supportedRestrictions = new HashMap<String, RestrictionDefinition>();
+        supportedRestrictions.put(slingResourceTypes.getName(), slingResourceTypes);
+        supportedRestrictions.put(slingResourceTypesWithChildren.getName(), slingResourceTypesWithChildren);
+        return Collections.unmodifiableMap(supportedRestrictions);
+    }
+
+    // ------------------------------------------------< RestrictionProvider >---
+
+    @Nonnull
+    @Override
+    public RestrictionPattern getPattern(String oakPath, @Nonnull Tree tree) {
+        if (oakPath != null) {
+            PropertyState resourceTypes = tree.getProperty(SLING_RESOURCE_TYPES);
+            if (resourceTypes != null) {
+                ResourceTypePattern resourceTypePattern = new ResourceTypePattern(resourceTypes.getValue(Type.STRINGS), oakPath, false);
+                LOG.trace("Returning resourceTypePattern={} for rep:slingResourceTypes in getPattern(String,Tree)", resourceTypePattern);
+                return resourceTypePattern;
+            }
+            PropertyState resourceTypesWithChildren = tree.getProperty(SLING_RESOURCE_TYPES_WITH_DESCENDANTS);
+            if (resourceTypesWithChildren != null) {
+                ResourceTypePattern resourceTypePattern = new ResourceTypePattern(resourceTypesWithChildren.getValue(Type.STRINGS), oakPath, true);
+                LOG.trace("Returning resourceTypePattern={} for rep:slingResourceTypesWithChildren in getPattern(String,Tree)", resourceTypePattern);
+                return resourceTypePattern;
+            }            
+        }
+        return RestrictionPattern.EMPTY;
+    }
+
+    @Nonnull
+    @Override
+    public RestrictionPattern getPattern(@Nullable String oakPath, @Nonnull Set<Restriction> restrictions) {
+
+        if (oakPath != null && !restrictions.isEmpty()) {
+            for (Restriction r : restrictions) {
+                String name = r.getDefinition().getName();
+                if (SLING_RESOURCE_TYPES.equals(name)) {
+                    ResourceTypePattern resourceTypePattern = new ResourceTypePattern(r.getProperty().getValue(Type.STRINGS), oakPath, false);
+                    LOG.trace(
+                            "Returning resourceTypePattern={} for rep:slingResourceTypes in getPattern(String,Set<Restriction>)",
+                            resourceTypePattern);
+                    return resourceTypePattern;
+                } else if(SLING_RESOURCE_TYPES_WITH_DESCENDANTS.equals(name)) {
+                    ResourceTypePattern resourceTypePattern = new ResourceTypePattern(r.getProperty().getValue(Type.STRINGS), oakPath, true);
+                    LOG.trace(
+                            "Returning resourceTypePattern={} for rep:slingResourceTypesWithChildren in getPattern(String,Set<Restriction>)",
+                            resourceTypePattern);
+                    return resourceTypePattern;
+                }
+            }
+        }
+
+        return RestrictionPattern.EMPTY;
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/oak/restrictions/impl/ResourceTypePatternTest.java b/src/test/java/org/apache/sling/oak/restrictions/impl/ResourceTypePatternTest.java
new file mode 100644
index 0000000..eaffda2
--- /dev/null
+++ b/src/test/java/org/apache/sling/oak/restrictions/impl/ResourceTypePatternTest.java
@@ -0,0 +1,209 @@
+/*
+ * 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.sling.oak.restrictions.impl;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import java.util.Arrays;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+
+public class ResourceTypePatternTest {
+    
+    private static final String TEST_PATH = "/content/path/to/test";
+    
+    private static final String RESOURCE_TYPE_TEST_PATH = "myproj/comp1";
+    private static final String RESOURCE_TYPE_SUB1 = "myproj/comp2";
+    private static final String RESOURCE_TYPE_SUB2 = "myproj/comp3";
+    private static final String RESOURCE_TYPE_SUB3 = "myproj/comp4";
+    private static final String RESOURCE_TYPE_SUBSUB1 = "myproj/comp5";
+    
+    private static final String RESOURCE_TYPE_OUTSIDE_SCOPE = "myproj/outsidescope";
+    
+    private static final String TEST_NODE_SUB1 = "subfolder1";        
+    private static final String TEST_NODE_SUB2 = "subfolder2";
+    private static final String TEST_NODE_SUB3 = "subfolder3";
+    private static final String TEST_NODE_SUBSUB1 = "subsubfolder1";
+    private static final String TEST_NODE_SUBSUB2 = "subsubfolder2";
+
+    @Mock
+    private Tree testTree;
+    
+    @Mock
+    private Tree testTreeSub1;    
+   
+    @Mock
+    private Tree testTreeSub2;
+
+    @Mock
+    private Tree testTreeSub3;
+    
+    @Mock
+    private Tree testTreeSub3Sub1;
+    
+    @Mock
+    private Tree testTreeSub3Sub2;
+    
+    @Mock
+    private Tree testTreeParentOutsideScope;
+    
+    
+    private ResourceTypePattern resourceTypePattern;
+    
+
+    @Before
+    public void setup() {
+        initMocks(this);
+        
+        setupTreeMock(testTreeParentOutsideScope, StringUtils.substringBeforeLast(TEST_PATH, "/"), null, RESOURCE_TYPE_OUTSIDE_SCOPE);
+        setupTreeMock(testTree, StringUtils.substringAfterLast(TEST_PATH, "/"), testTreeParentOutsideScope, RESOURCE_TYPE_TEST_PATH);
+
+        setupTreeMock(testTreeSub1, TEST_NODE_SUB1, testTree, RESOURCE_TYPE_SUB1);
+        setupTreeMock(testTreeSub2, TEST_NODE_SUB2, testTree, RESOURCE_TYPE_SUB2);
+        setupTreeMock(testTreeSub3, TEST_NODE_SUB3, testTree, RESOURCE_TYPE_SUB3);
+
+        setupTreeMock(testTreeSub3Sub1, TEST_NODE_SUBSUB1, testTreeSub3, RESOURCE_TYPE_SUBSUB1);
+        setupTreeMock(testTreeSub3Sub2, TEST_NODE_SUBSUB2, testTreeSub3, RESOURCE_TYPE_SUBSUB1);
+        
+    }
+
+
+
+    @Test
+    public void testBasicMatchWithoutChildren() {
+        
+        resourceTypePattern = new ResourceTypePattern(Arrays.asList(RESOURCE_TYPE_TEST_PATH, RESOURCE_TYPE_SUB1, RESOURCE_TYPE_SUB3), TEST_PATH, false);
+        
+        assertNonTreeFunctionsReturnFalse();
+        
+        assertTrue(resourceTypePattern.matches(testTree, null));
+        assertTrue(resourceTypePattern.matches(testTreeSub1, null));
+        assertFalse(resourceTypePattern.matches(testTreeSub2, null));
+        assertTrue(resourceTypePattern.matches(testTreeSub3, null));
+        assertFalse(resourceTypePattern.matches(testTreeSub3Sub1, null));
+        assertFalse(resourceTypePattern.matches(testTreeSub3Sub2, null));
+
+    }
+    
+    @Test
+    public void testMatchWithoutChildren() {
+        
+        String restrictionWithPath = RESOURCE_TYPE_SUB2+"@"+TEST_NODE_SUB2;
+        resourceTypePattern = new ResourceTypePattern(Arrays.asList(restrictionWithPath, RESOURCE_TYPE_SUB3), TEST_PATH, false);
+        
+        assertNonTreeFunctionsReturnFalse();
+        
+        assertTrue("Has to match because of @path usage "+restrictionWithPath, resourceTypePattern.matches(testTree, null));
+        assertFalse(resourceTypePattern.matches(testTreeSub1, null));
+        assertFalse("Must not match (although it has "+RESOURCE_TYPE_SUB2+", it does not have a child '"+TEST_NODE_SUB2+"' with this resource type)", resourceTypePattern.matches(testTreeSub2, null));
+        assertTrue("Has to match because of "+RESOURCE_TYPE_SUB3+" in list", resourceTypePattern.matches(testTreeSub3, null));
+        assertFalse(resourceTypePattern.matches(testTreeSub3Sub1, null));
+        assertFalse(resourceTypePattern.matches(testTreeSub3Sub2, null));
+    }
+    
+    @Test
+    public void testMatchWithChildren() {
+        
+        resourceTypePattern = new ResourceTypePattern(Arrays.asList(RESOURCE_TYPE_SUB3), TEST_PATH, true);
+        
+        assertNonTreeFunctionsReturnFalse();
+        
+        assertFalse("Not a node at or below "+RESOURCE_TYPE_SUB3, resourceTypePattern.matches(testTree, null));
+        assertFalse("Not a node at or below "+RESOURCE_TYPE_SUB3, resourceTypePattern.matches(testTreeSub1, null));
+        assertFalse("Not a node at or below "+RESOURCE_TYPE_SUB3, resourceTypePattern.matches(testTreeSub2, null));
+        assertTrue("The node with "+RESOURCE_TYPE_SUB3, resourceTypePattern.matches(testTreeSub3, null));
+        assertTrue("A node below a node with "+RESOURCE_TYPE_SUB3, resourceTypePattern.matches(testTreeSub3Sub1, null));
+        assertTrue("A node below a node with "+RESOURCE_TYPE_SUB3, resourceTypePattern.matches(testTreeSub3Sub2, null));
+    }
+    
+    @Test
+    public void testMatchWithChildrenDoesNotLeaveBasePath() {
+        
+        resourceTypePattern = new ResourceTypePattern(Arrays.asList(RESOURCE_TYPE_OUTSIDE_SCOPE), TEST_PATH, true);
+        
+        assertNonTreeFunctionsReturnFalse();
+        
+        // all false as RESOURCE_TYPE_OUTSIDE_SCOPE is only found above TEST_PATH
+        assertFalse(resourceTypePattern.matches(testTree, null));
+        assertFalse(resourceTypePattern.matches(testTreeSub1, null));
+        assertFalse(resourceTypePattern.matches(testTreeSub2, null));
+        assertFalse(resourceTypePattern.matches(testTreeSub3, null));
+        assertFalse(resourceTypePattern.matches(testTreeSub3Sub1, null));
+        assertFalse(resourceTypePattern.matches(testTreeSub3Sub2, null));
+    }
+    
+    @Test
+    public void testMatchWithChildrenAndPathUsage() {
+        
+        String restrictionWithPath = RESOURCE_TYPE_SUBSUB1+"@"+TEST_NODE_SUBSUB1;
+        resourceTypePattern = new ResourceTypePattern(Arrays.asList(restrictionWithPath), TEST_PATH, true);
+        
+        assertNonTreeFunctionsReturnFalse();
+        
+        assertFalse("This node or any of its parents do not have a sub node '"+TEST_NODE_SUBSUB1+"' with "+RESOURCE_TYPE_SUBSUB1, resourceTypePattern.matches(testTree, null));
+        assertFalse("This node or any of its parents do not have a sub node '"+TEST_NODE_SUBSUB1+"' with "+RESOURCE_TYPE_SUBSUB1, resourceTypePattern.matches(testTreeSub1, null));
+        assertFalse("This node or any of its parents do not have a sub node '"+TEST_NODE_SUBSUB1+"' with "+RESOURCE_TYPE_SUBSUB1,resourceTypePattern.matches(testTreeSub2, null));
+        assertTrue("This node does have a sub node '"+TEST_NODE_SUBSUB1+"' with "+RESOURCE_TYPE_SUBSUB1, resourceTypePattern.matches(testTreeSub3, null));
+        assertTrue("A node in parent hierarchy does have a sub node '"+TEST_NODE_SUBSUB1+"' with "+RESOURCE_TYPE_SUBSUB1, resourceTypePattern.matches(testTreeSub3Sub1, null));
+        assertTrue("A node in parent hierarchy does have a sub node '"+TEST_NODE_SUBSUB1+"' with "+RESOURCE_TYPE_SUBSUB1, resourceTypePattern.matches(testTreeSub3Sub2, null));
+    }
+    
+
+    private void assertNonTreeFunctionsReturnFalse() {
+        assertFalse(resourceTypePattern.matches(TEST_PATH));
+        assertFalse(resourceTypePattern.matches());
+    }
+
+    private void setupTreeMock(Tree tree, String path, Tree parentTree, String resourceType) {
+
+        doReturn(parentTree==null).when(tree).isRoot();
+        doReturn(parentTree).when(tree).getParent();
+        
+        mockGetStringProperty(tree, ResourceTypePattern.SLING_RESOURCE_TYPE, resourceType);
+        
+        String effectivePath = path;
+        if(parentTree!=null) {
+            effectivePath = parentTree.getPath() + "/" + path;
+            String childNodeName = StringUtils.substringAfterLast(effectivePath, "/");
+            doReturn(tree).when(parentTree).getChild(childNodeName);
+        }
+        doReturn(effectivePath).when(tree).getPath();
+        
+        // default for getChild
+        doThrow(new IllegalArgumentException()).when(tree).getChild(Mockito.anyString());
+        
+    }
+    
+    private void mockGetStringProperty(Tree tree, String propertyName, String value) {
+        PropertyState propertyState = mock(PropertyState.class);
+        doReturn(propertyState).when(tree).getProperty(propertyName);
+        doReturn(false).when(propertyState).isArray();
+        doReturn(value).when(propertyState).getValue(Type.STRING);
+    }
+}
diff --git a/src/test/java/org/apache/sling/oak/restrictions/impl/SlingRestrictionProviderImplTest.java b/src/test/java/org/apache/sling/oak/restrictions/impl/SlingRestrictionProviderImplTest.java
new file mode 100644
index 0000000..d1d58b5
--- /dev/null
+++ b/src/test/java/org/apache/sling/oak/restrictions/impl/SlingRestrictionProviderImplTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.sling.oak.restrictions.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.Restriction;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionDefinition;
+import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionPattern;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+public class SlingRestrictionProviderImplTest {
+    
+    private static final String TEST_PATH = "/content/path/to/test";
+
+    private static final String RESOURCE_TYPE1 = "myproj/comp1";
+    private static final String RESOURCE_TYPE2 = "myproj/comp2";
+    
+    @Mock
+    private Tree restrictionNodeTree;
+
+    @Mock
+    private PropertyState restrictionProperty;
+    
+    @Mock
+    private Restriction restriction;
+    
+    @Mock
+    private RestrictionDefinition definition;
+    
+    private SlingRestrictionProviderImpl slingRestrictionProviderImpl;
+
+    @Before
+    public void setup() {
+        initMocks(this);
+        
+        doReturn(definition).when(restriction).getDefinition();
+        doReturn(restrictionProperty).when(restriction).getProperty();
+    }
+    
+    @Test
+    public void testGetPatternFromTreeResourceTypes() {
+        
+        doReturn(restrictionProperty).when(restrictionNodeTree).getProperty(SlingRestrictionProviderImpl.SLING_RESOURCE_TYPES);
+        doReturn(Arrays.asList(RESOURCE_TYPE1, RESOURCE_TYPE2)).when(restrictionProperty).getValue(Type.STRINGS);
+        
+        slingRestrictionProviderImpl = new SlingRestrictionProviderImpl();
+        
+        RestrictionPattern pattern = slingRestrictionProviderImpl.getPattern(TEST_PATH, restrictionNodeTree);
+        assertTrue(pattern instanceof ResourceTypePattern);
+        ResourceTypePattern resourceTypePattern = (ResourceTypePattern) pattern;
+        
+        assertFalse(resourceTypePattern.isMatchChildren());
+        assertEquals(TEST_PATH, resourceTypePattern.getLimitedToPath());
+    }
+
+    @Test
+    public void testGetPatternFromTreeResourceTypesWithDescendants() {
+        
+        doReturn(restrictionProperty).when(restrictionNodeTree).getProperty(SlingRestrictionProviderImpl.SLING_RESOURCE_TYPES_WITH_DESCENDANTS);
+        doReturn(Arrays.asList(RESOURCE_TYPE1, RESOURCE_TYPE2)).when(restrictionProperty).getValue(Type.STRINGS);
+        
+        slingRestrictionProviderImpl = new SlingRestrictionProviderImpl();
+        
+        RestrictionPattern pattern = slingRestrictionProviderImpl.getPattern(TEST_PATH, restrictionNodeTree);
+        assertTrue(pattern instanceof ResourceTypePattern);
+        ResourceTypePattern resourceTypePattern = (ResourceTypePattern) pattern;
+        
+        assertTrue(resourceTypePattern.isMatchChildren());
+        assertEquals(TEST_PATH, resourceTypePattern.getLimitedToPath());
+    }
+    
+    @Test
+    public void testGetPatternFromRestrictionsResourceTypes() {
+        
+        doReturn(SlingRestrictionProviderImpl.SLING_RESOURCE_TYPES).when(definition).getName();
+        doReturn(Arrays.asList(RESOURCE_TYPE1, RESOURCE_TYPE2)).when(restrictionProperty).getValue(Type.STRINGS);
+        
+        slingRestrictionProviderImpl = new SlingRestrictionProviderImpl();
+        
+        RestrictionPattern pattern = slingRestrictionProviderImpl.getPattern(TEST_PATH, new HashSet<Restriction>(Arrays.asList(restriction)));
+        assertTrue(pattern instanceof ResourceTypePattern);
+        ResourceTypePattern resourceTypePattern = (ResourceTypePattern) pattern;
+        
+        assertFalse(resourceTypePattern.isMatchChildren());
+        assertEquals(TEST_PATH, resourceTypePattern.getLimitedToPath());
+
+    }
+
+    @Test
+    public void testGetPatternFromRestrictionsResourceTypesWithDescendants() {
+        
+        doReturn(SlingRestrictionProviderImpl.SLING_RESOURCE_TYPES_WITH_DESCENDANTS).when(definition).getName();
+        doReturn(Arrays.asList(RESOURCE_TYPE1, RESOURCE_TYPE2)).when(restrictionProperty).getValue(Type.STRINGS);
+        
+        slingRestrictionProviderImpl = new SlingRestrictionProviderImpl();
+        
+        RestrictionPattern pattern = slingRestrictionProviderImpl.getPattern(TEST_PATH, new HashSet<Restriction>(Arrays.asList(restriction)));
+        assertTrue(pattern instanceof ResourceTypePattern);
+        ResourceTypePattern resourceTypePattern = (ResourceTypePattern) pattern;
+        
+        assertTrue(resourceTypePattern.isMatchChildren());
+        assertEquals(TEST_PATH, resourceTypePattern.getLimitedToPath());
+
+    }
+    
+}