This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.oak.restrictions-1.0.0 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-oak-restrictions.git
commit 49238f735e5ab846627db7f327e5daf890a3aa31 Author: Robert Munteanu <[email protected]> AuthorDate: Tue Sep 27 20:25:42 2016 +0000 Rename sling-oak-restrictions folder to oak-restrictions The sling prefix is redundant. git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/contrib/extensions/oak-restrictions@1762555 13f79535-47bb-0310-9956-ffa450edef68 --- pom.xml | 114 +++++++++++ .../oak/restrictions/impl/ResourceTypePattern.java | 199 ++++++++++++++++++++ .../impl/SlingRestrictionProviderImpl.java | 120 ++++++++++++ .../restrictions/impl/ResourceTypePatternTest.java | 209 +++++++++++++++++++++ .../impl/SlingRestrictionProviderImplTest.java | 133 +++++++++++++ 5 files changed, 775 insertions(+) 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()); + + } + +} -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
