You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/10/18 23:21:44 UTC
[sling-org-apache-sling-resourceaccesssecurity] 01/24: SLING-3435 -
ResourceAccessSecurity does not secure access for update operations
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resourceaccesssecurity.git
commit 535810d6ffcb8180308f0c6ea1b2ab3c8ee05f60
Author: Mike Müller <my...@apache.org>
AuthorDate: Sun Mar 16 19:17:41 2014 +0000
SLING-3435 - ResourceAccessSecurity does not secure access for update operations
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1578141 13f79535-47bb-0310-9956-ffa450edef68
---
README.txt | 27 ++
pom.xml | 141 +++++++++
.../AllowingResourceAccessGate.java | 129 ++++++++
.../resourceaccesssecurity/ResourceAccessGate.java | 215 +++++++++++++
.../impl/AccessGateResourceWrapper.java | 87 ++++++
.../ApplicationResourceAccessSecurityImpl.java | 42 +++
.../impl/ProviderResourceAccessSecurityImpl.java | 42 +++
.../impl/ReadOnlyValueMapWrapper.java | 63 ++++
.../impl/ResourceAccessGateHandler.java | 122 ++++++++
.../impl/ResourceAccessSecurityImpl.java | 347 +++++++++++++++++++++
.../sling/resourceaccesssecurity/package-info.java | 24 ++
.../impl/ResourceAccessSecurityImplTests.java | 189 +++++++++++
12 files changed, 1428 insertions(+)
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..948b6c2
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,27 @@
+Apache Sling Resource Access Security
+
+This bundle provides in implementation of the ResourceAccessSecurity
+
+Getting Started
+===============
+
+This component uses a Maven 3 (http://maven.apache.org/) build
+environment. It requires a Java 5 JDK (or higher) and Maven (http://maven.apache.org/)
+3.0.3 or later. We recommend to use the latest Maven version.
+
+If you have Maven 3 installed, you can compile and
+package the jar using the following command:
+
+ mvn package
+
+See the Maven 3 documentation for other build features.
+
+The latest source code for this component is available in the
+Subversion (http://subversion.apache.org/) source repository of
+the Apache Software Foundation. If you have Subversion installed,
+you can checkout the latest source using the following command:
+
+ svn checkout http://svn.apache.org/repos/asf/sling/trunk/resourceresolver/core
+
+See the Subversion documentation for other source control features.
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..2bafb55
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,141 @@
+<?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>18</version>
+ <relativePath>../../../parent/pom.xml</relativePath>
+ </parent>
+
+ <artifactId>org.apache.sling.resourceaccesssecurity</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <name>Apache Sling Resource Access Security</name>
+ <description>
+ This bundle provides in implementation of the ResourceAccessSecurity service
+ </description>
+
+ <scm>
+ <connection>
+ scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/resourceaccesssecurity/core
+ </connection>
+ <developerConnection>
+ scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/resourceaccesssecurity/core
+ </developerConnection>
+ <url>
+ http://svn.apache.org/viewvc/sling/trunk/bundles/resourceaccesssecurity/core
+ </url>
+ </scm>
+
+ <properties>
+ <site.javadoc.exclude>**.internal.**</site.javadoc.exclude>
+ <sling.java.version>6</sling.java.version>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-scr-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>maven-sling-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>generate-adapter-metadata</id>
+ <phase>process-classes</phase>
+ <goals>
+ <goal>generate-adapter-metadata</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>javax.jcr</groupId>
+ <artifactId>jcr</artifactId>
+ <version>2.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.api</artifactId>
+ <version>2.5.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.commons.osgi</artifactId>
+ <version>2.2.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-collections</groupId>
+ <artifactId>commons-collections</artifactId>
+ <version>3.2.1</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>adapter-annotations</artifactId>
+ <version>1.0.0</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- Testing -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/src/main/java/org/apache/sling/resourceaccesssecurity/AllowingResourceAccessGate.java b/src/main/java/org/apache/sling/resourceaccesssecurity/AllowingResourceAccessGate.java
new file mode 100644
index 0000000..1e7d8c7
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourceaccesssecurity/AllowingResourceAccessGate.java
@@ -0,0 +1,129 @@
+/*
+ * 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.resourceaccesssecurity;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.security.AccessSecurityException;
+
+/**
+ * This abstract implementation of the <code>ResourceAccessGate</code> can be
+ * used to implement own resource access gates.
+ * This implementation simply allows operations, restricting implementations
+ * just need to overwrite the operations they want to restrict.
+ */
+public abstract class AllowingResourceAccessGate implements ResourceAccessGate {
+
+ @Override
+ public GateResult canRead(final Resource resource) {
+ return GateResult.DONTCARE;
+ }
+
+ @Override
+ public GateResult canCreate(final String absPathName,
+ final ResourceResolver resourceResolver) {
+ return GateResult.DONTCARE;
+ }
+
+ @Override
+ public GateResult canUpdate(final Resource resource) {
+ return GateResult.DONTCARE;
+ }
+
+ @Override
+ public GateResult canDelete(final Resource resource) {
+ return GateResult.DONTCARE;
+ }
+
+ @Override
+ public GateResult canExecute(final Resource resource) {
+ return GateResult.DONTCARE;
+ }
+
+ @Override
+ public GateResult canReadValue(final Resource resource, final String valueName) {
+ return GateResult.DONTCARE;
+ }
+
+ @Override
+ public GateResult canCreateValue(final Resource resource, final String valueName) {
+ return GateResult.DONTCARE;
+ }
+
+ @Override
+ public GateResult canUpdateValue(final Resource resource, final String valueName) {
+ return GateResult.DONTCARE;
+ }
+
+ @Override
+ public GateResult canDeleteValue(final Resource resource, final String valueName) {
+ return GateResult.DONTCARE;
+ }
+
+ @Override
+ public String transformQuery(final String query, final String language,
+ final ResourceResolver resourceResolver) throws AccessSecurityException {
+ return query;
+ }
+
+ @Override
+ public boolean hasReadRestrictions(final ResourceResolver resourceResolver) {
+ return false;
+ }
+
+ @Override
+ public boolean hasCreateRestrictions(final ResourceResolver resourceResolver) {
+ return false;
+ }
+
+ @Override
+ public boolean hasUpdateRestrictions(final ResourceResolver resourceResolver) {
+ return false;
+ }
+
+ @Override
+ public boolean hasDeleteRestrictions(final ResourceResolver resourceResolver) {
+ return false;
+ }
+
+ @Override
+ public boolean hasExecuteRestrictions(final ResourceResolver resourceResolver) {
+ return false;
+ }
+
+ @Override
+ public boolean canReadAllValues(final Resource resource) {
+ return true;
+ }
+
+ @Override
+ public boolean canCreateAllValues(final Resource resource) {
+ return true;
+ }
+
+ @Override
+ public boolean canUpdateAllValues(final Resource resource) {
+ return true;
+ }
+
+ @Override
+ public boolean canDeleteAllValues(final Resource resource) {
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/sling/resourceaccesssecurity/ResourceAccessGate.java b/src/main/java/org/apache/sling/resourceaccesssecurity/ResourceAccessGate.java
new file mode 100644
index 0000000..4b096e8
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourceaccesssecurity/ResourceAccessGate.java
@@ -0,0 +1,215 @@
+/*
+ * 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.resourceaccesssecurity;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.security.AccessSecurityException;
+
+import aQute.bnd.annotation.ConsumerType;
+
+/**
+ * The <code>ResourceAccessGate</code> defines a service API which might be used
+ * to make some restrictions to accessing resources.
+ *
+ * Implementations of this service interface must be registered like
+ * ResourceProvider with a path (like provider.roots). If different
+ * ResourceAccessGateService services match a path, not only the
+ * ResourceAccessGateService with the longest path will be called, but all of
+ * them, that's in contrast to the ResourceProvider, but in this case more
+ * logical (and secure!). The gates will be called in the order of the
+ * service ranking.
+ * If one of the gates grants access for a given operation access will be granted.
+ *
+ * service properties:
+ * <ul>
+ * <li><b>path</b>: regexp to define on which paths the service should be called
+ * (default .*)</li>
+ * <li><b>operations</b>: set of operations on which the service should be
+ * called ("read,create,update,delete,execute", default all of them)</li>
+ * <li><b>finaloperations</b>: set of operations on which the service answer is
+ * final and no further service should be called (default none of them), except
+ * the GateResult is {@link GateResult.DONTCARE}</li>
+ * </ul>
+ *
+ * The resource access gate can either have the context {@link #PROVIDER_CONTEXT},
+ * in this case the gate is only applied to resource providers requesting the
+ * security checks. Or the context can be {@link #APPLICATION_CONTEXT}. In this
+ * case the access gate is invoked for the whole resource tree.
+ * This is indicated by the required service property {@link #CONTEXT}. If the
+ * property is missing or invalid, the service is ignored.
+ */
+@ConsumerType
+public interface ResourceAccessGate {
+
+ /**
+ * The service name to use when registering implementations of this
+ * interface as services (value is
+ * "org.apache.sling.api.resource.ResourceAccessGate").
+ */
+ String SERVICE_NAME = ResourceAccessGate.class.getName();
+
+ /**
+ * The name of the service registration property containing the context
+ * of this service. Allowed values are {@link #APPLICATION_CONTEXT} and
+ * {@link #PROVIDER_CONTEXT}.
+ * This property is required and has no default value.
+ * (value is "access.context")
+ */
+ String CONTEXT = "access.context";
+
+ /**
+ * Allowed value for the {@link #CONTEXT} service registration property.
+ * Services marked with this context are applied to all resources.
+ */
+ String APPLICATION_CONTEXT = "application";
+
+ /**
+ * Allowed value for the {@link #CONTEXT} service registration property.
+ * Services marked with this context are only applied to resource
+ * providers which indicate the additional checks with the
+ * {@link org.apache.sling.api.resource.ResourceProvider#USE_RESOURCE_ACCESS_SECURITY}
+ * property.
+ */
+ String PROVIDER_CONTEXT = "provider";
+
+ /**
+ * The name of the service registration property containing the path as a
+ * regular expression for which the service should be called (value is
+ * "path").
+ */
+ String PATH = "path";
+
+ /**
+ * The name of the service registration property containing the operations
+ * for which the service should be called, defaults to all the operations
+ * (value is "operations").
+ */
+ String OPERATIONS = "operations";
+
+ /**
+ * The name of the service registration property containing the operations
+ * for which the service should be called and no further service should be
+ * called after this, except the services returns DONTCARE as result,
+ * default is empty (non of them are final) (value is "finaloperations").
+ */
+ String FINALOPERATIONS = "finaloperations";
+
+ /**
+ * <code>GateResult</code> defines 3 possible states which can be returned
+ * by the different canXXX methods of this interface.
+ * <ul>
+ * <li>GRANTED: means no restrictions</li>
+ * <li>DENIED: means no permission for the requested action</li>
+ * <li>DONTCARE: means that the implementation of the service has no
+ * information or can't decide and therefore neither can't grant or deny
+ * access</li>
+ * </ul>
+ */
+ public enum GateResult {
+ GRANTED, DENIED, DONTCARE
+ };
+
+ public enum Operation {
+ READ("read"), CREATE("create"), UPDATE("update"), DELETE("delete"), EXECUTE(
+ "execute");
+
+ private String text;
+
+ Operation(String text) {
+ this.text = text;
+ }
+
+ public static Operation fromString(String opAsString) {
+ Operation returnValue = null;
+
+ for (Operation op : Operation.values()) {
+ if (opAsString.equals(op.getText())) {
+ returnValue = op;
+ break;
+ }
+ }
+
+ return returnValue;
+ }
+
+ public String getText() {
+ return this.text;
+ }
+ }
+
+ public GateResult canRead(Resource resource);
+
+ public GateResult canCreate(String absPathName,
+ ResourceResolver resourceResolver);
+
+ public GateResult canUpdate(Resource resource);
+
+ public GateResult canDelete(Resource resource);
+
+ public GateResult canExecute(Resource resource);
+
+ public GateResult canReadValue(Resource resource, String valueName);
+
+ public GateResult canCreateValue(Resource resource, String valueName);
+
+ public GateResult canUpdateValue(Resource resource, String valueName);
+
+ public GateResult canDeleteValue(Resource resource, String valueName);
+
+ /**
+ * Allows to transform the query based on the current user's credentials.
+ * Can be used to narrow down queries to omit results that the current user
+ * is not allowed to see anyway, speeding up downstream access control.
+ *
+ * Query transformations are not critical with respect to access control as
+ * results are checked using the canRead.. methods anyway.
+ *
+ * @param query
+ * the query
+ * @param language
+ * the language in which the query is expressed
+ * @param resourceResolver
+ * the resource resolver which resolves the query
+ * @return the transformed query
+ * @throws AccessSecurityException
+ */
+ public String transformQuery(String query, String language,
+ ResourceResolver resourceResolver) throws AccessSecurityException;
+
+ /* for convenience (and performance) */
+ public boolean hasReadRestrictions(ResourceResolver resourceResolver);
+
+ public boolean hasCreateRestrictions(ResourceResolver resourceResolver);
+
+ public boolean hasUpdateRestrictions(ResourceResolver resourceResolver);
+
+ public boolean hasDeleteRestrictions(ResourceResolver resourceResolver);
+
+ public boolean hasExecuteRestrictions(ResourceResolver resourceResolver);
+
+ public boolean canReadAllValues(Resource resource);
+
+ public boolean canCreateAllValues(Resource resource);
+
+ public boolean canUpdateAllValues(Resource resource);
+
+ public boolean canDeleteAllValues(Resource resource);
+
+}
diff --git a/src/main/java/org/apache/sling/resourceaccesssecurity/impl/AccessGateResourceWrapper.java b/src/main/java/org/apache/sling/resourceaccesssecurity/impl/AccessGateResourceWrapper.java
new file mode 100644
index 0000000..fd93b2f
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourceaccesssecurity/impl/AccessGateResourceWrapper.java
@@ -0,0 +1,87 @@
+/*
+ * 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.resourceaccesssecurity.impl;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sling.api.resource.ModifiableValueMap;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceWrapper;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.resourceaccesssecurity.ResourceAccessGate;
+
+/**
+ * The <code>AccessGateResourceWrapper</code> wraps a <code>Resource</code> and
+ * intercepts calls to adaptTo to wrap the adapted <code>ValueMap</code> or
+ * also a <code>ModifiableValueMap</code> to enforce access rules defined
+ * by implementations of <code>ResourceAccessGate</code>
+ *
+ */
+public class AccessGateResourceWrapper extends ResourceWrapper {
+
+ private final List<ResourceAccessGate> accessGatesForReadValues;
+ private final boolean modifiable;
+
+ /**
+ * Creates a new wrapper instance delegating all method calls to the given
+ * <code>resource</code>, but intercepts the calls with checks to the
+ * applied ResourceAccessGate instances for read and/or update values.
+ *
+ * @param resource resource to protect
+ * @param accessGatesForReadForValues list of access gates to ask when reading values. If
+ * the list is <code>null</code> or empty there are no read restrictions
+ * @param modifiable if <code>true</code> the resource can be updated
+ */
+ public AccessGateResourceWrapper(final Resource resource,
+ final List<ResourceAccessGate> accessGatesForReadForValues,
+ final boolean modifiable ) {
+ super( resource );
+ this.accessGatesForReadValues = accessGatesForReadForValues;
+ this.modifiable = modifiable;
+ }
+
+ /**
+ * Returns the value of calling <code>adaptTo</code> on the
+ * {@link #getResource() wrapped resource}.
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+ // we do not support the deprecated PersistableValueMap
+ AdapterType adapter = getResource().adaptTo(type);
+
+ if (adapter != null && !modifiable) {
+ if (type == ModifiableValueMap.class) {
+ adapter = null;
+ }
+ else if (type == Map.class || type == ValueMap.class) {
+ // protect also against accidental modifications when changes are done in an adapted map
+ adapter = (AdapterType) new ReadOnlyValueMapWrapper((Map) adapter);
+ }
+ }
+
+
+ return adapter;
+
+
+ }
+
+
+}
diff --git a/src/main/java/org/apache/sling/resourceaccesssecurity/impl/ApplicationResourceAccessSecurityImpl.java b/src/main/java/org/apache/sling/resourceaccesssecurity/impl/ApplicationResourceAccessSecurityImpl.java
new file mode 100644
index 0000000..e784236
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourceaccesssecurity/impl/ApplicationResourceAccessSecurityImpl.java
@@ -0,0 +1,42 @@
+/*
+ * 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.resourceaccesssecurity.impl;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.ReferencePolicy;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.security.ResourceAccessSecurity;
+import org.apache.sling.resourceaccesssecurity.ResourceAccessGate;
+
+@Component
+@Service(value=ResourceAccessSecurity.class)
+@Property(name=ResourceAccessSecurity.CONTEXT, value=ResourceAccessSecurity.APPLICATION_CONTEXT)
+@Reference(name="ResourceAccessGate", referenceInterface=ResourceAccessGate.class,
+ cardinality=ReferenceCardinality.MANDATORY_MULTIPLE,
+ policy=ReferencePolicy.DYNAMIC,
+ target="(" + ResourceAccessGate.CONTEXT + "=" + ResourceAccessGate.APPLICATION_CONTEXT + ")")
+public class ApplicationResourceAccessSecurityImpl extends ResourceAccessSecurityImpl {
+
+ public ApplicationResourceAccessSecurityImpl() {
+ super(true);
+ }
+}
diff --git a/src/main/java/org/apache/sling/resourceaccesssecurity/impl/ProviderResourceAccessSecurityImpl.java b/src/main/java/org/apache/sling/resourceaccesssecurity/impl/ProviderResourceAccessSecurityImpl.java
new file mode 100644
index 0000000..80fd43e
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourceaccesssecurity/impl/ProviderResourceAccessSecurityImpl.java
@@ -0,0 +1,42 @@
+/*
+ * 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.resourceaccesssecurity.impl;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.ReferencePolicy;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.security.ResourceAccessSecurity;
+import org.apache.sling.resourceaccesssecurity.ResourceAccessGate;
+
+@Component
+@Service(value=ResourceAccessSecurity.class)
+@Property(name=ResourceAccessSecurity.CONTEXT, value=ResourceAccessSecurity.PROVIDER_CONTEXT)
+@Reference(name="ResourceAccessGate", referenceInterface=ResourceAccessGate.class,
+ cardinality=ReferenceCardinality.MANDATORY_MULTIPLE,
+ policy=ReferencePolicy.DYNAMIC,
+ target="(" + ResourceAccessGate.CONTEXT + "=" + ResourceAccessGate.PROVIDER_CONTEXT + ")")
+public class ProviderResourceAccessSecurityImpl extends ResourceAccessSecurityImpl {
+
+ public ProviderResourceAccessSecurityImpl() {
+ super(false);
+ }
+}
diff --git a/src/main/java/org/apache/sling/resourceaccesssecurity/impl/ReadOnlyValueMapWrapper.java b/src/main/java/org/apache/sling/resourceaccesssecurity/impl/ReadOnlyValueMapWrapper.java
new file mode 100644
index 0000000..43ac8db
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourceaccesssecurity/impl/ReadOnlyValueMapWrapper.java
@@ -0,0 +1,63 @@
+/*
+ * 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.resourceaccesssecurity.impl;
+
+import java.util.Map;
+
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+
+/**
+ * Wrapper class that does protect the underlying map from modifications.
+ */
+public class ReadOnlyValueMapWrapper extends ValueMapDecorator
+ implements ValueMap {
+
+ /**
+ * Creates a new wrapper around a given map.
+ *
+ * @param base wrapped object
+ */
+ public ReadOnlyValueMapWrapper(Map<String, Object> base) {
+ super(base);
+ }
+
+ @Override
+ public Object put(String key, Object value) {
+ // TODO we probably should log this as a warning
+ return null;
+ }
+
+ @Override
+ public Object remove(Object key) {
+ // TODO we probably should log this as a warning
+ return null;
+ }
+
+ @Override
+ public void putAll(Map<? extends String, ?> t) {
+ // TODO we probably should log this as a warning
+ }
+
+ @Override
+ public void clear() {
+ // TODO we probably should log this as a warning
+ }
+}
diff --git a/src/main/java/org/apache/sling/resourceaccesssecurity/impl/ResourceAccessGateHandler.java b/src/main/java/org/apache/sling/resourceaccesssecurity/impl/ResourceAccessGateHandler.java
new file mode 100644
index 0000000..7cb94d6
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourceaccesssecurity/impl/ResourceAccessGateHandler.java
@@ -0,0 +1,122 @@
+/*
+ * 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.resourceaccesssecurity.impl;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.apache.sling.resourceaccesssecurity.ResourceAccessGate;
+import org.osgi.framework.ServiceReference;
+
+public class ResourceAccessGateHandler implements Comparable<ResourceAccessGateHandler> {
+
+ private final ResourceAccessGate resourceAccessGate;
+
+ private final ServiceReference reference;
+
+ private final Pattern pathPattern;
+ private final Set<ResourceAccessGate.Operation> operations = new HashSet<ResourceAccessGate.Operation>();
+ private final Set<ResourceAccessGate.Operation> finalOperations = new HashSet<ResourceAccessGate.Operation>();
+
+ /**
+ * constructor
+ */
+ public ResourceAccessGateHandler ( final ServiceReference resourceAccessGateRef ) {
+ this.reference = resourceAccessGateRef;
+
+ resourceAccessGate = (ResourceAccessGate) resourceAccessGateRef.getBundle().
+ getBundleContext().getService(resourceAccessGateRef);
+ // extract the service property "path"
+ final String path = (String) resourceAccessGateRef.getProperty(ResourceAccessGate.PATH);
+ if ( path != null ) {
+ pathPattern = Pattern.compile(path);
+ } else {
+ pathPattern = Pattern.compile(".*");
+ }
+
+ // extract the service property "operations"
+ final String ops = PropertiesUtil.toString( resourceAccessGateRef.getProperty(ResourceAccessGate.OPERATIONS), null );
+ if ( ops != null && ops.length() > 0 ) {
+ final String[] opsArray = ops.split( "," );
+ for (final String opAsString : opsArray) {
+ final ResourceAccessGate.Operation operation = ResourceAccessGate.Operation.fromString(opAsString);
+ if ( operation != null ) {
+ operations.add(operation);
+ }
+ }
+ } else {
+ for (final ResourceAccessGate.Operation op : ResourceAccessGate.Operation.values() ) {
+ operations.add(op);
+ }
+ }
+
+ // extract the service property "finaloperations"
+ final String finalOps = PropertiesUtil.toString(resourceAccessGateRef.getProperty(ResourceAccessGate.FINALOPERATIONS), null );
+ if ( finalOps != null && finalOps.length() > 0 ) {
+ final String[] finOpsArray = finalOps.split( "," );
+ for (final String opAsString : finOpsArray) {
+ final ResourceAccessGate.Operation operation = ResourceAccessGate.Operation.fromString(opAsString);
+ if ( operation != null ) {
+ finalOperations.add(operation);
+ }
+ }
+ }
+
+ }
+
+ public boolean matches ( final String path, final ResourceAccessGate.Operation operation ) {
+ boolean returnValue = false;
+
+ if ( operations.contains( operation ) ) {
+ final Matcher match = pathPattern.matcher(path);
+ returnValue = match.matches();
+ }
+
+ return returnValue;
+ }
+
+ public boolean isFinalOperation( final ResourceAccessGate.Operation operation ) {
+ return finalOperations.contains(operation);
+ }
+
+ public ResourceAccessGate getResourceAccessGate () {
+ return resourceAccessGate;
+ }
+
+ @Override
+ public int compareTo(final ResourceAccessGateHandler o) {
+ return this.reference.compareTo(o.reference);
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if ( obj instanceof ResourceAccessGateHandler ) {
+ return ((ResourceAccessGateHandler)obj).reference.equals(this.reference);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.reference.hashCode();
+ }
+}
diff --git a/src/main/java/org/apache/sling/resourceaccesssecurity/impl/ResourceAccessSecurityImpl.java b/src/main/java/org/apache/sling/resourceaccesssecurity/impl/ResourceAccessSecurityImpl.java
new file mode 100644
index 0000000..72279e5
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourceaccesssecurity/impl/ResourceAccessSecurityImpl.java
@@ -0,0 +1,347 @@
+/*
+ * 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.resourceaccesssecurity.impl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.security.AccessSecurityException;
+import org.apache.sling.api.security.ResourceAccessSecurity;
+import org.apache.sling.resourceaccesssecurity.ResourceAccessGate;
+import org.apache.sling.resourceaccesssecurity.ResourceAccessGate.GateResult;
+import org.osgi.framework.ServiceReference;
+
+public abstract class ResourceAccessSecurityImpl implements ResourceAccessSecurity {
+
+ private List<ResourceAccessGateHandler> allHandlers = Collections.emptyList();
+
+ private final boolean defaultAllow;
+
+ public ResourceAccessSecurityImpl(final boolean defaultAllow) {
+ this.defaultAllow = defaultAllow;
+ }
+
+ /**
+ * This method returns either an iterator delivering the matching handlers
+ * or <code>null</code>.
+ */
+ private Iterator<ResourceAccessGateHandler> getMatchingResourceAccessGateHandlerIterator(
+ final String path, final ResourceAccessGate.Operation operation) {
+ //
+ // TODO: maybe caching some frequent paths with read operation would be
+ // a good idea
+ //
+ final List<ResourceAccessGateHandler> handlers = allHandlers;
+ if (handlers.size() > 0) {
+
+ final Iterator<ResourceAccessGateHandler> iter = handlers.iterator();
+ return new Iterator<ResourceAccessGateHandler>() {
+
+ private ResourceAccessGateHandler next;
+
+ {
+ peek();
+ }
+
+ private void peek() {
+ this.next = null;
+ while ( iter.hasNext() && next == null ) {
+ final ResourceAccessGateHandler handler = iter.next();
+ if (handler.matches(path, operation)) {
+ next = handler;
+ }
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ @Override
+ public ResourceAccessGateHandler next() {
+ if ( next == null ) {
+ throw new NoSuchElementException();
+ }
+ final ResourceAccessGateHandler handler = this.next;
+ peek();
+ return handler;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ return null;
+ }
+
+ @Override
+ public Resource getReadableResource(final Resource resource) {
+ Resource returnValue = (this.defaultAllow ? resource : null);
+
+ final Iterator<ResourceAccessGateHandler> accessGateHandlers = getMatchingResourceAccessGateHandlerIterator(
+ resource.getPath(), ResourceAccessGate.Operation.READ);
+
+ GateResult finalGateResult = null;
+ List<ResourceAccessGate> accessGatesForReadValues = null;
+ boolean canReadAllValues = false;
+
+
+ if ( accessGateHandlers != null ) {
+
+ while ( accessGateHandlers.hasNext() ) {
+ final ResourceAccessGateHandler resourceAccessGateHandler = accessGateHandlers.next();
+
+ final GateResult gateResult = resourceAccessGateHandler.getResourceAccessGate().canRead(resource);
+ if (!canReadAllValues && gateResult == GateResult.GRANTED) {
+ if (resourceAccessGateHandler.getResourceAccessGate().canReadAllValues(resource)) {
+ canReadAllValues = true;
+ accessGatesForReadValues = null;
+ } else {
+ if (accessGatesForReadValues == null) {
+ accessGatesForReadValues = new ArrayList<ResourceAccessGate>();
+ }
+ accessGatesForReadValues.add(resourceAccessGateHandler.getResourceAccessGate());
+ }
+ }
+ if (finalGateResult == null) {
+ finalGateResult = gateResult;
+ } else if (finalGateResult != GateResult.GRANTED && gateResult != GateResult.DONTCARE) {
+ finalGateResult = gateResult;
+ }
+ // stop checking if the operation is final and the result not GateResult.DONTCARE
+ if (gateResult != GateResult.DONTCARE && resourceAccessGateHandler.isFinalOperation(ResourceAccessGate.Operation.READ)) {
+ break;
+ }
+ }
+
+
+ // return null if access is denied or no ResourceAccessGate is present
+ if (finalGateResult == null || finalGateResult == GateResult.DENIED) {
+ returnValue = null;
+ } else if (finalGateResult == GateResult.DONTCARE) {
+ returnValue = (this.defaultAllow ? resource : null);
+ } else if (finalGateResult == GateResult.GRANTED ) {
+ returnValue = resource;
+ }
+ }
+
+ boolean canUpdateResource = canUpdate(resource);
+
+ // wrap Resource if read access is not or partly (values) not granted
+ if (returnValue != null) {
+ if( !canReadAllValues || !canUpdateResource ) {
+ returnValue = new AccessGateResourceWrapper(returnValue,
+ accessGatesForReadValues,
+ canUpdateResource);
+ }
+ }
+
+ return returnValue;
+ }
+
+ @Override
+ public boolean canCreate(final String path,
+ final ResourceResolver resolver) {
+ final Iterator<ResourceAccessGateHandler> handlers = getMatchingResourceAccessGateHandlerIterator(
+ path, ResourceAccessGate.Operation.CREATE);
+ boolean result = this.defaultAllow;
+ if ( handlers != null ) {
+ GateResult finalGateResult = null;
+
+ while ( handlers.hasNext() ) {
+ final ResourceAccessGateHandler resourceAccessGateHandler = handlers.next();
+
+ final GateResult gateResult = resourceAccessGateHandler.getResourceAccessGate().canCreate(path, resolver);
+ if (finalGateResult == null) {
+ finalGateResult = gateResult;
+ } else if (finalGateResult != GateResult.GRANTED && gateResult != GateResult.DONTCARE) {
+ finalGateResult = gateResult;
+ }
+ if (finalGateResult == GateResult.GRANTED || gateResult != GateResult.DONTCARE &&
+ resourceAccessGateHandler.isFinalOperation(ResourceAccessGate.Operation.CREATE)) {
+ break;
+ }
+ }
+
+ if ( finalGateResult == GateResult.GRANTED ) {
+ result = true;
+ } else if ( finalGateResult == GateResult.DENIED ) {
+ result = false;
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public boolean canUpdate(final Resource resource) {
+ final Iterator<ResourceAccessGateHandler> handlers = getMatchingResourceAccessGateHandlerIterator(
+ resource.getPath(), ResourceAccessGate.Operation.UPDATE);
+ boolean result = this.defaultAllow;
+ if ( handlers != null ) {
+ GateResult finalGateResult = null;
+
+ while ( handlers.hasNext() ) {
+ final ResourceAccessGateHandler resourceAccessGateHandler = handlers.next();
+
+ final GateResult gateResult = resourceAccessGateHandler.getResourceAccessGate().canUpdate(resource);
+ if (finalGateResult == null) {
+ finalGateResult = gateResult;
+ } else if (finalGateResult != GateResult.GRANTED && gateResult != GateResult.DONTCARE) {
+ finalGateResult = gateResult;
+ }
+ if (finalGateResult == GateResult.GRANTED || gateResult != GateResult.DONTCARE &&
+ resourceAccessGateHandler.isFinalOperation(ResourceAccessGate.Operation.UPDATE)) {
+ break;
+ }
+ }
+
+ if ( finalGateResult == GateResult.GRANTED ) {
+ result = true;
+ } else if ( finalGateResult == GateResult.DENIED ) {
+ result = false;
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public boolean canDelete(final Resource resource) {
+ final Iterator<ResourceAccessGateHandler> handlers = getMatchingResourceAccessGateHandlerIterator(
+ resource.getPath(), ResourceAccessGate.Operation.DELETE);
+ boolean result = this.defaultAllow;
+ if ( handlers != null ) {
+ GateResult finalGateResult = null;
+
+ while ( handlers.hasNext() ) {
+ final ResourceAccessGateHandler resourceAccessGateHandler = handlers.next();
+
+ final GateResult gateResult = resourceAccessGateHandler.getResourceAccessGate().canDelete(resource);
+ if (finalGateResult == null) {
+ finalGateResult = gateResult;
+ } else if (finalGateResult != GateResult.GRANTED && gateResult != GateResult.DONTCARE) {
+ finalGateResult = gateResult;
+ }
+ if (finalGateResult == GateResult.GRANTED || gateResult != GateResult.DONTCARE &&
+ resourceAccessGateHandler.isFinalOperation(ResourceAccessGate.Operation.DELETE)) {
+ break;
+ }
+ }
+
+ if ( finalGateResult == GateResult.GRANTED ) {
+ result = true;
+ } else if ( finalGateResult == GateResult.DENIED ) {
+ result = false;
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public boolean canExecute(final Resource resource) {
+ final Iterator<ResourceAccessGateHandler> handlers = getMatchingResourceAccessGateHandlerIterator(
+ resource.getPath(), ResourceAccessGate.Operation.EXECUTE);
+ boolean result = this.defaultAllow;
+ if ( handlers != null ) {
+ GateResult finalGateResult = null;
+
+ while ( handlers.hasNext() ) {
+ final ResourceAccessGateHandler resourceAccessGateHandler = handlers.next();
+
+ final GateResult gateResult = resourceAccessGateHandler.getResourceAccessGate().canExecute(resource);
+ if (finalGateResult == null) {
+ finalGateResult = gateResult;
+ } else if (finalGateResult != GateResult.GRANTED && gateResult != GateResult.DONTCARE) {
+ finalGateResult = gateResult;
+ }
+ if (finalGateResult == GateResult.GRANTED || gateResult != GateResult.DONTCARE && resourceAccessGateHandler.isFinalOperation(ResourceAccessGate.Operation.EXECUTE)) {
+ break;
+ }
+ }
+
+ if ( finalGateResult == GateResult.GRANTED ) {
+ result = true;
+ } else if ( finalGateResult == GateResult.DENIED ) {
+ result = false;
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public boolean canReadValue(final Resource resource, final String valueName) {
+ // TODO Auto-generated method stub
+ return this.defaultAllow;
+ }
+
+ @Override
+ public boolean canSetValue(final Resource resource, final String valueName) {
+ // TODO Auto-generated method stub
+ return this.defaultAllow;
+ }
+
+ @Override
+ public boolean canDeleteValue(final Resource resource, final String valueName) {
+ // TODO Auto-generated method stub
+ return this.defaultAllow;
+ }
+
+ @Override
+ public String transformQuery(final String query,
+ final String language,
+ final ResourceResolver resourceResolver)
+ throws AccessSecurityException {
+ return query;
+ }
+
+ /**
+ * Add a new resource access gate
+ */
+ protected void bindResourceAccessGate(final ServiceReference ref) {
+ synchronized ( this ) {
+ final List<ResourceAccessGateHandler> newList = new ArrayList<ResourceAccessGateHandler>(this.allHandlers);
+
+ final ResourceAccessGateHandler h = new ResourceAccessGateHandler(ref);
+ newList.add(h);
+ Collections.sort(newList);
+ this.allHandlers = newList;
+ }
+ }
+
+ /**
+ * Remove a resource access gate
+ */
+ protected void unbindResourceAccessGate(final ServiceReference ref) {
+ synchronized ( this ) {
+ final List<ResourceAccessGateHandler> newList = new ArrayList<ResourceAccessGateHandler>(this.allHandlers);
+
+ final ResourceAccessGateHandler h = new ResourceAccessGateHandler(ref);
+ newList.remove(h);
+ this.allHandlers = newList;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/sling/resourceaccesssecurity/package-info.java b/src/main/java/org/apache/sling/resourceaccesssecurity/package-info.java
new file mode 100644
index 0000000..61145aa
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourceaccesssecurity/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+@Version("1.0.0")
+package org.apache.sling.resourceaccesssecurity;
+
+import aQute.bnd.annotation.Version;
+
diff --git a/src/test/java/org/apache/sling/resourceaccesssecurity/impl/ResourceAccessSecurityImplTests.java b/src/test/java/org/apache/sling/resourceaccesssecurity/impl/ResourceAccessSecurityImplTests.java
new file mode 100644
index 0000000..c7575a6
--- /dev/null
+++ b/src/test/java/org/apache/sling/resourceaccesssecurity/impl/ResourceAccessSecurityImplTests.java
@@ -0,0 +1,189 @@
+/*
+ * 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.resourceaccesssecurity.impl;
+
+
+import junit.framework.TestCase;
+import org.apache.sling.api.resource.ModifiableValueMap;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.security.ResourceAccessSecurity;
+import org.apache.sling.resourceaccesssecurity.ResourceAccessGate;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import static org.junit.Assert.assertTrue;
+
+public class ResourceAccessSecurityImplTests {
+
+ ServiceReference serviceReference;
+ ResourceAccessSecurity resourceAccessSecurity;
+ ResourceAccessGate resourceAccessGate;
+
+ @Before
+ public void setUp() {
+ resourceAccessSecurity = new ProviderResourceAccessSecurityImpl();
+ }
+
+
+ @Test
+ public void testCanUpdate(){
+ initMocks("/content", new String[] { "update"} );
+
+ Resource resource = mock(Resource.class);
+ when(resource.getPath()).thenReturn("/content");
+ when(resourceAccessGate.canUpdate(resource)).thenReturn(ResourceAccessGate.GateResult.GRANTED);
+ assertTrue(resourceAccessSecurity.canUpdate(resource));
+ }
+
+ @Test
+ public void testCannotUpdate(){
+ initMocks("/content", new String[] { "update"} );
+
+ Resource resource = mock(Resource.class);
+ when(resource.getPath()).thenReturn("/content");
+ when(resourceAccessGate.canUpdate(resource)).thenReturn(ResourceAccessGate.GateResult.DENIED);
+ assertFalse(resourceAccessSecurity.canUpdate(resource));
+ }
+
+ @Test
+ public void testCannotUpdateWrongPath(){
+ initMocks("/content", new String[] { "update"} );
+
+ Resource resource = mock(Resource.class);
+ when(resource.getPath()).thenReturn("/wrongcontent");
+ when(resourceAccessGate.canUpdate(resource)).thenReturn(ResourceAccessGate.GateResult.GRANTED);
+ assertFalse(resourceAccessSecurity.canUpdate(resource));
+ }
+
+ @Test
+ public void testCanUpdateUsingReadableResource(){
+ // one needs to have also read rights to obtain the resource
+
+ initMocks("/content", new String[] { "read", "update"} );
+
+ Resource resource = mock(Resource.class);
+ when(resource.getPath()).thenReturn("/content");
+
+ ModifiableValueMap valueMap = mock(ModifiableValueMap.class);
+ when(resource.adaptTo(ModifiableValueMap.class)).thenReturn(valueMap);
+
+ when(resourceAccessGate.canRead(resource)).thenReturn(ResourceAccessGate.GateResult.GRANTED);
+ when(resourceAccessGate.canUpdate(resource)).thenReturn(ResourceAccessGate.GateResult.GRANTED);
+ Resource readableResource = resourceAccessSecurity.getReadableResource(resource);
+
+ ModifiableValueMap resultValueMap = readableResource.adaptTo(ModifiableValueMap.class);
+
+
+ resultValueMap.put("modified", "value");
+
+ verify(valueMap, times(1)).put("modified", "value");
+ }
+
+
+ @Test
+ public void testCannotUpdateUsingReadableResourceIfCannotRead(){
+ initMocks("/content", new String[] { "read", "update"} );
+
+ Resource resource = mock(Resource.class);
+ when(resource.getPath()).thenReturn("/content");
+
+ ModifiableValueMap valueMap = mock(ModifiableValueMap.class);
+ when(resource.adaptTo(ModifiableValueMap.class)).thenReturn(valueMap);
+
+ when(resourceAccessGate.canRead(resource)).thenReturn(ResourceAccessGate.GateResult.DENIED);
+ when(resourceAccessGate.canUpdate(resource)).thenReturn(ResourceAccessGate.GateResult.GRANTED);
+ Resource readableResource = resourceAccessSecurity.getReadableResource(resource);
+
+
+ assertNull(readableResource);
+ }
+
+
+ @Test
+ public void testCannotUpdateUsingReadableResourceIfCannotUpdate(){
+ initMocks("/content", new String[] { "read", "update"} );
+
+ Resource resource = mock(Resource.class);
+ when(resource.getPath()).thenReturn("/content");
+
+ ModifiableValueMap valueMap = mock(ModifiableValueMap.class);
+ when(resource.adaptTo(ModifiableValueMap.class)).thenReturn(valueMap);
+
+ when(resourceAccessGate.canRead(resource)).thenReturn(ResourceAccessGate.GateResult.GRANTED);
+ when(resourceAccessGate.canUpdate(resource)).thenReturn(ResourceAccessGate.GateResult.DENIED);
+ Resource readableResource = resourceAccessSecurity.getReadableResource(resource);
+
+ ModifiableValueMap resultValueMap = readableResource.adaptTo(ModifiableValueMap.class);
+
+ assertNull(resultValueMap);
+ }
+
+
+ @Test
+ public void testCannotUpdateAccidentallyUsingReadableResourceIfCannotUpdate(){
+ initMocks("/content", new String[] { "read", "update"} );
+
+ Resource resource = mock(Resource.class);
+ when(resource.getPath()).thenReturn("/content");
+
+ ModifiableValueMap valueMap = mock(ModifiableValueMap.class);
+ when(resource.adaptTo(ModifiableValueMap.class)).thenReturn(valueMap);
+
+ when(resourceAccessGate.canRead(resource)).thenReturn(ResourceAccessGate.GateResult.GRANTED);
+ when(resourceAccessGate.canUpdate(resource)).thenReturn(ResourceAccessGate.GateResult.DENIED);
+ Resource readableResource = resourceAccessSecurity.getReadableResource(resource);
+
+ ValueMap resultValueMap = readableResource.adaptTo(ValueMap.class);
+
+ resultValueMap.put("modified", "value");
+
+ verify(valueMap, times(0)).put("modified", "value");
+ }
+
+ private void initMocks(String path, String[] operations){
+ serviceReference = mock(ServiceReference.class);
+ Bundle bundle = mock(Bundle.class);
+ BundleContext bundleContext = mock(BundleContext.class);
+ resourceAccessGate = mock(ResourceAccessGate.class);
+
+ when(serviceReference.getBundle()).thenReturn(bundle);
+ when(bundle.getBundleContext()).thenReturn(bundleContext);
+ when(bundleContext.getService(serviceReference)).thenReturn(resourceAccessGate);
+
+ when(serviceReference.getProperty(ResourceAccessGate.PATH)).thenReturn(path);
+ when(serviceReference.getProperty(ResourceAccessGate.OPERATIONS)).thenReturn(operations);
+
+ ((ProviderResourceAccessSecurityImpl) resourceAccessSecurity).bindResourceAccessGate(serviceReference);
+ }
+
+
+
+}
--
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.