SLING-10436 Move integration tests (#3)
diff --git a/pom.xml b/pom.xml
index 169ed1d..5e25700 100644
--- a/pom.xml
+++ b/pom.xml
@@ -44,6 +44,9 @@
<properties>
<sling.java.version>8</sling.java.version>
<project.build.outputTimestamp>2020-10-15T22:30:27Z</project.build.outputTimestamp>
+ <org.ops4j.pax.exam.version>4.13.3</org.ops4j.pax.exam.version>
+ <!-- To debug the pax process, override this with -D -->
+ <pax.vm.options>-Xmx512M</pax.vm.options>
</properties>
<build>
@@ -55,6 +58,43 @@
<additionalparam>-Xdoclint:none</additionalparam>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.apache.servicemix.tooling</groupId>
+ <artifactId>depends-maven-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>integration-test</id>
+ <phase>integration-test</phase>
+ <goals>
+ <goal>integration-test</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>verify</id>
+ <phase>integration-test</phase>
+ <goals>
+ <goal>verify</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <redirectTestOutputToFile>true</redirectTestOutputToFile>
+ <systemProperties>
+ <property>
+ <name>bundle.filename</name>
+ <value>${basedir}/target/${project.build.finalName}.jar</value>
+ </property>
+ <property>
+ <name>pax.vm.options</name>
+ <value>${pax.vm.options}</value>
+ </property>
+ </systemProperties>
+ </configuration>
+ </plugin>
</plugins>
</build>
@@ -68,12 +108,12 @@
<groupId>org.osgi</groupId>
<artifactId>osgi.cmpn</artifactId>
<scope>provided</scope>
- </dependency>
+ </dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.annotation.versioning</artifactId>
</dependency>
-
+
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
@@ -140,6 +180,78 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam</artifactId>
+ <version>${org.ops4j.pax.exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-cm</artifactId>
+ <version>${org.ops4j.pax.exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-container-forked</artifactId>
+ <version>${org.ops4j.pax.exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-junit4</artifactId>
+ <version>${org.ops4j.pax.exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-link-mvn</artifactId>
+ <version>${org.ops4j.pax.exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.testing.paxexam</artifactId>
+ <version>3.1.0</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpcore</artifactId>
+ <version>4.4.13</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>4.5.13</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.healthcheck.api</artifactId>
+ <version>2.0.4</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.awaitility</groupId>
+ <artifactId>awaitility</artifactId>
+ <version>4.0.0</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.inject</groupId>
+ <artifactId>javax.inject</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.framework</artifactId>
+ <version>7.0.0</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</dependency>
diff --git a/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/AccessManagerClientTestSupport.java b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/AccessManagerClientTestSupport.java
new file mode 100644
index 0000000..ebda239
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/AccessManagerClientTestSupport.java
@@ -0,0 +1,448 @@
+/*
+ * 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.jcr.jackrabbit.accessmanager.it;
+
+import static org.apache.sling.testing.paxexam.SlingOptions.slingCommonsCompiler;
+import static org.apache.sling.testing.paxexam.SlingOptions.slingJcrJackrabbitSecurity;
+import static org.apache.sling.testing.paxexam.SlingOptions.slingScriptingJavascript;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.ops4j.pax.exam.CoreOptions.when;
+import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.factoryConfiguration;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.JsonReader;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.Header;
+import org.apache.http.HttpHost;
+import org.apache.http.NameValuePair;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.AuthCache;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.config.CookieSpecs;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.impl.auth.BasicScheme;
+import org.apache.http.impl.client.BasicAuthCache;
+import org.apache.http.impl.client.BasicCookieStore;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.ops4j.pax.exam.Option;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * base class for tests doing http requests to verify calls to the accessmanager
+ * servlets
+ */
+public abstract class AccessManagerClientTestSupport extends AccessManagerTestSupport {
+ protected static final String TEST_FOLDER_JSON = "{'jcr:primaryType': 'nt:unstructured'}";
+
+ protected static final String CONTENT_TYPE_JSON = "application/json";
+ protected static final String CONTENT_TYPE_HTML = "text/html";
+
+ protected static long randomId = System.currentTimeMillis();
+
+ protected static synchronized long getNextInt() {
+ final long val = randomId;
+ randomId++;
+ return val;
+ }
+
+ @Inject
+ protected ConfigurationAdmin cm;
+
+ protected static final String COOKIE_SLING_FORMAUTH = "sling.formauth";
+ protected static final String COOKIE_SLING_FORMAUTH_DOMAIN = "sling.formauth.cookie.domain";
+ protected static final String HEADER_SET_COOKIE = "Set-Cookie";
+
+ protected URI baseServerUri;
+ protected HttpClientContext httpContext;
+ protected CloseableHttpClient httpClient;
+
+ protected String testUserId = null;
+ protected String testUserId2 = null;
+ protected String testGroupId = null;
+ protected String testFolderUrl = null;
+
+ @Override
+ protected Option[] additionalOptions() throws IOException {
+ // optionally create a tinybundle that contains a test script
+ final Option bundle = buildBundleResourcesBundle();
+
+ return new Option[]{
+ // for usermanager support
+ slingJcrJackrabbitSecurity(),
+ // add javascript support for the test script
+ slingCommonsCompiler(),
+ when(bundle != null).useOptions(slingScriptingJavascript()),
+
+ // add the test script tinybundle
+ when(bundle != null).useOptions(bundle),
+
+ // enable the healthcheck configuration for checking when the server is ready to
+ // receive http requests. (adapted from the starter healthcheck.json configuration)
+ factoryConfiguration("org.apache.felix.hc.generalchecks.FrameworkStartCheck")
+ .put("hc.tags", new String[] {"systemalive"})
+ .put("targetStartLevel", 5)
+ .asOption(),
+ factoryConfiguration("org.apache.felix.hc.generalchecks.ServicesCheck")
+ .put("hc.tags", new String[] {"systemalive"})
+ .put("services.list", new String[] {
+ "org.apache.sling.jcr.api.SlingRepository",
+ "org.apache.sling.engine.auth.Authenticator",
+ "org.apache.sling.api.resource.ResourceResolverFactory",
+ "org.apache.sling.api.servlets.ServletResolver",
+ "javax.script.ScriptEngineManager"
+ })
+ .asOption(),
+ factoryConfiguration("org.apache.felix.hc.generalchecks.BundlesStartedCheck")
+ .put("hc.tags", new String[] {"bundles"})
+ .asOption(),
+ factoryConfiguration("org.apache.sling.jcr.contentloader.hc.BundleContentLoadedCheck")
+ .put("hc.tags", new String[] {"bundles"})
+ .asOption(),
+ };
+ }
+
+ @Before
+ public void before() throws IOException, URISyntaxException {
+ // wait for the health checks to be OK
+ waitForServerReady(Duration.ofMinutes(1).toMillis(), 500);
+
+ // calculate the address of the http server
+ baseServerUri = getBaseServerUri();
+ assertNotNull(baseServerUri);
+
+ HttpHost targetHost = new HttpHost(baseServerUri.getHost(), baseServerUri.getPort(), baseServerUri.getScheme());
+ AuthCache authCache = new BasicAuthCache();
+ authCache.put(targetHost, new BasicScheme());
+
+ // prepare the http client for the test user
+ httpContext = HttpClientContext.create();
+ httpContext.setCookieStore(new BasicCookieStore());
+ httpContext.setCredentialsProvider(new BasicCredentialsProvider());
+ httpContext.setAuthCache(authCache);
+ RequestConfig requestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT).build();
+ httpContext.setRequestConfig(requestConfig);
+ httpClient = HttpClients.custom()
+ .disableRedirectHandling()
+ .build();
+ }
+
+ @After
+ public void after() throws IOException {
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+
+ if (testFolderUrl != null) {
+ //remove the test user if it exists.
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":operation", "delete"));
+ assertAuthenticatedPostStatus(creds, testFolderUrl, HttpServletResponse.SC_OK, postParams, null);
+ }
+ if (testGroupId != null) {
+ //remove the test user if it exists.
+ String postUrl = String.format("%s/system/userManager/group/%s.delete.html", baseServerUri, testGroupId);
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, Collections.emptyList(), null);
+ }
+ if (testUserId != null) {
+ //remove the test user if it exists.
+ String postUrl = String.format("%s/system/userManager/user/%s.delete.html", baseServerUri, testUserId);
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, Collections.emptyList(), null);
+ }
+ if (testUserId2 != null) {
+ //remove the test user if it exists.
+ String postUrl = String.format("%s/system/userManager/user/%s.delete.html", baseServerUri, testUserId2);
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, Collections.emptyList(), null);
+ }
+
+ // close/cleanup the test user http client
+ if (httpClient != null) {
+ httpClient.close();
+ httpClient = null;
+ }
+
+ // clear out other state
+ httpContext = null;
+ baseServerUri = null;
+ }
+
+ /**
+ * Calculate the base server URI from the current configuration of the
+ * httpservice
+ */
+ protected URI getBaseServerUri() throws IOException, URISyntaxException {
+ assertNotNull(cm);
+ Configuration httpServiceConfiguration = cm.getConfiguration("org.apache.felix.http");
+ Dictionary<String, Object> properties = httpServiceConfiguration.getProperties();
+
+ String host;
+ Object hostObj = properties.get("org.apache.felix.http.host");
+ if (hostObj == null) {
+ host = "localhost";
+ } else {
+ assertTrue(hostObj instanceof String);
+ host = (String)hostObj;
+ }
+ assertNotNull(host);
+
+ String scheme = null;
+ Object portObj = null;
+ Object httpsEnableObj = properties.get("org.apache.felix.https.enable");
+ if ("true".equals(httpsEnableObj)) {
+ scheme = "https";
+ portObj = properties.get("org.osgi.service.http.port.secure");
+ } else {
+ Object httpEnableObj = properties.get("org.apache.felix.http.enable");
+ if (httpEnableObj == null || "true".equals(httpEnableObj)) {
+ scheme = "http";
+ portObj = properties.get("org.osgi.service.http.port");
+ } else {
+ fail("Expected either http or https to be enabled");
+ }
+ }
+ int port = -1;
+ if (portObj instanceof Number) {
+ port = ((Number)portObj).intValue();
+ }
+ assertTrue(port > 0);
+
+ return new URI(String.format("%s://%s:%d", scheme, host, port));
+ }
+
+ protected void assertPrivilege(Collection<String> privileges, boolean expected, String privilegeName) {
+ if(expected != privileges.contains(privilegeName)) {
+ fail("Expected privilege " + privilegeName + " to be "
+ + (expected ? "included" : "NOT INCLUDED")
+ + " in supplied list: " + privileges + ")");
+ }
+ }
+
+ protected Object doAuthenticatedWork(Credentials creds, AuthenticatedWorker worker) throws IOException {
+ Object result = null;
+ AuthScope authScope = new AuthScope(baseServerUri.getHost(), baseServerUri.getPort(), baseServerUri.getScheme());
+ CredentialsProvider oldCredentialsProvider = httpContext.getCredentialsProvider();
+ try {
+ BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+ httpContext.setCredentialsProvider(credentialsProvider);
+ credentialsProvider.setCredentials(authScope, creds);
+
+ result = worker.doWork();
+ } finally {
+ httpContext.setCredentialsProvider(oldCredentialsProvider);
+ }
+ return result;
+ }
+
+ protected void assertAuthenticatedPostStatus(Credentials creds, String url, int expectedStatusCode, List<NameValuePair> postParams, String assertMessage) throws IOException {
+ doAuthenticatedWork(creds, () -> {
+ HttpPost postRequest = new HttpPost(url);
+ postRequest.setEntity(new UrlEncodedFormEntity(postParams));
+ try (CloseableHttpResponse response = httpClient.execute(postRequest, httpContext)) {
+ assertEquals(assertMessage, expectedStatusCode, response.getStatusLine().getStatusCode());
+ }
+ return null;
+ });
+ }
+
+ protected void assertAuthenticatedHttpStatus(Credentials creds, String urlString, int expectedStatusCode, String assertMessage) throws IOException {
+ doAuthenticatedWork(creds, () -> {
+ HttpGet getRequest = new HttpGet(urlString);
+ try (CloseableHttpResponse response = httpClient.execute(getRequest, httpContext)) {
+ assertEquals(assertMessage, expectedStatusCode, response.getStatusLine().getStatusCode());
+ return null;
+ }
+ });
+ }
+
+ protected String getAuthenticatedContent(Credentials creds, String url, String expectedContentType, int expectedStatusCode) throws IOException {
+ return (String)doAuthenticatedWork(creds, () -> {
+ HttpGet getRequest = new HttpGet(url);
+ try (CloseableHttpResponse response = httpClient.execute(getRequest, httpContext)) {
+ assertEquals(expectedStatusCode, response.getStatusLine().getStatusCode());
+ final Header h = response.getFirstHeader("Content-Type");
+ if (expectedContentType == null) {
+ if (h != null) {
+ fail("Expected null Content-Type, got " + h.getValue());
+ }
+ } else if (h == null) {
+ fail(
+ "Expected Content-Type that starts with '" + expectedContentType
+ +" but got no Content-Type header at " + url
+ );
+ } else {
+ assertTrue(
+ "Expected Content-Type that starts with '" + expectedContentType
+ + "' for " + url + ", got '" + h.getValue() + "'",
+ h.getValue().startsWith(expectedContentType)
+ );
+ }
+ return EntityUtils.toString(response.getEntity());
+ }
+ });
+ }
+
+ protected String getAuthenticatedPostContent(Credentials creds, String url, String expectedContentType, List<NameValuePair> postParams, int expectedStatusCode) throws IOException {
+ return (String)doAuthenticatedWork(creds, () -> {
+ HttpPost postRequest = new HttpPost(url);
+ postRequest.setEntity(new UrlEncodedFormEntity(postParams));
+ try (CloseableHttpResponse response = httpClient.execute(postRequest, httpContext)) {
+ assertEquals(expectedStatusCode, response.getStatusLine().getStatusCode());
+ final Header h = response.getFirstHeader("Content-Type");
+ if (expectedContentType == null) {
+ if (h != null) {
+ fail("Expected null Content-Type, got " + h.getValue());
+ }
+ } else if (h == null) {
+ fail(
+ "Expected Content-Type that starts with '" + expectedContentType
+ +" but got no Content-Type header at " + url
+ );
+ } else {
+ assertTrue(
+ "Expected Content-Type that starts with '" + expectedContentType
+ + "' for " + url + ", got '" + h.getValue() + "'",
+ h.getValue().startsWith(expectedContentType)
+ );
+ }
+ return EntityUtils.toString(response.getEntity());
+ }
+ });
+ }
+
+ protected String createTestUser() throws IOException {
+ String postUrl = String.format("%s/system/userManager/user.create.html", baseServerUri);
+
+ String userId = "testUser" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":name", userId));
+ postParams.add(new BasicNameValuePair("pwd", "testPwd"));
+ postParams.add(new BasicNameValuePair("pwdConfirm", "testPwd"));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ final String msg = "Unexpected status while attempting to create test user at " + postUrl;
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, msg);
+
+ final String sessionInfoUrl = String.format("%s/system/sling/info.sessionInfo.json", baseServerUri);
+ assertAuthenticatedHttpStatus(creds, sessionInfoUrl, HttpServletResponse.SC_OK,
+ "session info failed for user " + userId);
+
+ return userId;
+ }
+
+ protected String createTestGroup() throws IOException {
+ String postUrl = String.format("%s/system/userManager/group.create.html", baseServerUri);
+
+ String groupId = "testGroup" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":name", groupId));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ final String msg = "Unexpected status while attempting to create test group at " + postUrl;
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, msg);
+
+ return groupId;
+ }
+
+ protected String createTestFolder() throws IOException {
+ return createTestFolder(null, "sling-tests");
+ }
+
+ protected String createTestFolder(String parentPath, String nameHint) throws IOException {
+ return createTestFolder(parentPath, nameHint, TEST_FOLDER_JSON);
+ }
+ protected String createTestFolder(String parentPath, String nameHint, String jsonImport) throws IOException {
+ JsonObject json = importJSON(parentPath, nameHint, jsonImport);
+ return String.format("%s%s", baseServerUri, json.getString("path"));
+ }
+
+ protected JsonObject importJSON(String nameHint, String jsonImport) throws IOException {
+ return importJSON(null, nameHint, jsonImport);
+ }
+
+ protected JsonObject importJSON(String parentPath, String nameHint, String jsonImport) throws IOException {
+ JsonObject result = null;
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ result = (JsonObject)doAuthenticatedWork(creds, () -> {
+ List<NameValuePair> parameters = new ArrayList<>();
+ parameters.add(new BasicNameValuePair(":operation", "import"));
+ if (nameHint != null) {
+ parameters.add(new BasicNameValuePair(":nameHint", nameHint));
+ }
+ parameters.add(new BasicNameValuePair(":content", jsonImport));
+ parameters.add(new BasicNameValuePair(":contentType", "json"));
+ parameters.add(new BasicNameValuePair(":replaceProperties", "true"));
+
+ String postUrl = String.format("%s%s", baseServerUri, parentPath != null ? parentPath : "/content");
+ HttpPost postRequest = new HttpPost(postUrl);
+ postRequest.setEntity(new UrlEncodedFormEntity(parameters));
+ postRequest.addHeader(new BasicHeader("Accept", "application/json,*/*;q=0.9"));
+ JsonObject jsonObj = null;
+ try (CloseableHttpResponse response = httpClient.execute(postRequest, httpContext)) {
+ assertEquals(HttpServletResponse.SC_CREATED, response.getStatusLine().getStatusCode());
+ jsonObj = parseJson(EntityUtils.toString(response.getEntity()));
+ }
+ return jsonObj;
+ });
+ return result;
+ }
+
+ /**
+ * @param json the json string to parse
+ * @return the parsed JsonObject
+ */
+ protected JsonObject parseJson(String json) {
+ JsonObject jsonObj = null;
+ try (JsonReader reader = Json.createReader(new StringReader(json))) {
+ jsonObj = reader.readObject();
+ }
+ return jsonObj;
+ }
+
+ protected static interface AuthenticatedWorker {
+ public Object doWork() throws IOException;
+ }
+}
diff --git a/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/AccessManagerTestSupport.java b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/AccessManagerTestSupport.java
new file mode 100644
index 0000000..b29c1c2
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/AccessManagerTestSupport.java
@@ -0,0 +1,276 @@
+/*
+ * 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.jcr.jackrabbit.accessmanager.it;
+
+import static org.apache.felix.hc.api.FormattingResultLog.msHumanReadable;
+import static org.apache.sling.testing.paxexam.SlingOptions.awaitility;
+import static org.apache.sling.testing.paxexam.SlingOptions.slingQuickstartOakTar;
+import static org.apache.sling.testing.paxexam.SlingOptions.versionResolver;
+import static org.awaitility.Awaitility.await;
+import static org.junit.Assert.assertNotNull;
+import static org.ops4j.pax.exam.CoreOptions.composite;
+import static org.ops4j.pax.exam.CoreOptions.junitBundles;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.streamBundle;
+import static org.ops4j.pax.exam.CoreOptions.vmOption;
+import static org.ops4j.pax.exam.CoreOptions.when;
+import static org.ops4j.pax.tinybundles.core.TinyBundles.withBnd;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+
+import org.apache.felix.hc.api.Result;
+import org.apache.felix.hc.api.ResultLog;
+import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
+import org.apache.felix.hc.api.execution.HealthCheckExecutor;
+import org.apache.felix.hc.api.execution.HealthCheckSelector;
+import org.apache.sling.testing.paxexam.TestSupport;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.options.ModifiableCompositeOption;
+import org.ops4j.pax.exam.options.extra.VMOption;
+import org.ops4j.pax.tinybundles.core.TinyBundle;
+import org.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.Constants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AccessManagerTestSupport extends TestSupport {
+ private static final String BUNDLE_SYMBOLICNAME = "TEST-CONTENT-BUNDLE";
+ private static final String SLING_BUNDLE_RESOURCES_HEADER = "Sling-Bundle-Resources";
+
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ @Inject
+ private HealthCheckExecutor hcExecutor;
+
+
+ @Configuration
+ public Option[] configuration() throws IOException {
+ final String vmOpt = System.getProperty("pax.vm.options");
+ VMOption vmOption = null;
+ if (vmOpt != null && !vmOpt.isEmpty()) {
+ vmOption = new VMOption(vmOpt);
+ }
+
+ final String jacocoOpt = System.getProperty("jacoco.command");
+ VMOption jacocoCommand = null;
+ if (jacocoOpt != null && !jacocoOpt.isEmpty()) {
+ jacocoCommand = new VMOption(jacocoOpt);
+ }
+
+ // newer version of sling.api and dependencies for SLING-10034
+ // may remove at a later date if the superclass includes these versions or later
+ versionResolver.setVersionFromProject("org.apache.sling", "org.apache.sling.api");
+ versionResolver.setVersion("org.apache.sling", "org.apache.sling.resourceresolver", "1.7.0"); // to be compatible with current o.a.sling.api
+ versionResolver.setVersion("org.apache.sling", "org.apache.sling.scripting.core", "2.3.4"); // to be compatible with current o.a.sling.api
+ versionResolver.setVersion("org.apache.sling", "org.apache.sling.scripting.api", "2.2.0"); // to be compatible with current o.a.sling.api
+ versionResolver.setVersion("org.apache.sling", "org.apache.sling.servlets.resolver", "2.7.12"); // to be compatible with current o.a.sling.api
+ versionResolver.setVersion("org.apache.sling", "org.apache.sling.commons.compiler", "2.4.0"); // to be compatible with current o.a.sling.scripting.core
+
+ return options(
+ composite(
+ super.baseConfiguration(),
+ when(vmOption != null).useOptions(vmOption),
+ when(jacocoCommand != null).useOptions(jacocoCommand),
+ optionalRemoteDebug(),
+ slingQuickstart(),
+ testBundle("bundle.filename"),
+ junitBundles(),
+ awaitility()
+ ).add(
+ // needed by latest version of org.apache.sling.api
+ mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.converter").version("1.0.14")
+ ).add(
+ additionalOptions()
+ ).remove(
+ // remove our bundle under test to avoid duplication
+ mavenBundle().groupId("org.apache.sling").artifactId("org.apache.sling.jcr.jackrabbit.accessmanager").version(versionResolver)
+ )
+ );
+ }
+
+ protected Option[] additionalOptions() throws IOException { // NOSONAR
+ return new Option[]{};
+ }
+
+ protected Option slingQuickstart() {
+ final String workingDirectory = workingDirectory();
+ final int httpPort = findFreePort();
+ return composite(
+ slingQuickstartOakTar(workingDirectory, httpPort)
+ );
+ }
+
+ public String getTestFileUrl(String path) {
+ return getClass().getResource(path).toExternalForm();
+ }
+
+ /**
+ * Optionally configure remote debugging on the port supplied by the "debugPort"
+ * system property.
+ */
+ protected ModifiableCompositeOption optionalRemoteDebug() {
+ VMOption option = null;
+ String property = System.getProperty("debugPort");
+ if (property != null) {
+ option = vmOption(String.format("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=%s", property));
+ }
+ return composite(option);
+ }
+
+ /**
+ * Wait for the health check to be ok
+ *
+ * @param timeoutMsec the max time to wait for the health check to be ok
+ * @param nextIterationDelay the sleep time between the check attempts
+ */
+ protected void waitForServerReady(long timeoutMsec, long nextIterationDelay) {
+ // retry until the exec call returns true and doesn't throw any exception
+ await().atMost(timeoutMsec, TimeUnit.MILLISECONDS)
+ .pollInterval(nextIterationDelay, TimeUnit.MILLISECONDS)
+ .until(this::doHealthCheck);
+ }
+
+ /**
+ * @return true if health checks are ok
+ */
+ protected boolean doHealthCheck() throws IOException {
+ boolean isOk = true;
+ logger.info("Performing health check");
+ HealthCheckSelector hcs = HealthCheckSelector.tags("systemalive");
+ List<HealthCheckExecutionResult> results = hcExecutor.execute(hcs);
+ logger.info("systemalive health check got {} results", results.size());
+ isOk &= !results.isEmpty();
+ for (final HealthCheckExecutionResult exR : results) {
+ final Result r = exR.getHealthCheckResult();
+ if (logger.isInfoEnabled()) {
+ logger.info("systemalive health check: {}", toHealthCheckResultInfo(exR, false));
+ }
+ isOk &= r.isOk();
+ if (!isOk) {
+ break; // found a failure so stop checking further
+ }
+ }
+
+ if (isOk) {
+ hcs = HealthCheckSelector.tags("bundles");
+ results = hcExecutor.execute(hcs);
+ logger.info("bundles health check got {} results", results.size());
+ isOk &= !results.isEmpty();
+ for (final HealthCheckExecutionResult exR : results) {
+ final Result r = exR.getHealthCheckResult();
+ if (logger.isInfoEnabled()) {
+ logger.info("bundles health check: {}", toHealthCheckResultInfo(exR, false));
+ }
+ isOk &= r.isOk();
+ if (!isOk) {
+ break; // found a failure so stop checking further
+ }
+ }
+ }
+ return isOk;
+ }
+
+ /**
+ * Produce a human readable report of the health check results that is suitable for
+ * debugging or writing to a log
+ */
+ protected String toHealthCheckResultInfo(final HealthCheckExecutionResult exResult, final boolean debug) throws IOException {
+ String value = null;
+ try (StringWriter resultWriter = new StringWriter(); BufferedWriter writer = new BufferedWriter(resultWriter)) {
+ final Result result = exResult.getHealthCheckResult();
+
+ writer.append('"').append(exResult.getHealthCheckMetadata().getTitle()).append('"');
+ writer.append(" result is: ").append(result.getStatus().toString());
+ writer.newLine();
+ writer.append(" Finished: ").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(exResult.getFinishedAt()) + " after "
+ + msHumanReadable(exResult.getElapsedTimeInMs()));
+
+ for (final ResultLog.Entry e : result) {
+ if (!debug && e.isDebug()) {
+ continue;
+ }
+ writer.newLine();
+ writer.append(" ");
+ writer.append(e.getStatus().toString());
+ writer.append(' ');
+ writer.append(e.getMessage());
+ if (e.getException() != null) {
+ writer.append(" ");
+ writer.append(e.getException().toString());
+ }
+ }
+ writer.flush();
+ value = resultWriter.toString();
+ }
+ return value;
+ }
+
+ /**
+ * Add content to our test bundle
+ */
+ protected void addContent(final TinyBundle bundle, String resourcePath) throws IOException {
+ String pathInBundle = resourcePath;
+ resourcePath = "/content" + resourcePath;
+ try (final InputStream is = getClass().getResourceAsStream(resourcePath)) {
+ assertNotNull("Expecting resource to be found:" + resourcePath, is);
+ logger.info("Adding resource to bundle, path={}, resource={}", pathInBundle, resourcePath);
+ bundle.add(pathInBundle, is);
+ }
+ }
+
+ /**
+ * Override to provide the option for your test
+ *
+ * @return the tinybundle Option or null if none
+ */
+ protected Option buildBundleResourcesBundle() throws IOException {
+ return null;
+ }
+
+ /**
+ * Build a test bundle containing the specified bundle resources
+ *
+ * @param header the value for the {@link #SLING_BUNDLE_RESOURCES_HEADER} header
+ * @param content the collection of files to embed in the tinybundle
+ * @return the tinybundle Option
+ */
+ protected Option buildBundleResourcesBundle(final String header, final Collection<String> content) throws IOException {
+ final TinyBundle bundle = TinyBundles.bundle();
+ bundle.set(Constants.BUNDLE_SYMBOLICNAME, BUNDLE_SYMBOLICNAME);
+ bundle.set(SLING_BUNDLE_RESOURCES_HEADER, header);
+ bundle.set("Require-Capability", "osgi.extender;filter:=\"(&(osgi.extender=org.apache.sling.bundleresource)(version<=1.1.0)(!(version>=2.0.0)))\"");
+
+ for (final String entry : content) {
+ addContent(bundle, entry);
+ }
+ return streamBundle(
+ bundle.build(withBnd())
+ ).start();
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/AccessPrivilegesInfoIT.java b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/AccessPrivilegesInfoIT.java
new file mode 100644
index 0000000..337c1eb
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/AccessPrivilegesInfoIT.java
@@ -0,0 +1,475 @@
+/*
+ * 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.jcr.jackrabbit.accessmanager.it;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.json.JsonArray;
+import javax.json.JsonException;
+import javax.json.JsonObject;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class AccessPrivilegesInfoIT extends AccessManagerClientTestSupport {
+
+ @Override
+ protected Option buildBundleResourcesBundle() throws IOException {
+ final List<String> resourcePaths = Arrays.asList("/apps/nt/unstructured/privileges-info.json.esp");
+ final String bundleResourcesHeader = String.join(",", resourcePaths);
+ return buildBundleResourcesBundle(bundleResourcesHeader, resourcePaths);
+ }
+
+ /*
+ * testuser granted read / denied write
+ */
+ @Test
+ public void testDeniedWriteForUser() throws IOException, JsonException {
+ testUserId = createTestUser();
+ testFolderUrl = createTestFolder();
+
+ //assign some privileges
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:readAccessControl", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "denied"));
+
+ Credentials adminCreds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(adminCreds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ String getUrl = testFolderUrl + ".privileges-info.json";
+
+ //fetch the JSON for the test page to verify the settings.
+ Credentials testUserCreds = new UsernamePasswordCredentials(testUserId, "testPwd");
+
+ String json = getAuthenticatedContent(testUserCreds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+
+ assertEquals(false, jsonObj.getBoolean("canAddChildren"));
+ assertEquals(false, jsonObj.getBoolean("canDeleteChildren"));
+ assertEquals(false, jsonObj.getBoolean("canDelete"));
+ assertEquals(false, jsonObj.getBoolean("canModifyProperties"));
+ assertEquals(true, jsonObj.getBoolean("canReadAccessControl"));
+ assertEquals(false, jsonObj.getBoolean("canModifyAccessControl"));
+ }
+
+ /*
+ * testuser granted read / granted write
+ */
+ @Test
+ public void testGrantedWriteForUser() throws IOException, JsonException {
+ testUserId = createTestUser();
+ testFolderUrl = createTestFolder();
+
+ //assign some privileges
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:readAccessControl", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:modifyAccessControl", "granted"));
+
+ Credentials adminCreds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(adminCreds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ String getUrl = testFolderUrl + ".privileges-info.json";
+
+ //fetch the JSON for the test page to verify the settings.
+ Credentials testUserCreds = new UsernamePasswordCredentials(testUserId, "testPwd");
+
+ String json = getAuthenticatedContent(testUserCreds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+ JsonObject jsonObj = parseJson(json);
+
+ assertEquals(true, jsonObj.getBoolean("canAddChildren"));
+ assertEquals(true, jsonObj.getBoolean("canDeleteChildren"));
+ //the parent node must also have jcr:removeChildren granted for 'canDelete' to be true
+ assertEquals(false, jsonObj.getBoolean("canDelete"));
+ assertEquals(true, jsonObj.getBoolean("canModifyProperties"));
+ assertEquals(true, jsonObj.getBoolean("canReadAccessControl"));
+ assertEquals(true, jsonObj.getBoolean("canModifyAccessControl"));
+
+ //add a child node to verify the 'canDelete' use case
+ String parentPath = testFolderUrl.substring(baseServerUri.toString().length());
+ String childFolderUrl = createTestFolder(parentPath, "testFolder");
+ String childPostUrl = childFolderUrl + ".modifyAce.html";
+
+ postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:removeNode", "granted"));
+ assertAuthenticatedPostStatus(adminCreds, childPostUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ String childGetUrl = childFolderUrl + ".privileges-info.json";
+ String childJson = getAuthenticatedContent(testUserCreds, childGetUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(childJson);
+ JsonObject childJsonObj = parseJson(childJson);
+ assertEquals(true, childJsonObj.getBoolean("canDelete"));
+ }
+
+ /*
+ * group testuser granted read / denied write
+ */
+ @Test
+ public void testDeniedWriteForGroup() throws IOException, JsonException {
+ testGroupId = createTestGroup();
+ testUserId = createTestUser();
+ testFolderUrl = createTestFolder();
+
+ Credentials adminCreds = new UsernamePasswordCredentials("admin", "admin");
+
+ //add testUserId to testGroup
+ String groupPostUrl = String.format("%s/system/userManager/group/%s.update.html", baseServerUri, testGroupId);
+ List<NameValuePair> groupPostParams = new ArrayList<>();
+ groupPostParams.add(new BasicNameValuePair(":member", testUserId));
+ assertAuthenticatedPostStatus(adminCreds, groupPostUrl, HttpServletResponse.SC_OK, groupPostParams, null);
+
+ //assign some privileges
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testGroupId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:readAccessControl", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "denied"));
+
+ assertAuthenticatedPostStatus(adminCreds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ String getUrl = testFolderUrl + ".privileges-info.json";
+
+ //fetch the JSON for the test page to verify the settings.
+ Credentials testUserCreds = new UsernamePasswordCredentials(testUserId, "testPwd");
+
+ String json = getAuthenticatedContent(testUserCreds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+ JsonObject jsonObj = parseJson(json);
+
+ assertEquals(false, jsonObj.getBoolean("canAddChildren"));
+ assertEquals(false, jsonObj.getBoolean("canDeleteChildren"));
+ assertEquals(false, jsonObj.getBoolean("canDelete"));
+ assertEquals(false, jsonObj.getBoolean("canModifyProperties"));
+ assertEquals(true, jsonObj.getBoolean("canReadAccessControl"));
+ assertEquals(false, jsonObj.getBoolean("canModifyAccessControl"));
+ }
+
+ /*
+ * group testuser granted read / granted write
+ */
+ @Test
+ public void testGrantedWriteForGroup() throws IOException, JsonException {
+ testGroupId = createTestGroup();
+ testUserId = createTestUser();
+ testFolderUrl = createTestFolder();
+
+ Credentials adminCreds = new UsernamePasswordCredentials("admin", "admin");
+
+ //add testUserId to testGroup
+ String groupPostUrl = String.format("%s/system/userManager/group/%s.update.html", baseServerUri, testGroupId);
+ List<NameValuePair> groupPostParams = new ArrayList<>();
+ groupPostParams.add(new BasicNameValuePair(":member", testUserId));
+ assertAuthenticatedPostStatus(adminCreds, groupPostUrl, HttpServletResponse.SC_OK, groupPostParams, null);
+
+ //assign some privileges
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testGroupId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:readAccessControl", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:modifyAccessControl", "granted"));
+
+ assertAuthenticatedPostStatus(adminCreds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ String getUrl = testFolderUrl + ".privileges-info.json";
+
+ //fetch the JSON for the test page to verify the settings.
+ Credentials testUserCreds = new UsernamePasswordCredentials(testUserId, "testPwd");
+
+ String json = getAuthenticatedContent(testUserCreds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+ JsonObject jsonObj = parseJson(json);
+
+ assertEquals(true, jsonObj.getBoolean("canAddChildren"));
+ assertEquals(true, jsonObj.getBoolean("canDeleteChildren"));
+ //the parent node must also have jcr:removeChildren granted for 'canDelete' to be true
+ assertEquals(false, jsonObj.getBoolean("canDelete"));
+ assertEquals(true, jsonObj.getBoolean("canModifyProperties"));
+ assertEquals(true, jsonObj.getBoolean("canReadAccessControl"));
+ assertEquals(true, jsonObj.getBoolean("canModifyAccessControl"));
+
+ //add a child node to verify the 'canDelete' use case
+ String parentPath = testFolderUrl.substring(baseServerUri.toString().length());
+ String childFolderUrl = createTestFolder(parentPath, "testFolder");
+ String childPostUrl = childFolderUrl + ".modifyAce.html";
+
+ postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testGroupId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:removeNode", "granted"));
+ assertAuthenticatedPostStatus(adminCreds, childPostUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ String childGetUrl = childFolderUrl + ".privileges-info.json";
+ String childJson = getAuthenticatedContent(testUserCreds, childGetUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(childJson);
+ JsonObject childJsonObj = parseJson(childJson);
+ assertEquals(true, childJsonObj.getBoolean("canDelete"));
+ }
+
+
+ /**
+ * Test the fix for SLING-1090
+ */
+ @Test
+ public void testSLING1090() throws IOException {
+ testUserId = createTestUser();
+
+ //grant jcr: removeChildNodes to the root node
+ ArrayList<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:removeChildNodes", "granted"));
+ Credentials adminCreds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(adminCreds, String.format("%s/.modifyAce.html", baseServerUri), HttpServletResponse.SC_OK, postParams, null);
+
+ //create a node as a child of the root folder
+ testFolderUrl = createTestFolder("/", "testSLING1090");
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ //grant jcr:removeNode to the test node
+ postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:removeNode", "granted"));
+ assertAuthenticatedPostStatus(adminCreds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ //fetch the JSON for the test page to verify the settings.
+ String getUrl = testFolderUrl + ".privileges-info.json";
+ Credentials testUserCreds = new UsernamePasswordCredentials(testUserId, "testPwd");
+ String json = getAuthenticatedContent(testUserCreds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+ JsonObject jsonObj = parseJson(json);
+ assertEquals(true, jsonObj.getBoolean("canDelete"));
+ }
+
+ /**
+ * Test for SLING-7835, PrivilegesInfo#getDeclaredAccessRights returns incorrect information
+ */
+ @Test
+ public void testDeclaredAclForUser() throws IOException, JsonException {
+ testUserId = createTestUser();
+ testUserId2 = createTestUser();
+
+ testFolderUrl = createTestFolder(null, "sling-tests",
+ "{ \"jcr:primaryType\": \"nt:unstructured\", \"propOne\" : \"propOneValue\", \"child\" : { \"childPropOne\" : true } }");
+
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ //1. create an initial set of privileges
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "granted"));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId2));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "granted"));
+
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId2));
+ postParams.add(new BasicNameValuePair("privilege@jcr:lockManagement", "granted"));
+
+ postUrl = testFolderUrl + "/child.modifyAce.html";
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+
+ //fetch the JSON for the eacl to verify the settings.
+ String getUrl = testFolderUrl + "/child.privileges-info.json";
+ Credentials testUserCreds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedContent(testUserCreds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+ JsonObject jsonObject = parseJson(json);
+ jsonObject = jsonObject.getJsonObject("declaredAccessRights");
+
+ assertNull(jsonObject.get(testUserId));
+
+ JsonObject aceObject2 = jsonObject.getJsonObject(testUserId2);
+ assertNotNull(aceObject2);
+
+ JsonArray grantedArray2 = aceObject2.getJsonArray("granted");
+ assertNotNull(grantedArray2);
+ assertEquals(1, grantedArray2.size());
+ Set<String> grantedPrivilegeNames2 = new HashSet<>();
+ for (int i=0; i < grantedArray2.size(); i++) {
+ grantedPrivilegeNames2.add(grantedArray2.getString(i));
+ }
+ assertPrivilege(grantedPrivilegeNames2, true, "jcr:lockManagement");
+
+ JsonArray deniedArray2 = aceObject2.getJsonArray("denied");
+ assertNotNull(deniedArray2);
+ assertEquals(0, deniedArray2.size());
+
+
+ getUrl = testFolderUrl + ".privileges-info.json";
+ json = getAuthenticatedContent(testUserCreds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+ jsonObject = parseJson(json);
+ jsonObject = jsonObject.getJsonObject("declaredAccessRights");
+
+ JsonObject aceObject = jsonObject.getJsonObject(testUserId);
+ assertNotNull(aceObject);
+
+ JsonArray grantedArray = aceObject.getJsonArray("granted");
+ assertNotNull(grantedArray);
+ assertEquals(1, grantedArray.size());
+ Set<String> grantedPrivilegeNames = new HashSet<>();
+ for (int i=0; i < grantedArray.size(); i++) {
+ grantedPrivilegeNames.add(grantedArray.getString(i));
+ }
+ assertPrivilege(grantedPrivilegeNames,true, PrivilegeConstants.JCR_WRITE);
+
+ JsonArray deniedArray = aceObject.getJsonArray("denied");
+ assertNotNull(deniedArray);
+ assertEquals(0, deniedArray.size());
+
+ aceObject2 = jsonObject.getJsonObject(testUserId2);
+ assertNotNull(aceObject2);
+
+ grantedArray2 = aceObject2.getJsonArray("granted");
+ assertNotNull(grantedArray2);
+ assertEquals(1, grantedArray2.size());
+ grantedPrivilegeNames2 = new HashSet<>();
+ for (int i=0; i < grantedArray2.size(); i++) {
+ grantedPrivilegeNames2.add(grantedArray2.getString(i));
+ }
+ assertPrivilege(grantedPrivilegeNames2, true, PrivilegeConstants.JCR_WRITE);
+
+ deniedArray2 = aceObject2.getJsonArray("denied");
+ assertNotNull(deniedArray2);
+ assertEquals(0, deniedArray2.size());
+ }
+
+ /**
+ * Test for SLING-7835, PrivilegesInfo#getEffectiveAccessRights returns incorrect information
+ */
+ @Test
+ public void testEffectiveAclForUser() throws IOException, JsonException {
+ testUserId = createTestUser();
+ testUserId2 = createTestUser();
+
+ testFolderUrl = createTestFolder(null, "sling-tests",
+ "{ \"jcr:primaryType\": \"nt:unstructured\", \"propOne\" : \"propOneValue\", \"child\" : { \"childPropOne\" : true } }");
+
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ //1. create an initial set of privileges
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "granted"));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId2));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "granted"));
+
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId2));
+ postParams.add(new BasicNameValuePair("privilege@jcr:lockManagement", "granted"));
+
+ postUrl = testFolderUrl + "/child.modifyAce.html";
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+
+ //fetch the JSON for the eacl to verify the settings.
+ String getUrl = testFolderUrl + "/child.privileges-info.json";
+ Credentials testUserCreds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedContent(testUserCreds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+ JsonObject jsonObject = parseJson(json);
+ jsonObject = jsonObject.getJsonObject("effectiveAccessRights");
+
+ JsonObject aceObject = jsonObject.getJsonObject(testUserId);
+ assertNotNull(aceObject);
+
+ JsonArray grantedArray = aceObject.getJsonArray("granted");
+ assertNotNull(grantedArray);
+ assertEquals(1, grantedArray.size());
+ Set<String> grantedPrivilegeNames = new HashSet<>();
+ for (int i=0; i < grantedArray.size(); i++) {
+ grantedPrivilegeNames.add(grantedArray.getString(i));
+ }
+ assertPrivilege(grantedPrivilegeNames,true, PrivilegeConstants.JCR_WRITE);
+
+ JsonArray deniedArray = aceObject.getJsonArray("denied");
+ assertNotNull(deniedArray);
+ assertEquals(0, deniedArray.size());
+
+ JsonObject aceObject2 = jsonObject.getJsonObject(testUserId2);
+ assertNotNull(aceObject2);
+
+ JsonArray grantedArray2 = aceObject2.getJsonArray("granted");
+ assertNotNull(grantedArray2);
+ assertEquals(2, grantedArray2.size());
+ Set<String> grantedPrivilegeNames2 = new HashSet<>();
+ for (int i=0; i < grantedArray2.size(); i++) {
+ grantedPrivilegeNames2.add(grantedArray2.getString(i));
+ }
+ assertPrivilege(grantedPrivilegeNames2, true, PrivilegeConstants.JCR_WRITE);
+ assertPrivilege(grantedPrivilegeNames2, true, "jcr:lockManagement");
+
+ JsonArray deniedArray2 = aceObject2.getJsonArray("denied");
+ assertNotNull(deniedArray2);
+ assertEquals(0, deniedArray2.size());
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/CustomPostResponseCreatorImpl.java b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/CustomPostResponseCreatorImpl.java
new file mode 100644
index 0000000..4af6407
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/CustomPostResponseCreatorImpl.java
@@ -0,0 +1,55 @@
+/*
+ * 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.jcr.jackrabbit.accessmanager.it;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.servlets.post.AbstractPostResponse;
+import org.apache.sling.servlets.post.PostResponse;
+import org.apache.sling.servlets.post.PostResponseCreator;
+
+/**
+ * Sample implementation of the PostResponseCreator interface.
+ */
+public class CustomPostResponseCreatorImpl implements PostResponseCreator {
+
+ public PostResponse createPostResponse(SlingHttpServletRequest req) {
+ if ("custom".equals(req.getParameter(":responseType"))) {
+ return new AbstractPostResponse() {
+
+ public void onChange(String type, String... arguments) {
+ // NO-OP
+ }
+
+ @Override
+ protected void doSend(HttpServletResponse response) throws IOException {
+ response.setContentType("text/html");
+ response.setCharacterEncoding("UTF-8");
+
+ response.getWriter().write("Thanks!");
+ response.getWriter().flush();
+ }
+
+ };
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/GetAclIT.java b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/GetAclIT.java
new file mode 100644
index 0000000..29c7d96
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/GetAclIT.java
@@ -0,0 +1,487 @@
+/*
+ * 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.jcr.jackrabbit.accessmanager.it;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.json.JsonArray;
+import javax.json.JsonException;
+import javax.json.JsonObject;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+
+/**
+ * Tests for the 'acl' and 'eacl' Sling Get Operation
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class GetAclIT extends AccessManagerClientTestSupport {
+
+ /**
+ * Test for SLING-2600, Effective ACL servlet returns incorrect information
+ */
+ @Test
+ public void testEffectiveAclForUser() throws IOException, JsonException {
+ testUserId = createTestUser();
+ testUserId2 = createTestUser();
+
+ testFolderUrl = createTestFolder(null, "sling-tests",
+ "{ \"jcr:primaryType\": \"nt:unstructured\", \"propOne\" : \"propOneValue\", \"child\" : { \"childPropOne\" : true } }");
+
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ //1. create an initial set of privileges
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "granted"));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId2));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "granted"));
+
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId2));
+ postParams.add(new BasicNameValuePair("privilege@jcr:lockManagement", "granted"));
+
+ postUrl = testFolderUrl + "/child.modifyAce.html";
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+
+ //fetch the JSON for the eacl to verify the settings.
+ String getUrl = testFolderUrl + "/child.eacl.json";
+
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+ JsonObject jsonObject = parseJson(json);
+
+ JsonObject aceObject = jsonObject.getJsonObject(testUserId);
+ assertNotNull(aceObject);
+
+ String principalString = aceObject.getString("principal");
+ assertEquals(testUserId, principalString);
+
+ JsonArray grantedArray = aceObject.getJsonArray("granted");
+ assertNotNull(grantedArray);
+ assertEquals(1, grantedArray.size());
+ Set<String> grantedPrivilegeNames = new HashSet<>();
+ for (int i=0; i < grantedArray.size(); i++) {
+ grantedPrivilegeNames.add(grantedArray.getString(i));
+ }
+ assertPrivilege(grantedPrivilegeNames,true,PrivilegeConstants.JCR_WRITE);
+
+ Object deniedArray = aceObject.get("denied");
+ assertNull(deniedArray);
+
+ JsonObject aceObject2 = jsonObject.getJsonObject(testUserId2);
+ assertNotNull(aceObject2);
+
+ String principalString2 = aceObject2.getString("principal");
+ assertEquals(testUserId2, principalString2);
+
+ JsonArray grantedArray2 = aceObject2.getJsonArray("granted");
+ assertNotNull(grantedArray2);
+ assertEquals(2, grantedArray2.size());
+ Set<String> grantedPrivilegeNames2 = new HashSet<>();
+ for (int i=0; i < grantedArray2.size(); i++) {
+ grantedPrivilegeNames2.add(grantedArray2.getString(i));
+ }
+ assertPrivilege(grantedPrivilegeNames2, true, PrivilegeConstants.JCR_WRITE);
+ assertPrivilege(grantedPrivilegeNames2, true, PrivilegeConstants.JCR_LOCK_MANAGEMENT);
+
+ Object deniedArray2 = aceObject2.get("denied");
+ assertNull(deniedArray2);
+
+ }
+
+ /**
+ * Test for SLING-2600, Effective ACL servlet returns incorrect information
+ */
+ @Test
+ public void testEffectiveAclMergeForUserReplacePrivilegeOnChild() throws IOException, JsonException {
+ testUserId = createTestUser();
+
+ testFolderUrl = createTestFolder(null, "sling-tests",
+ "{ \"jcr:primaryType\": \"nt:unstructured\", \"propOne\" : \"propOneValue\", \"child\" : { \"childPropOne\" : true } }");
+
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ //1. create an initial set of privileges
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "denied"));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "granted"));
+
+ postUrl = testFolderUrl + "/child.modifyAce.html";
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+
+ //fetch the JSON for the eacl to verify the settings.
+ String getUrl = testFolderUrl + "/child.eacl.json";
+
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+ JsonObject jsonObject = parseJson(json);
+
+ JsonObject aceObject = jsonObject.getJsonObject(testUserId);
+ assertNotNull(aceObject);
+
+ String principalString = aceObject.getString("principal");
+ assertEquals(testUserId, principalString);
+
+ JsonArray grantedArray = aceObject.getJsonArray("granted");
+ assertNotNull(grantedArray);
+ assertEquals(1, grantedArray.size());
+ Set<String> grantedPrivilegeNames = new HashSet<>();
+ for (int i=0; i < grantedArray.size(); i++) {
+ grantedPrivilegeNames.add(grantedArray.getString(i));
+ }
+ assertPrivilege(grantedPrivilegeNames,true,PrivilegeConstants.JCR_WRITE);
+
+ Object deniedArray = aceObject.get("denied");
+ assertNull(deniedArray);
+ }
+
+ /**
+ * Test for SLING-2600, Effective ACL servlet returns incorrect information
+ */
+ @Test
+ public void testEffectiveAclMergeForUserFewerPrivilegesGrantedOnChild() throws IOException, JsonException {
+ testUserId = createTestUser();
+
+ testFolderUrl = createTestFolder(null, "sling-tests",
+ "{ \"jcr:primaryType\": \"nt:unstructured\", \"propOne\" : \"propOneValue\", \"child\" : { \"childPropOne\" : true } }");
+
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ //1. create an initial set of privileges
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:all", "granted"));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "granted"));
+
+ postUrl = testFolderUrl + "/child.modifyAce.html";
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+
+ //fetch the JSON for the eacl to verify the settings.
+ String getUrl = testFolderUrl + "/child.eacl.json";
+
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+ JsonObject jsonObject = parseJson(json);
+
+ JsonObject aceObject = jsonObject.getJsonObject(testUserId);
+ assertNotNull(aceObject);
+
+ String principalString = aceObject.getString("principal");
+ assertEquals(testUserId, principalString);
+
+ JsonArray grantedArray = aceObject.getJsonArray("granted");
+ assertNotNull(grantedArray);
+ assertEquals(1, grantedArray.size());
+ Set<String> grantedPrivilegeNames = new HashSet<>();
+ for (int i=0; i < grantedArray.size(); i++) {
+ grantedPrivilegeNames.add(grantedArray.getString(i));
+ }
+ assertPrivilege(grantedPrivilegeNames, true, PrivilegeConstants.JCR_ALL);
+
+ Object deniedArray = aceObject.get("denied");
+ assertNull(deniedArray);
+ }
+
+ /**
+ * Test for SLING-2600, Effective ACL servlet returns incorrect information
+ */
+ @Test
+ public void testEffectiveAclMergeForUserMorePrivilegesGrantedOnChild() throws IOException, JsonException {
+ testUserId = createTestUser();
+
+ testFolderUrl = createTestFolder(null, "sling-tests",
+ "{ \"jcr:primaryType\": \"nt:unstructured\", \"propOne\" : \"propOneValue\", \"child\" : { \"childPropOne\" : true } }");
+
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ //1. create an initial set of privileges
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "granted"));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:all", "granted"));
+
+ postUrl = testFolderUrl + "/child.modifyAce.html";
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+
+ //fetch the JSON for the eacl to verify the settings.
+ String getUrl = testFolderUrl + "/child.eacl.json";
+
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+ JsonObject jsonObject = parseJson(json);
+
+ JsonObject aceObject = jsonObject.getJsonObject(testUserId);
+ assertNotNull(aceObject);
+
+ String principalString = aceObject.getString("principal");
+ assertEquals(testUserId, principalString);
+
+ JsonArray grantedArray = aceObject.getJsonArray("granted");
+ assertNotNull(grantedArray);
+ assertEquals(1, grantedArray.size());
+ Set<String> grantedPrivilegeNames = new HashSet<>();
+ for (int i=0; i < grantedArray.size(); i++) {
+ grantedPrivilegeNames.add(grantedArray.getString(i));
+ }
+ assertPrivilege(grantedPrivilegeNames,true,PrivilegeConstants.JCR_ALL);
+
+ Object deniedArray = aceObject.get("denied");
+ assertNull(deniedArray);
+ }
+
+ /**
+ * Test for SLING-2600, Effective ACL servlet returns incorrect information
+ */
+ @Test
+ public void testEffectiveAclMergeForUserSubsetOfPrivilegesDeniedOnChild2() throws IOException, JsonException {
+ testUserId = createTestUser();
+
+ testFolderUrl = createTestFolder(null, "sling-tests",
+ "{ \"jcr:primaryType\": \"nt:unstructured\", \"propOne\" : \"propOneValue\", \"child\" : { \"childPropOne\" : true } }");
+
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ //1. create an initial set of privileges
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:all", "granted"));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:removeNode", "denied"));
+
+ postUrl = testFolderUrl + "/child.modifyAce.html";
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+
+ //fetch the JSON for the eacl to verify the settings.
+ String getUrl = testFolderUrl + "/child.eacl.json";
+
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+ JsonObject jsonObject = parseJson(json);
+
+ JsonObject aceObject = jsonObject.getJsonObject(testUserId);
+ assertNotNull(aceObject);
+
+ String principalString = aceObject.getString("principal");
+ assertEquals(testUserId, principalString);
+
+ JsonArray grantedArray = aceObject.getJsonArray("granted");
+ assertNotNull(grantedArray);
+ assertTrue(grantedArray.size() >= 11);
+ Set<String> grantedPrivilegeNames = new HashSet<>();
+ for (int i=0; i < grantedArray.size(); i++) {
+ grantedPrivilegeNames.add(grantedArray.getString(i));
+ }
+ assertPrivilege(grantedPrivilegeNames,false,PrivilegeConstants.JCR_ALL);
+ assertPrivilege(grantedPrivilegeNames,false,PrivilegeConstants.JCR_WRITE);
+ assertPrivilege(grantedPrivilegeNames,true,PrivilegeConstants.JCR_READ);
+ assertPrivilege(grantedPrivilegeNames,true,PrivilegeConstants.JCR_READ_ACCESS_CONTROL);
+ assertPrivilege(grantedPrivilegeNames,true,PrivilegeConstants.JCR_MODIFY_ACCESS_CONTROL);
+ assertPrivilege(grantedPrivilegeNames,true,PrivilegeConstants.JCR_LOCK_MANAGEMENT);
+ assertPrivilege(grantedPrivilegeNames,true,PrivilegeConstants.JCR_VERSION_MANAGEMENT);
+ assertPrivilege(grantedPrivilegeNames,true,PrivilegeConstants.JCR_NODE_TYPE_MANAGEMENT);
+ assertPrivilege(grantedPrivilegeNames,true,PrivilegeConstants.JCR_RETENTION_MANAGEMENT);
+ assertPrivilege(grantedPrivilegeNames,true,PrivilegeConstants.JCR_LIFECYCLE_MANAGEMENT);
+ assertPrivilege(grantedPrivilegeNames,true,PrivilegeConstants.JCR_MODIFY_PROPERTIES);
+ assertPrivilege(grantedPrivilegeNames,true,PrivilegeConstants.JCR_ADD_CHILD_NODES);
+ assertPrivilege(grantedPrivilegeNames,true,PrivilegeConstants.JCR_REMOVE_CHILD_NODES);
+
+ JsonArray deniedArray = aceObject.getJsonArray("denied");
+ assertNotNull(deniedArray);
+ assertEquals(1, deniedArray.size());
+ Set<String> deniedPrivilegeNames = new HashSet<>();
+ for (int i=0; i < deniedArray.size(); i++) {
+ deniedPrivilegeNames.add(deniedArray.getString(i));
+ }
+ assertPrivilege(deniedPrivilegeNames, true, PrivilegeConstants.JCR_REMOVE_NODE);
+ }
+
+ /**
+ * Test for SLING-2600, Effective ACL servlet returns incorrect information
+ */
+ @Test
+ public void testEffectiveAclMergeForUserSupersetOfPrivilegesDeniedOnChild() throws IOException, JsonException {
+ testUserId = createTestUser();
+
+ testFolderUrl = createTestFolder(null, "sling-tests",
+ "{ \"jcr:primaryType\": \"nt:unstructured\", \"propOne\" : \"propOneValue\", \"child\" : { \"childPropOne\" : true } }");
+
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ //1. create an initial set of privileges
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "granted"));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:all", "denied"));
+
+ postUrl = testFolderUrl + "/child.modifyAce.html";
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+
+ //fetch the JSON for the eacl to verify the settings.
+ String getUrl = testFolderUrl + "/child.eacl.json";
+
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+ JsonObject jsonObject = parseJson(json);
+
+ JsonObject aceObject = jsonObject.getJsonObject(testUserId);
+ assertNotNull(aceObject);
+
+ String principalString = aceObject.getString("principal");
+ assertEquals(testUserId, principalString);
+
+ Object grantedArray = aceObject.get("granted");
+ assertNull(grantedArray);
+
+ JsonArray deniedArray = aceObject.getJsonArray("denied");
+ assertNotNull(deniedArray);
+ assertEquals(1, deniedArray.size());
+ Set<String> deniedPrivilegeNames = new HashSet<>();
+ for (int i=0; i < deniedArray.size(); i++) {
+ deniedPrivilegeNames.add(deniedArray.getString(i));
+ }
+ assertPrivilege(deniedPrivilegeNames, true, PrivilegeConstants.JCR_ALL);
+ }
+
+ /**
+ * Test for SLING-2600, Effective ACL servlet returns incorrect information
+ */
+ @Test
+ public void testEffectiveAclMergeForUserSupersetOfPrivilegesDeniedOnChild2() throws IOException, JsonException {
+ testUserId = createTestUser();
+
+ testFolderUrl = createTestFolder(null, "sling-tests",
+ "{ \"jcr:primaryType\": \"nt:unstructured\", \"propOne\" : \"propOneValue\", \"child\" : { \"childPropOne\" : true } }");
+
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ //1. create an initial set of privileges
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:modifyProperties", "granted"));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:all", "denied"));
+
+ postUrl = testFolderUrl + "/child.modifyAce.html";
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+
+ //fetch the JSON for the eacl to verify the settings.
+ String getUrl = testFolderUrl + "/child.eacl.json";
+
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+ JsonObject jsonObject = parseJson(json);
+
+ JsonObject aceObject = jsonObject.getJsonObject(testUserId);
+ assertNotNull(aceObject);
+
+ String principalString = aceObject.getString("principal");
+ assertEquals(testUserId, principalString);
+
+ Object grantedArray = aceObject.get("granted");
+ assertNull(grantedArray);
+
+ JsonArray deniedArray = aceObject.getJsonArray("denied");
+ assertNotNull(deniedArray);
+ assertEquals(1, deniedArray.size());
+ Set<String> deniedPrivilegeNames = new HashSet<>();
+ for (int i=0; i < deniedArray.size(); i++) {
+ deniedPrivilegeNames.add(deniedArray.getString(i));
+ }
+ assertPrivilege(deniedPrivilegeNames, true, PrivilegeConstants.JCR_ALL);
+ }
+}
diff --git a/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/ModifyAceIT.java b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/ModifyAceIT.java
new file mode 100644
index 0000000..1398027
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/ModifyAceIT.java
@@ -0,0 +1,1402 @@
+/*
+ * 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.jcr.jackrabbit.accessmanager.it;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.json.JsonArray;
+import javax.json.JsonException;
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+import org.apache.sling.servlets.post.JSONResponse;
+import org.apache.sling.servlets.post.PostResponseCreator;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Tests for the 'modifyAce' Sling Post Operation
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class ModifyAceIT extends AccessManagerClientTestSupport {
+
+ private ServiceRegistration<PostResponseCreator> serviceReg;
+
+ @Before
+ @Override
+ public void before() throws IOException, URISyntaxException {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ Dictionary<String, Object> props = new Hashtable<>(); // NOSONAR
+ serviceReg = bundle.getBundleContext().registerService(PostResponseCreator.class,
+ new CustomPostResponseCreatorImpl(), props);
+
+ super.before();
+ }
+
+ @After
+ @Override
+ public void after() throws IOException {
+ if (serviceReg != null) {
+ serviceReg.unregister();
+ }
+
+ super.after();
+ }
+
+ @Test
+ public void testModifyAceForUser() throws IOException, JsonException {
+ testUserId = createTestUser();
+
+ testFolderUrl = createTestFolder();
+
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "denied"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:modifyAccessControl", "bogus")); //invalid value should be ignored.
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = testFolderUrl + ".acl.json";
+
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+ JsonObject jsonObject = parseJson(json);
+ assertEquals(1, jsonObject.size());
+
+ JsonObject aceObject = jsonObject.getJsonObject(testUserId);
+ assertNotNull(aceObject);
+
+ String principalString = aceObject.getString("principal");
+ assertEquals(testUserId, principalString);
+
+ int order = aceObject.getInt("order");
+ assertEquals(0, order);
+
+ JsonArray grantedArray = aceObject.getJsonArray("granted");
+ assertNotNull(grantedArray);
+ assertEquals(1, grantedArray.size());
+ assertEquals(PrivilegeConstants.JCR_READ, grantedArray.getString(0));
+
+ JsonArray deniedArray = aceObject.getJsonArray("denied");
+ assertNotNull(deniedArray);
+ assertEquals(1, deniedArray.size());
+ assertEquals(PrivilegeConstants.JCR_WRITE, deniedArray.getString(0));
+ }
+
+ /**
+ * Test for SLING-7831
+ */
+ @Test
+ public void testModifyAceCustomPostResponse() throws IOException, JsonException {
+ testUserId = createTestUser();
+
+ testFolderUrl = createTestFolder();
+
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":responseType", "custom"));
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "granted"));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String content = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_HTML, postParams, HttpServletResponse.SC_OK);
+ assertEquals("Thanks!", content); //verify that the content matches the custom response
+ }
+
+ @Test
+ public void testModifyAceForGroup() throws IOException, JsonException {
+ testGroupId = createTestGroup();
+
+ testFolderUrl = createTestFolder();
+
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testGroupId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "denied"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:modifyAccessControl", "bogus")); //invalid value should be ignored.
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = testFolderUrl + ".acl.json";
+
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+ JsonObject jsonObject = parseJson(json);
+ assertEquals(1, jsonObject.size());
+
+ JsonObject aceObject = jsonObject.getJsonObject(testGroupId);
+ assertNotNull(aceObject);
+
+ int order = aceObject.getInt("order");
+ assertEquals(0, order);
+
+ String principalString = aceObject.getString("principal");
+ assertEquals(testGroupId, principalString);
+
+ JsonArray grantedArray = aceObject.getJsonArray("granted");
+ assertNotNull(grantedArray);
+ assertEquals(1, grantedArray.size());
+ assertEquals(PrivilegeConstants.JCR_READ, grantedArray.getString(0));
+
+ JsonArray deniedArray = aceObject.getJsonArray("denied");
+ assertNotNull(deniedArray);
+ assertEquals(PrivilegeConstants.JCR_WRITE, deniedArray.getString(0));
+ }
+
+ /**
+ * Test for SLING-997, preserve privileges that were not posted with the modifyAce
+ * request.
+ */
+ @Test
+ public void testMergeAceForUser() throws IOException, JsonException {
+ testUserId = createTestUser();
+ testFolderUrl = createTestFolder();
+
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ //1. create an initial set of privileges
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:readAccessControl", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:addChildNodes", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:modifyAccessControl", "denied"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:removeChildNodes", "denied"));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = testFolderUrl + ".acl.json";
+
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+ JsonObject jsonObject = parseJson(json);
+ assertEquals(1, jsonObject.size());
+
+ JsonObject aceObject = jsonObject.getJsonObject(testUserId);
+ assertNotNull(aceObject);
+
+ String principalString = aceObject.getString("principal");
+ assertEquals(testUserId, principalString);
+
+ int order = aceObject.getInt("order");
+ assertEquals(0, order);
+
+ JsonArray grantedArray = aceObject.getJsonArray("granted");
+ assertNotNull(grantedArray);
+ assertEquals(3, grantedArray.size());
+ Set<String> grantedPrivilegeNames = new HashSet<>();
+ for (int i=0; i < grantedArray.size(); i++) {
+ grantedPrivilegeNames.add(grantedArray.getString(i));
+ }
+ assertPrivilege(grantedPrivilegeNames, true, PrivilegeConstants.JCR_READ);
+ assertPrivilege(grantedPrivilegeNames, true, PrivilegeConstants.JCR_READ_ACCESS_CONTROL);
+ assertPrivilege(grantedPrivilegeNames, true, PrivilegeConstants.JCR_ADD_CHILD_NODES);
+
+ JsonArray deniedArray = aceObject.getJsonArray("denied");
+ assertNotNull(deniedArray);
+ assertEquals(2, deniedArray.size());
+ Set<String> deniedPrivilegeNames = new HashSet<>();
+ for (int i=0; i < deniedArray.size(); i++) {
+ deniedPrivilegeNames.add(deniedArray.getString(i));
+ }
+ assertPrivilege(deniedPrivilegeNames, true, PrivilegeConstants.JCR_MODIFY_ACCESS_CONTROL);
+ assertPrivilege(deniedPrivilegeNames, true, PrivilegeConstants.JCR_REMOVE_CHILD_NODES);
+
+
+
+ //2. post a new set of privileges to merge with the existing privileges
+ List<NameValuePair> postParams2 = new ArrayList<>();
+ postParams2.add(new BasicNameValuePair("principalId", testUserId));
+ //jcr:read and jcr:addChildNodes are not posted, so they should remain in the granted ACE
+ postParams2.add(new BasicNameValuePair("privilege@jcr:readAccessControl", "none")); //clear the existing privilege
+ postParams2.add(new BasicNameValuePair("privilege@jcr:modifyProperties", "granted")); //add a new privilege
+ //jcr:modifyAccessControl is not posted, so it should remain in the denied ACE
+ postParams2.add(new BasicNameValuePair("privilege@jcr:modifyAccessControl", "denied")); //deny the modifyAccessControl privilege
+ postParams2.add(new BasicNameValuePair("privilege@jcr:removeChildNodes", "none")); //clear the existing privilege
+ postParams2.add(new BasicNameValuePair("privilege@jcr:removeNode", "denied")); //deny a new privilege
+
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams2, null);
+
+
+ //fetch the JSON for the acl to verify the settings.
+ String json2 = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json2);
+ JsonObject jsonObject2 = parseJson(json2);
+ assertEquals(1, jsonObject2.size());
+
+ JsonObject aceObject2 = jsonObject2.getJsonObject(testUserId);
+ assertNotNull(aceObject2);
+
+ String principalString2 = aceObject2.getString("principal");
+ assertEquals(testUserId, principalString2);
+
+ JsonArray grantedArray2 = aceObject2.getJsonArray("granted");
+ assertNotNull(grantedArray2);
+ assertEquals(3, grantedArray2.size());
+ Set<String> grantedPrivilegeNames2 = new HashSet<>();
+ for (int i=0; i < grantedArray2.size(); i++) {
+ grantedPrivilegeNames2.add(grantedArray2.getString(i));
+ }
+ assertPrivilege(grantedPrivilegeNames2, true, PrivilegeConstants.JCR_READ);
+ assertPrivilege(grantedPrivilegeNames2, true, PrivilegeConstants.JCR_ADD_CHILD_NODES);
+ assertPrivilege(grantedPrivilegeNames2, true, PrivilegeConstants.JCR_MODIFY_PROPERTIES);
+
+ JsonArray deniedArray2 = aceObject2.getJsonArray("denied");
+ assertNotNull(deniedArray2);
+ assertEquals(2, deniedArray2.size());
+ Set<String> deniedPrivilegeNames2 = new HashSet<>();
+ for (int i=0; i < deniedArray2.size(); i++) {
+ deniedPrivilegeNames2.add(deniedArray2.getString(i));
+ }
+ assertPrivilege(deniedPrivilegeNames2, true, PrivilegeConstants.JCR_MODIFY_ACCESS_CONTROL);
+ assertPrivilege(deniedPrivilegeNames2, true, PrivilegeConstants.JCR_REMOVE_NODE);
+ }
+
+
+ /**
+ * Test for SLING-997, preserve privileges that were not posted with the modifyAce
+ * request.
+ */
+ @Test
+ public void testMergeAceForUserSplitAggregatePrincipal() throws IOException, JsonException {
+ testUserId = createTestUser();
+ testFolderUrl = createTestFolder();
+
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ //1. create an initial set of privileges
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "denied"));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = testFolderUrl + ".acl.json";
+
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+ JsonObject jsonObject = parseJson(json);
+ assertEquals(1, jsonObject.size());
+
+ JsonObject aceObject = jsonObject.getJsonObject(testUserId);
+ assertNotNull(aceObject);
+
+ assertEquals(testUserId, aceObject.getString("principal"));
+
+ JsonArray grantedArray = aceObject.getJsonArray("granted");
+ assertNotNull(grantedArray);
+ assertEquals(1, grantedArray.size());
+ Set<String> grantedPrivilegeNames = new HashSet<>();
+ for (int i=0; i < grantedArray.size(); i++) {
+ grantedPrivilegeNames.add(grantedArray.getString(i));
+ }
+ assertPrivilege(grantedPrivilegeNames, true, PrivilegeConstants.JCR_READ);
+
+ JsonArray deniedArray = aceObject.getJsonArray("denied");
+ assertNotNull(deniedArray);
+ assertEquals(1, deniedArray.size());
+ Set<String> deniedPrivilegeNames = new HashSet<>();
+ for (int i=0; i < deniedArray.size(); i++) {
+ deniedPrivilegeNames.add(deniedArray.getString(i));
+ }
+ assertPrivilege(deniedPrivilegeNames, true, PrivilegeConstants.JCR_WRITE);
+
+
+
+ //2. post a new set of privileges to merge with the existing privileges
+ List<NameValuePair> postParams2 = new ArrayList<>();
+ postParams2.add(new BasicNameValuePair("principalId", testUserId));
+ //jcr:read is not posted, so it should remain in the granted ACE
+ postParams2.add(new BasicNameValuePair("privilege@jcr:modifyProperties", "granted")); //add a new privilege
+ //jcr:write is not posted, but one of the aggregate privileges is now granted, so the aggregate priviledge should be disagreaged into
+ // the remaining denied privileges in the denied ACE
+
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams2, null);
+
+
+ //fetch the JSON for the acl to verify the settings.
+ String json2 = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json2);
+
+ JsonObject jsonObject2 = parseJson(json2);
+ assertEquals(1, jsonObject2.size());
+
+ JsonObject aceObject2 = jsonObject2.getJsonObject(testUserId);
+ assertNotNull(aceObject2);
+
+ assertEquals(testUserId, aceObject2.getString("principal"));
+
+ JsonArray grantedArray2 = aceObject2.getJsonArray("granted");
+ assertNotNull(grantedArray2);
+ assertEquals(2, grantedArray2.size());
+ Set<String> grantedPrivilegeNames2 = new HashSet<>();
+ for (int i=0; i < grantedArray2.size(); i++) {
+ grantedPrivilegeNames2.add(grantedArray2.getString(i));
+ }
+ assertPrivilege(grantedPrivilegeNames2, true, PrivilegeConstants.JCR_READ);
+ assertPrivilege(grantedPrivilegeNames2, true, PrivilegeConstants.JCR_MODIFY_PROPERTIES);
+
+ JsonArray deniedArray2 = aceObject2.getJsonArray("denied");
+ assertNotNull(deniedArray2);
+ assertEquals(3, deniedArray2.size());
+ Set<String> deniedPrivilegeNames2 = new HashSet<>();
+ for (int i=0; i < deniedArray2.size(); i++) {
+ deniedPrivilegeNames2.add(deniedArray2.getString(i));
+ }
+ assertPrivilege(deniedPrivilegeNames2, false, PrivilegeConstants.JCR_WRITE);
+ //only the remaining privileges from the disaggregated jcr:write collection should remain.
+ assertPrivilege(deniedPrivilegeNames2, true, PrivilegeConstants.JCR_ADD_CHILD_NODES);
+ assertPrivilege(deniedPrivilegeNames2, true, PrivilegeConstants.JCR_REMOVE_NODE);
+ assertPrivilege(deniedPrivilegeNames2, true, PrivilegeConstants.JCR_REMOVE_CHILD_NODES);
+ }
+
+ /**
+ * Test for SLING-997, preserve privileges that were not posted with the modifyAce
+ * request.
+ */
+ @Test
+ public void testMergeAceForUserCombineAggregatePrivilege() throws IOException, JsonException {
+ testUserId = createTestUser();
+ testFolderUrl = createTestFolder();
+
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ //1. create an initial set of privileges
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:removeNode", "denied"));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = testFolderUrl + ".acl.json";
+
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+ JsonObject jsonObject = parseJson(json);
+ assertEquals(1, jsonObject.size());
+
+ JsonObject aceObject = jsonObject.getJsonObject(testUserId);
+ assertNotNull(aceObject);
+
+ assertEquals(testUserId, aceObject.getString("principal"));
+
+ JsonArray grantedArray = aceObject.getJsonArray("granted");
+ assertNotNull(grantedArray);
+ assertEquals(1, grantedArray.size());
+ Set<String> grantedPrivilegeNames = new HashSet<>();
+ for (int i=0; i < grantedArray.size(); i++) {
+ grantedPrivilegeNames.add(grantedArray.getString(i));
+ }
+ assertPrivilege(grantedPrivilegeNames, true, PrivilegeConstants.JCR_READ);
+
+ JsonArray deniedArray = aceObject.getJsonArray("denied");
+ assertNotNull(deniedArray);
+ assertEquals(1, deniedArray.size());
+ Set<String> deniedPrivilegeNames = new HashSet<>();
+ for (int i=0; i < deniedArray.size(); i++) {
+ deniedPrivilegeNames.add(deniedArray.getString(i));
+ }
+ assertPrivilege(deniedPrivilegeNames, true, PrivilegeConstants.JCR_REMOVE_NODE);
+
+
+
+ //2. post a new set of privileges to merge with the existing privileges
+ List<NameValuePair> postParams2 = new ArrayList<>();
+ postParams2.add(new BasicNameValuePair("principalId", testUserId));
+ //jcr:read is not posted, so it should remain in the granted ACE
+
+ //deny the full jcr:write aggregate privilege, which should merge with the
+ //existing part.
+ postParams2.add(new BasicNameValuePair("privilege@jcr:write", "denied")); //add a new privilege
+
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams2, null);
+
+
+ //fetch the JSON for the acl to verify the settings.
+ String json2 = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json2);
+
+ JsonObject jsonObject2 = parseJson(json2);
+ assertEquals(1, jsonObject2.size());
+
+ JsonObject aceObject2 = jsonObject2.getJsonObject(testUserId);
+ assertNotNull(aceObject2);
+
+ assertEquals(testUserId, aceObject.getString("principal"));
+
+ JsonArray grantedArray2 = aceObject2.getJsonArray("granted");
+ assertNotNull(grantedArray2);
+ assertEquals(1, grantedArray2.size());
+ Set<String> grantedPrivilegeNames2 = new HashSet<>();
+ for (int i=0; i < grantedArray2.size(); i++) {
+ grantedPrivilegeNames2.add(grantedArray2.getString(i));
+ }
+ assertPrivilege(grantedPrivilegeNames2, true, PrivilegeConstants.JCR_READ);
+
+ JsonArray deniedArray2 = aceObject2.getJsonArray("denied");
+ assertNotNull(deniedArray2);
+ assertEquals(1, deniedArray2.size());
+ Set<String> deniedPrivilegeNames2 = new HashSet<>();
+ for (int i=0; i < deniedArray2.size(); i++) {
+ deniedPrivilegeNames2.add(deniedArray2.getString(i));
+ }
+ assertPrivilege(deniedPrivilegeNames2, true, PrivilegeConstants.JCR_WRITE);
+ }
+
+
+ /**
+ * Test ACE update with a deny privilege for an ACE that already contains
+ * a grant privilege
+ */
+ @Test
+ public void testMergeAceForUserDenyPrivilegeAfterGrantPrivilege() throws IOException, JsonException {
+ testUserId = createTestUser();
+ testFolderUrl = createTestFolder();
+
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ //1. create an initial set of privileges
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "granted"));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = testFolderUrl + ".acl.json";
+
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+ JsonObject jsonObject = parseJson(json);
+ assertEquals(1, jsonObject.size());
+
+ JsonObject aceObject = jsonObject.getJsonObject(testUserId);
+ assertNotNull(aceObject);
+
+ assertEquals(testUserId, aceObject.getString("principal"));
+
+ JsonArray grantedArray = aceObject.getJsonArray("granted");
+ assertNotNull(grantedArray);
+ assertEquals(1, grantedArray.size());
+ Set<String> grantedPrivilegeNames = new HashSet<>();
+ for (int i=0; i < grantedArray.size(); i++) {
+ grantedPrivilegeNames.add(grantedArray.getString(i));
+ }
+ assertPrivilege(grantedPrivilegeNames, true, PrivilegeConstants.JCR_WRITE);
+
+ assertFalse(aceObject.containsKey("denied"));
+
+
+ //2. post a new set of privileges to merge with the existing privileges
+ List<NameValuePair> postParams2 = new ArrayList<>();
+ postParams2.add(new BasicNameValuePair("principalId", testUserId));
+ //jcr:write is not posted, so it should remain in the granted ACE
+
+ //deny the jcr:nodeTypeManagement privilege, which should merge with the
+ //existing ACE.
+ postParams2.add(new BasicNameValuePair("privilege@jcr:nodeTypeManagement", "denied")); //add a new privilege
+
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams2, null);
+
+
+ //fetch the JSON for the acl to verify the settings.
+ String json2 = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json2);
+
+ JsonObject jsonObject2 = parseJson(json2);
+ assertEquals(1, jsonObject2.size());
+
+ JsonObject aceObject2 = jsonObject2.getJsonObject(testUserId);
+ assertNotNull(aceObject2);
+
+ assertEquals(testUserId, aceObject2.getString("principal"));
+
+ JsonArray grantedArray2 = aceObject2.getJsonArray("granted");
+ assertNotNull(grantedArray2);
+ assertEquals(1, grantedArray2.size());
+ Set<String> grantedPrivilegeNames2 = new HashSet<>();
+ for (int i=0; i < grantedArray2.size(); i++) {
+ grantedPrivilegeNames2.add(grantedArray2.getString(i));
+ }
+ assertPrivilege(grantedPrivilegeNames2, true, PrivilegeConstants.JCR_WRITE);
+
+ JsonArray deniedArray2 = aceObject2.getJsonArray("denied");
+ assertNotNull(deniedArray2);
+ assertEquals(1, deniedArray2.size());
+ Set<String> deniedPrivilegeNames2 = new HashSet<>();
+ for (int i=0; i < deniedArray2.size(); i++) {
+ deniedPrivilegeNames2.add(deniedArray2.getString(i));
+ }
+ assertPrivilege(deniedPrivilegeNames2, true, PrivilegeConstants.JCR_NODE_TYPE_MANAGEMENT);
+ }
+
+
+
+ /**
+ * Test to verify adding an ACE in the first position of
+ * the ACL
+ */
+ @Test
+ public void testAddAceOrderByFirst() throws IOException, JsonException {
+ createAceOrderTestFolderWithOneAce();
+
+ testGroupId = createTestGroup();
+
+ addOrUpdateAce(testFolderUrl, testGroupId, true, "first");
+
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = testFolderUrl + ".acl.json";
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+ JsonObject jsonObject = parseJson(json);
+ assertEquals(2, jsonObject.size());
+
+ JsonObject group = jsonObject.getJsonObject(testGroupId);
+ assertNotNull(group);
+ assertEquals(testGroupId, group.getString("principal"));
+ assertEquals(0, group.getInt("order"));
+ JsonObject user = jsonObject.getJsonObject(testUserId);
+ assertNotNull(user);
+ assertEquals(testUserId, user.getString("principal"));
+ assertEquals(1, user.getInt("order"));
+ }
+
+ /**
+ * Test to verify adding an ACE at the end
+ * the ACL
+ */
+ @Test
+ public void testAddAceOrderByLast() throws IOException, JsonException {
+ createAceOrderTestFolderWithOneAce();
+
+ testGroupId = createTestGroup();
+
+ addOrUpdateAce(testFolderUrl, testGroupId, true, "last");
+
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = testFolderUrl + ".acl.json";
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+ JsonObject jsonObject = parseJson(json);
+ assertEquals(2, jsonObject.size());
+
+ JsonObject user = jsonObject.getJsonObject(testUserId);
+ assertNotNull(user);
+ assertEquals(testUserId, user.getString("principal"));
+ assertEquals(0, user.getInt("order"));
+ JsonObject group = jsonObject.getJsonObject(testGroupId);
+ assertNotNull(group);
+ assertEquals(testGroupId, group.getString("principal"));
+ assertEquals(1, group.getInt("order"));
+
+ }
+
+ /**
+ * Test to verify adding an ACE before an existing ACE
+ * the ACL
+ */
+ @Test
+ public void testAddAceOrderByBefore() throws IOException, JsonException {
+ createAceOrderTestFolderWithOneAce();
+
+ testGroupId = createTestGroup();
+
+ addOrUpdateAce(testFolderUrl, testGroupId, true, "before " + testUserId);
+
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = testFolderUrl + ".acl.json";
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+
+ JsonObject jsonObject = parseJson(json);
+ assertEquals(2, jsonObject.size());
+
+
+ JsonObject group = jsonObject.getJsonObject(testGroupId);
+ assertNotNull(group);
+ assertEquals(testGroupId, group.getString("principal"));
+ assertEquals(0, group.getInt("order"));
+ JsonObject user = jsonObject.getJsonObject(testUserId);
+ assertNotNull(user);
+ assertEquals(testUserId, user.getString("principal"));
+ assertEquals(1, user.getInt("order"));
+
+ }
+
+ /**
+ * Test to verify adding an ACE after an existing ACE
+ * the ACL
+ */
+ @Test
+ public void testAddAceOrderByAfter() throws IOException, JsonException {
+ createAceOrderTestFolderWithOneAce();
+
+ testGroupId = createTestGroup();
+
+ addOrUpdateAce(testFolderUrl, testGroupId, true, "after " + testUserId);
+
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = testFolderUrl + ".acl.json";
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+ JsonObject jsonObject = parseJson(json);
+ assertEquals(2, jsonObject.size());
+
+ JsonObject user = jsonObject.getJsonObject(testUserId);
+ assertNotNull(user);
+ assertEquals(testUserId, user.getString("principal"));
+ assertEquals(0, user.getInt("order"));
+ JsonObject group = jsonObject.getJsonObject(testGroupId);
+ assertNotNull(group);
+ assertEquals(testGroupId, group.getString("principal"));
+ assertEquals(1, group.getInt("order"));
+
+ }
+
+ /**
+ * Test to verify adding an ACE at a specific index inside
+ * the ACL
+ */
+ @Test
+ public void testAddAceOrderByNumeric() throws IOException, JsonException {
+ createAceOrderTestFolderWithOneAce();
+
+ testGroupId = createTestGroup();
+ addOrUpdateAce(testFolderUrl, testGroupId, true, "0");
+
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = testFolderUrl + ".acl.json";
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+
+ JsonObject jsonObject = parseJson(json);
+ assertEquals(2, jsonObject.size());
+
+ JsonObject group = jsonObject.getJsonObject(testGroupId);
+ assertNotNull(group);
+ assertEquals(testGroupId, group.getString("principal"));
+ assertEquals(0, group.getInt("order"));
+
+ JsonObject user = jsonObject.getJsonObject(testUserId);
+ assertNotNull(user);
+ assertEquals(testUserId, user.getString("principal"));
+ assertEquals(1, user.getInt("order"));
+
+
+
+ //add another principal between the testGroupId and testUserId
+ testUserId2 = createTestUser();
+ addOrUpdateAce(testFolderUrl, testUserId2, true, "1");
+
+ String json2 = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json2);
+
+ JsonObject jsonObject2 = parseJson(json2);
+ assertEquals(3, jsonObject2.size());
+
+ JsonObject group2 = jsonObject2.getJsonObject(testGroupId);
+ assertNotNull(group2);
+ assertEquals(testGroupId, group2.getString("principal"));
+ assertEquals(0, group2.getInt("order"));
+
+ JsonObject user3 = jsonObject2.getJsonObject(testUserId2);
+ assertNotNull(user3);
+ assertEquals(testUserId2, user3.getString("principal"));
+ assertEquals(1, user3.getInt("order"));
+
+ JsonObject user2 = jsonObject2.getJsonObject(testUserId);
+ assertNotNull(user2);
+ assertEquals(testUserId, user2.getString("principal"));
+ assertEquals(2, user2.getInt("order"));
+
+ }
+
+ /**
+ * Test to make sure modifying an existing ace without changing the order
+ * leaves the ACE in the same position in the ACL
+ */
+ @Test
+ public void testUpdateAcePreservePosition() throws IOException, JsonException {
+ createAceOrderTestFolderWithOneAce();
+
+ testGroupId = createTestGroup();
+
+ addOrUpdateAce(testFolderUrl, testGroupId, true, "first");
+
+ //update the ace to make sure the update does not change the ACE order
+ addOrUpdateAce(testFolderUrl, testGroupId, false, null);
+
+
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = testFolderUrl + ".acl.json";
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+ JsonObject jsonObject = parseJson(json);
+ assertEquals(2, jsonObject.size());
+
+ JsonObject group = jsonObject.getJsonObject(testGroupId);
+ assertNotNull(group);
+ assertEquals(testGroupId, group.getString("principal"));
+ assertEquals(0, group.getInt("order"));
+ JsonObject user = jsonObject.getJsonObject(testUserId);
+ assertNotNull(user);
+ assertEquals(testUserId, user.getString("principal"));
+ assertEquals(1, user.getInt("order"));
+
+ }
+
+
+ /**
+ * Helper to create a test folder with a single ACE pre-created
+ */
+ private void createAceOrderTestFolderWithOneAce() throws IOException, JsonException {
+ testUserId = createTestUser();
+
+ testFolderUrl = createTestFolder();
+
+ addOrUpdateAce(testFolderUrl, testUserId, true, null);
+
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = testFolderUrl + ".acl.json";
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+ JsonObject jsonObject = parseJson(json);
+ assertEquals(1, jsonObject.size());
+
+ JsonObject user = jsonObject.getJsonObject(testUserId);
+ assertNotNull(user);
+ assertEquals(testUserId, user.getString("principal"));
+ assertEquals(0, user.getInt("order"));
+
+ }
+
+ /**
+ * Helper to add or update an ace for testing
+ */
+ private void addOrUpdateAce(String folderUrl, String principalId, boolean readGranted, String order) throws IOException, JsonException {
+ addOrUpdateAce(folderUrl, principalId, readGranted, order, null);
+ }
+ private void addOrUpdateAce(String folderUrl, String principalId, boolean readGranted, String order, Map<String, Object> restrictions) throws IOException, JsonException {
+ String postUrl = folderUrl + ".modifyAce.html";
+
+ //1. create an initial set of privileges
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", principalId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", readGranted ? "granted" : "denied"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "denied"));
+ if (order != null) {
+ postParams.add(new BasicNameValuePair("order", order));
+ }
+ if (restrictions != null) {
+ Set<Entry<String, Object>> entrySet = restrictions.entrySet();
+ for (Entry<String, Object> entry : entrySet) {
+ Object value = entry.getValue();
+ if (value != null) {
+ String rname = entry.getKey();
+ String paramName = String.format("restriction@%s", rname);
+
+ if (value.getClass().isArray()) {
+ int length = Array.getLength(value);
+ for (int i=0; i < length; i++) {
+ Object rvalue = Array.get(value, i);
+ if (rvalue instanceof String) {
+ postParams.add(new BasicNameValuePair(paramName, (String)rvalue));
+ }
+ }
+ } else if (value instanceof String) {
+ postParams.add(new BasicNameValuePair(paramName, (String)value));
+ }
+ }
+ }
+ }
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+ }
+
+ /**
+ * Test for SLING-1677
+ */
+ @Test
+ public void testModifyAceResponseAsJSON() throws IOException, JsonException {
+ testUserId = createTestUser();
+
+ testFolderUrl = createTestFolder();
+
+ String postUrl = testFolderUrl + ".modifyAce.json";
+
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "denied"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:modifyAccessControl", "bogus")); //invalid value should be ignored.
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObject = parseJson(json);
+ assertNotNull(jsonObject);
+ }
+
+
+ /**
+ * Test for SLING-3010
+ */
+ @Test
+ public void testMergeAceForUserGrantNestedAggregatePrivilegeAfterDenySuperAggregatePrivilege() throws IOException, JsonException {
+ testUserId = createTestUser();
+
+ testFolderUrl = createTestFolder();
+
+ String postUrl = testFolderUrl + ".modifyAce.json";
+
+ //1. setup an initial set of denied privileges for the test user
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:versionManagement", "denied"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "denied"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:modifyAccessControl", "denied"));
+ postParams.add(new BasicNameValuePair("privilege@rep:write", "denied"));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ /*String json = */getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+
+ //2. now grant the jcr:write subset from the rep:write aggregate privilege
+ postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:versionManagement", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:modifyAccessControl", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "granted")); //sub-aggregate of rep:write
+
+ /*String json = */getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //3. verify that the acl has the correct values
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = testFolderUrl + ".acl.json";
+
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+ JsonObject jsonObject = parseJson(json);
+ assertEquals(1, jsonObject.size());
+
+ JsonObject aceObject = jsonObject.getJsonObject(testUserId);
+ assertNotNull(aceObject);
+
+ assertEquals(testUserId, aceObject.getString("principal"));
+
+ JsonArray grantedArray = aceObject.getJsonArray("granted");
+ assertNotNull(grantedArray);
+ assertEquals(4, grantedArray.size());
+ Set<String> grantedPrivilegeNames = new HashSet<>();
+ for (int i=0; i < grantedArray.size(); i++) {
+ grantedPrivilegeNames.add(grantedArray.getString(i));
+ }
+ assertPrivilege(grantedPrivilegeNames, true, PrivilegeConstants.JCR_VERSION_MANAGEMENT);
+ assertPrivilege(grantedPrivilegeNames, true, PrivilegeConstants.JCR_READ);
+ assertPrivilege(grantedPrivilegeNames, true, PrivilegeConstants.JCR_MODIFY_ACCESS_CONTROL);
+ assertPrivilege(grantedPrivilegeNames, true, PrivilegeConstants.JCR_WRITE);
+
+ JsonArray deniedArray = aceObject.getJsonArray("denied");
+ assertNotNull(deniedArray);
+ assertEquals(1, deniedArray.size());
+ Set<String> deniedPrivilegeNames = new HashSet<>();
+ for (int i=0; i < deniedArray.size(); i++) {
+ deniedPrivilegeNames.add(deniedArray.getString(i));
+ }
+ //the leftovers from the denied rep:write that were not granted with jcr:write
+ assertPrivilege(deniedPrivilegeNames, true, PrivilegeConstants.JCR_NODE_TYPE_MANAGEMENT);
+ }
+
+ /**
+ * Test for SLING-3010
+ */
+ @Test
+ public void testMergeAceForUserGrantAggregatePrivilegePartsAfterDenyAggregatePrivilege() throws IOException, JsonException {
+ testUserId = createTestUser();
+
+ testFolderUrl = createTestFolder();
+
+ String postUrl = testFolderUrl + ".modifyAce.json";
+
+ //1. setup an initial set of denied privileges for the test user
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:versionManagement", "denied"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "denied"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:modifyAccessControl", "denied"));
+ postParams.add(new BasicNameValuePair("privilege@rep:write", "denied"));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ /*String json = */getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //2. now grant the all the privileges contained in the rep:write privilege
+ postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:versionManagement", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:modifyAccessControl", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:nodeTypeManagement", "granted")); //sub-privilege of rep:write
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "granted")); //sub-aggregate of rep:write
+
+ /*String json = */getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //3. verify that the acl has the correct values
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = testFolderUrl + ".acl.json";
+
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+ JsonObject jsonObject = parseJson(json);
+ assertEquals(1, jsonObject.size());
+
+ JsonObject aceObject = jsonObject.getJsonObject(testUserId);
+ assertNotNull(aceObject);
+
+ assertEquals(testUserId, aceObject.getString("principal"));
+
+ JsonArray grantedArray = aceObject.getJsonArray("granted");
+ assertNotNull(grantedArray);
+ Set<String> grantedPrivilegeNames = new HashSet<>();
+ for (int i=0; i < grantedArray.size(); i++) {
+ grantedPrivilegeNames.add(grantedArray.getString(i));
+ }
+ assertPrivilege(grantedPrivilegeNames, true, PrivilegeConstants.JCR_VERSION_MANAGEMENT);
+ assertPrivilege(grantedPrivilegeNames, true, PrivilegeConstants.JCR_READ);
+ assertPrivilege(grantedPrivilegeNames, true, PrivilegeConstants.JCR_MODIFY_ACCESS_CONTROL);
+ assertPrivilege(grantedPrivilegeNames, true, PrivilegeConstants.REP_WRITE); //jcr:nodeTypeManagement + jcr:write
+ assertEquals("Expecting the correct number of privileges in " + grantedPrivilegeNames, 4, grantedPrivilegeNames.size());
+
+ //should be nothing left in the denied set.
+ Object deniedArray = aceObject.get("denied");
+ assertNull(deniedArray);
+ }
+
+ /**
+ * SLING-8117 - Test to verify adding an ACE with restriction to
+ * the ACL
+ */
+ @Test
+ public void testAddAceWithRestriction() throws IOException, JsonException {
+ createAceOrderTestFolderWithOneAce();
+
+ testGroupId = createTestGroup();
+
+ Map<String, Object> restrictions = new HashMap<>();
+ restrictions.put("rep:glob", "/hello");
+ restrictions.put("rep:itemNames", new String[] {"child1", "child2"});
+
+ addOrUpdateAce(testFolderUrl, testGroupId, true, "first", restrictions);
+
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = testFolderUrl + ".acl.json";
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+
+ JsonObject jsonObject = parseJson(json);
+ assertEquals(2, jsonObject.size());
+
+
+ JsonObject group = jsonObject.getJsonObject(testGroupId);
+ assertNotNull(group);
+ assertEquals(testGroupId, group.getString("principal"));
+ assertEquals(0, group.getInt("order"));
+
+ //verify restrictions are returned
+ assertTrue(group.containsKey("restrictions"));
+ JsonObject restrictionsObj = group.getJsonObject("restrictions");
+ assertNotNull(restrictionsObj);
+
+ Object repGlob = restrictionsObj.get("rep:glob");
+ assertNotNull(repGlob);
+ assertTrue(repGlob instanceof JsonString);
+ assertEquals("/hello", ((JsonString)repGlob).getString());
+
+ Object itemNames = restrictionsObj.get("rep:itemNames");
+ assertNotNull(itemNames);
+ assertTrue(itemNames instanceof JsonArray);
+ assertEquals(2, ((JsonArray)itemNames).size());
+
+
+ JsonObject user = jsonObject.getJsonObject(testUserId);
+ assertNotNull(user);
+ assertEquals(testUserId, user.getString("principal"));
+ assertEquals(1, user.getInt("order"));
+ assertFalse(user.containsKey("restrictions"));
+
+ }
+
+ /**
+ * SLING-8117 - Test to verify merging an ACE with an existing restriction to
+ * the ACL
+ */
+ @Test
+ public void testUpdateAceToMergeNewRestriction() throws IOException, JsonException {
+ createAceOrderTestFolderWithOneAce();
+
+ testGroupId = createTestGroup();
+
+ //first create an ACE with the first restriction
+ Map<String, Object> restrictions = new HashMap<>();
+ restrictions.put("rep:glob", "/hello");
+
+ addOrUpdateAce(testFolderUrl, testGroupId, true, "first", restrictions);
+
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = testFolderUrl + ".acl.json";
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+ JsonObject jsonObject = parseJson(json);
+ assertEquals(2, jsonObject.size());
+
+ JsonObject group = jsonObject.getJsonObject(testGroupId);
+ assertNotNull(group);
+ assertEquals(testGroupId, group.getString("principal"));
+ assertEquals(0, group.getInt("order"));
+
+ //verify restrictions are returned
+ assertTrue(group.containsKey("restrictions"));
+ JsonObject restrictionsObj = group.getJsonObject("restrictions");
+ assertNotNull(restrictionsObj);
+ assertEquals(1, restrictionsObj.size());
+
+ Object repGlob = restrictionsObj.get("rep:glob");
+ assertNotNull(repGlob);
+ assertTrue(repGlob instanceof JsonString);
+ assertEquals("/hello", ((JsonString)repGlob).getString());
+
+
+
+ //second update the ACE with a second restriction
+ Map<String, Object> restrictions2 = new HashMap<>();
+ restrictions2.put("rep:itemNames", new String[] {"child1", "child2"});
+
+ addOrUpdateAce(testFolderUrl, testGroupId, true, "first", restrictions2);
+
+ String json2 = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json2);
+
+ JsonObject jsonObject2 = parseJson(json2);
+ assertEquals(2, jsonObject2.size());
+
+ JsonObject group2 = jsonObject2.getJsonObject(testGroupId);
+ assertNotNull(group2);
+ assertEquals(testGroupId, group2.getString("principal"));
+ assertEquals(0, group2.getInt("order"));
+
+ //verify restrictions are returned
+ assertTrue(group2.containsKey("restrictions"));
+ JsonObject restrictionsObj2 = group2.getJsonObject("restrictions");
+ assertNotNull(restrictionsObj2);
+ assertEquals(2, restrictionsObj2.size());
+
+ Object repGlob2 = restrictionsObj2.get("rep:glob");
+ assertNotNull(repGlob2);
+ assertTrue(repGlob2 instanceof JsonString);
+ assertEquals("/hello", ((JsonString)repGlob2).getString());
+
+ Object itemNames2 = restrictionsObj2.get("rep:itemNames");
+ assertNotNull(itemNames2);
+ assertTrue(itemNames2 instanceof JsonArray);
+ assertEquals(2, ((JsonArray)itemNames2).size());
+ }
+
+ /**
+ * SLING-8117 - Test to verify removing a restriction from an ACE
+ */
+ @Test
+ public void testUpdateAceToRemoveRestriction() throws IOException, JsonException {
+ createAceOrderTestFolderWithOneAce();
+
+ testGroupId = createTestGroup();
+
+ //first create an ACE with the restrictions
+ Map<String, Object> restrictions = new HashMap<>();
+ restrictions.put("rep:glob", "/hello");
+ restrictions.put("rep:itemNames", new String[] {"child1", "child2"});
+
+ addOrUpdateAce(testFolderUrl, testGroupId, true, "first", restrictions);
+
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = testFolderUrl + ".acl.json";
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+ JsonObject jsonObject = parseJson(json);
+ assertEquals(2, jsonObject.size());
+
+ JsonObject group = jsonObject.getJsonObject(testGroupId);
+ assertNotNull(group);
+ assertEquals(testGroupId, group.getString("principal"));
+ assertEquals(0, group.getInt("order"));
+
+ //verify restrictions are returned
+ assertTrue(group.containsKey("restrictions"));
+ JsonObject restrictionsObj = group.getJsonObject("restrictions");
+ assertNotNull(restrictionsObj);
+
+ Object repGlob = restrictionsObj.get("rep:glob");
+ assertNotNull(repGlob);
+ assertTrue(repGlob instanceof JsonString);
+ assertEquals("/hello", ((JsonString)repGlob).getString());
+
+ Object itemNames = restrictionsObj.get("rep:itemNames");
+ assertNotNull(itemNames);
+ assertTrue(itemNames instanceof JsonArray);
+ assertEquals(2, ((JsonArray)itemNames).size());
+
+
+ //second remove the restrictions
+ Map<String, Object> restrictions2 = new HashMap<>();
+ restrictions2.put("rep:glob@Delete", "true");
+ restrictions2.put("rep:itemNames@Delete", new String[] {"value does not", "matter"});
+ addOrUpdateAce(testFolderUrl, testGroupId, true, "first", restrictions2);
+
+ String json2 = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json2);
+
+ JsonObject jsonObject2 = parseJson(json2);
+ assertEquals(2, jsonObject2.size());
+
+ JsonObject group2 = jsonObject2.getJsonObject(testGroupId);
+ assertNotNull(group2);
+ assertEquals(testGroupId, group2.getString("principal"));
+ assertEquals(0, group2.getInt("order"));
+
+ //verify no restrictions are returned
+ assertFalse(group2.containsKey("restrictions"));
+ }
+
+ /**
+ * SLING-8117 - Test to verify removing a restriction from an ACE does not happen
+ * if a new value with the same name has also been supplied
+ */
+ @Test
+ public void testUpdateAceToRemoveRestrictionWithConflict() throws IOException, JsonException {
+ createAceOrderTestFolderWithOneAce();
+
+ testGroupId = createTestGroup();
+
+ //first create an ACE with the restrictions
+ Map<String, Object> restrictions = new HashMap<>();
+ restrictions.put("rep:glob", "/hello");
+
+ addOrUpdateAce(testFolderUrl, testGroupId, true, "first", restrictions);
+
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = testFolderUrl + ".acl.json";
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+ JsonObject jsonObject = parseJson(json);
+ assertEquals(2, jsonObject.size());
+
+ JsonObject group = jsonObject.getJsonObject(testGroupId);
+ assertNotNull(group);
+ assertEquals(testGroupId, group.getString("principal"));
+ assertEquals(0, group.getInt("order"));
+
+ //verify restrictions are returned
+ assertTrue(group.containsKey("restrictions"));
+ JsonObject restrictionsObj = group.getJsonObject("restrictions");
+ assertNotNull(restrictionsObj);
+
+ Object repGlob = restrictionsObj.get("rep:glob");
+ assertNotNull(repGlob);
+ assertTrue(repGlob instanceof JsonString);
+ assertEquals("/hello", ((JsonString)repGlob).getString());
+
+
+ //second remove the restriction and also supply a new value of the same
+ Map<String, Object> restrictions2 = new HashMap<>();
+ restrictions2.put("rep:glob@Delete", "true");
+ restrictions2.put("rep:glob", "/hello_again");
+ addOrUpdateAce(testFolderUrl, testGroupId, true, "first", restrictions2);
+
+ String json2 = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json2);
+
+ JsonObject jsonObject2 = parseJson(json2);
+ assertEquals(2, jsonObject2.size());
+
+ JsonObject group2 = jsonObject2.getJsonObject(testGroupId);
+ assertNotNull(group2);
+ assertEquals(testGroupId, group2.getString("principal"));
+ assertEquals(0, group2.getInt("order"));
+
+ //verify restrictions are returned
+ assertTrue(group2.containsKey("restrictions"));
+ JsonObject restrictionsObj2 = group2.getJsonObject("restrictions");
+ assertNotNull(restrictionsObj2);
+
+ Object repGlob2 = restrictionsObj2.get("rep:glob");
+ assertNotNull(repGlob2);
+ assertTrue(repGlob2 instanceof JsonString);
+ assertEquals("/hello_again", ((JsonString)repGlob2).getString());
+ }
+
+ /**
+ * SLING-8809 - Test to verify submitting an invalid principalId returns a
+ * good error message instead of a NullPointerException
+ */
+ @Test
+ public void testModifyAceForInvalidUser() throws IOException, JsonException {
+ String invalidUserId = "notRealUser123";
+
+ testFolderUrl = createTestFolder();
+
+ String postUrl = testFolderUrl + ".modifyAce.json";
+
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":http-equiv-accept", JSONResponse.RESPONSE_CONTENT_TYPE));
+ postParams.add(new BasicNameValuePair("principalId", invalidUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "denied"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:modifyAccessControl", "bogus")); //invalid value should be ignored.
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ assertNotNull(json);
+
+ JsonObject jsonObject = parseJson(json);
+ assertEquals("javax.jcr.RepositoryException: Invalid principalId was submitted.", jsonObject.getString("status.message"));
+ }
+
+ /**
+ * SLING-8811 - Test to verify that the "changes" list of a modifyAce response
+ * returns the list of principals that were changed
+ */
+ @Test
+ public void testModifyAceChangesInResponse() throws IOException, JsonException {
+ testUserId = createTestUser();
+
+ testFolderUrl = createTestFolder();
+
+ String postUrl = testFolderUrl + ".modifyAce.json";
+
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":http-equiv-accept", JSONResponse.RESPONSE_CONTENT_TYPE));
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "denied"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:modifyAccessControl", "bogus")); //invalid value should be ignored.
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+ JsonObject jsonObject = parseJson(json);
+ JsonArray changesArray = jsonObject.getJsonArray("changes");
+ assertNotNull(changesArray);
+ assertEquals(1, changesArray.size());
+ JsonObject change = changesArray.getJsonObject(0);
+ assertEquals("modified", change.getString("type"));
+ assertEquals(testUserId, change.getString("argument"));
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/RemoveAcesIT.java b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/RemoveAcesIT.java
new file mode 100644
index 0000000..77db5e8
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/RemoveAcesIT.java
@@ -0,0 +1,316 @@
+/*
+ * 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.jcr.jackrabbit.accessmanager.it;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+
+import javax.json.JsonArray;
+import javax.json.JsonException;
+import javax.json.JsonObject;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.sling.servlets.post.JSONResponse;
+import org.apache.sling.servlets.post.PostResponseCreator;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Tests for the 'removeAce' Sling POST operation
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class RemoveAcesIT extends AccessManagerClientTestSupport {
+
+ private ServiceRegistration<PostResponseCreator> serviceReg;
+
+ @Before
+ @Override
+ public void before() throws IOException, URISyntaxException {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ Dictionary<String, Object> props = new Hashtable<>(); // NOSONAR
+ serviceReg = bundle.getBundleContext().registerService(PostResponseCreator.class,
+ new CustomPostResponseCreatorImpl(), props);
+
+ super.before();
+ }
+
+ @After
+ @Override
+ public void after() throws IOException {
+ if (serviceReg != null) {
+ serviceReg.unregister();
+ }
+
+ super.after();
+ }
+
+
+ private String createFolderWithAces(boolean addGroupAce) throws IOException, JsonException {
+ testUserId = createTestUser();
+ testFolderUrl = createTestFolder();
+
+ String postUrl = testFolderUrl + ".modifyAce.html";
+
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testUserId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "granted"));
+ postParams.add(new BasicNameValuePair("privilege@jcr:write", "denied"));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ if (addGroupAce) {
+ testGroupId = createTestGroup();
+
+ postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("principalId", testGroupId));
+ postParams.add(new BasicNameValuePair("privilege@jcr:read", "granted"));
+
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+ }
+
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = testFolderUrl + ".acl.json";
+
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+ JsonObject jsonObject = parseJson(json);
+
+ if (addGroupAce) {
+ assertEquals(2, jsonObject.size());
+ } else {
+ assertEquals(1, jsonObject.size());
+ }
+
+ JsonObject aceObject = jsonObject.getJsonObject(testUserId);
+ assertNotNull(aceObject);
+
+ assertEquals(0, aceObject.getInt("order"));
+
+ String principalString = aceObject.getString("principal");
+ assertEquals(testUserId, principalString);
+
+ JsonArray grantedArray = aceObject.getJsonArray("granted");
+ assertNotNull(grantedArray);
+ assertEquals("jcr:read", grantedArray.getString(0));
+
+ JsonArray deniedArray = aceObject.getJsonArray("denied");
+ assertNotNull(deniedArray);
+ assertEquals("jcr:write", deniedArray.getString(0));
+
+ if (addGroupAce) {
+ aceObject = jsonObject.getJsonObject(testGroupId);
+ assertNotNull(aceObject);
+
+ principalString = aceObject.getString("principal");
+ assertEquals(testGroupId, principalString);
+
+ assertEquals(1, aceObject.getInt("order"));
+
+ grantedArray = aceObject.getJsonArray("granted");
+ assertNotNull(grantedArray);
+ assertEquals("jcr:read", grantedArray.getString(0));
+ }
+
+ return testFolderUrl;
+ }
+
+ //test removing a single ace
+ @Test
+ public void testRemoveAce() throws IOException, JsonException {
+ String folderUrl = createFolderWithAces(false);
+
+ //remove the ace for the testUser principal
+ String postUrl = folderUrl + ".deleteAce.html";
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":applyTo", testUserId));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = folderUrl + ".acl.json";
+
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+ JsonObject jsonObject = parseJson(json);
+ assertNotNull(jsonObject);
+ assertEquals(0, jsonObject.size());
+ }
+
+ /**
+ * Test for SLING-7831
+ */
+ @Test
+ public void testRemoveAceCustomPostResponse() throws IOException, JsonException {
+ String folderUrl = createFolderWithAces(false);
+
+ //remove the ace for the testUser principal
+ String postUrl = folderUrl + ".deleteAce.html";
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":responseType", "custom"));
+ postParams.add(new BasicNameValuePair(":applyTo", testUserId));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String content = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_HTML, postParams, HttpServletResponse.SC_OK);
+ assertEquals("Thanks!", content); //verify that the content matches the custom response
+ }
+
+ //test removing multiple aces
+ @Test
+ public void testRemoveAces() throws IOException, JsonException {
+ String folderUrl = createFolderWithAces(true);
+
+ //remove the ace for the testUser principal
+ String postUrl = folderUrl + ".deleteAce.html";
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":applyTo", testUserId));
+ postParams.add(new BasicNameValuePair(":applyTo", testGroupId));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams, null);
+
+ //fetch the JSON for the acl to verify the settings.
+ String getUrl = folderUrl + ".acl.json";
+
+ String json = getAuthenticatedContent(creds, getUrl, CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+ JsonObject jsonObject = parseJson(json);
+ assertNotNull(jsonObject);
+ assertEquals(0, jsonObject.size());
+ }
+
+ /**
+ * Test for SLING-1677
+ */
+ @Test
+ public void testRemoveAcesResponseAsJSON() throws IOException, JsonException {
+ String folderUrl = createFolderWithAces(true);
+
+ //remove the ace for the testUser principal
+ String postUrl = folderUrl + ".deleteAce.json";
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":applyTo", testUserId));
+ postParams.add(new BasicNameValuePair(":applyTo", testGroupId));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObject = parseJson(json);
+ assertNotNull(jsonObject);
+ }
+
+ /**
+ * SLING-8810 - Test that a attempt to remove an ACE from a
+ * node that does not yet have an AccessControlList responds
+ * in a consistent way to other scenarios
+ */
+ @Test
+ public void testRemoveAceWhenAccessControlListDoesNotExist() throws IOException, JsonException {
+ testUserId = createTestUser();
+ testFolderUrl = createTestFolder();
+
+ String postUrl = testFolderUrl + ".deleteAce.json";
+
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":http-equiv-accept", JSONResponse.RESPONSE_CONTENT_TYPE));
+ postParams.add(new BasicNameValuePair(":applyTo", testUserId));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+ JsonObject jsonObject = parseJson(json);
+ JsonArray changesArray = jsonObject.getJsonArray("changes");
+ assertNotNull(changesArray);
+ assertEquals(0, changesArray.size());
+ }
+
+ /**
+ * SLING-8812 - Test to verify submitting an invalid principalId returns a
+ * good error message instead of a NullPointerException
+ */
+ @Test
+ public void testRemoveAceForInvalidUser() throws IOException, JsonException {
+ String invalidUserId = "notRealUser123";
+
+ String folderUrl = createFolderWithAces(true);
+
+ String postUrl = folderUrl + ".deleteAce.json";
+
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":http-equiv-accept", JSONResponse.RESPONSE_CONTENT_TYPE));
+ postParams.add(new BasicNameValuePair(":applyTo", invalidUserId));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ assertNotNull(json);
+
+ JsonObject jsonObject = parseJson(json);
+ assertEquals("javax.jcr.RepositoryException: Invalid principalId was submitted.", jsonObject.getString("status.message"));
+ }
+
+ /**
+ * SLING-8811 - Test to verify that the "changes" list of a modifyAce response
+ * returns the list of principals that were changed
+ */
+ @Test
+ public void testRemoveAceChangesInResponse() throws IOException, JsonException {
+ String folderUrl = createFolderWithAces(true);
+
+ String postUrl = folderUrl + ".deleteAce.json";
+
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":http-equiv-accept", JSONResponse.RESPONSE_CONTENT_TYPE));
+ postParams.add(new BasicNameValuePair(":applyTo", testUserId));
+
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+ assertNotNull(json);
+
+ JsonObject jsonObject = parseJson(json);
+ JsonArray changesArray = jsonObject.getJsonArray("changes");
+ assertNotNull(changesArray);
+ assertEquals(1, changesArray.size());
+ JsonObject change = changesArray.getJsonObject(0);
+ assertEquals("deleted", change.getString("type"));
+ assertEquals(testUserId, change.getString("argument"));
+ }
+
+}
+
diff --git a/src/test/resources/content/apps/nt/unstructured/privileges-info.json.esp b/src/test/resources/content/apps/nt/unstructured/privileges-info.json.esp
new file mode 100644
index 0000000..01b32b6
--- /dev/null
+++ b/src/test/resources/content/apps/nt/unstructured/privileges-info.json.esp
@@ -0,0 +1,111 @@
+<%
+/*
+ * 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.
+ */
+
+var factory = Packages.javax.json.Json.createBuilderFactory(Packages.java.util.Collections.emptyMap());
+var jsonObjBuilder = factory.createObjectBuilder();
+
+var privilegesInfo = new Packages.org.apache.sling.jcr.jackrabbit.accessmanager.PrivilegesInfo();
+
+jsonObjBuilder.add("canAddChildren", privilegesInfo.canAddChildren(currentNode));
+jsonObjBuilder.add("canDeleteChildren", privilegesInfo.canDeleteChildren(currentNode));
+jsonObjBuilder.add("canDelete", privilegesInfo.canDelete(currentNode));
+jsonObjBuilder.add("canModifyProperties", privilegesInfo.canModifyProperties(currentNode));
+jsonObjBuilder.add("canReadAccessControl", privilegesInfo.canReadAccessControl(currentNode));
+jsonObjBuilder.add("canModifyAccessControl", privilegesInfo.canModifyAccessControl(currentNode));
+
+if (privilegesInfo.canReadAccessControl(currentNode)) {
+ var declaredBuilder = factory.createObjectBuilder();
+ var declared = privilegesInfo.getDeclaredAccessRights(currentNode);
+ if (declared != null) {
+ var iterator = declared.entrySet().iterator();
+ while (iterator.hasNext()) {
+ var next = iterator.next();
+ var principal = next.getKey();
+ var accessRights = next.getValue();
+
+ var entryBuilder = factory.createObjectBuilder();
+
+ var grantedBuilder = factory.createArrayBuilder();
+ var granted = accessRights.getGranted();
+ var grantedIt = granted.iterator();
+ while (grantedIt.hasNext()) {
+ var next = grantedIt.next();
+ var name = next.getName();
+ grantedBuilder.add(name);
+ }
+ entryBuilder.add("granted", grantedBuilder);
+
+ var deniedBuilder = factory.createArrayBuilder();
+ var denied = accessRights.getDenied();
+ var deniedIt = denied.iterator();
+ while (deniedIt.hasNext()) {
+ var next = deniedIt.next();
+ var name = next.getName();
+ deniedBuilder.add(name);
+ }
+ entryBuilder.add("denied", deniedBuilder);
+
+ declaredBuilder.add(principal.getName(), entryBuilder);
+ }
+ }
+ jsonObjBuilder.add("declaredAccessRights", declaredBuilder);
+
+ var effectiveBuilder = factory.createObjectBuilder();
+ var effective = privilegesInfo.getEffectiveAccessRights(currentNode);
+ if (effective != null) {
+ var iterator = effective.entrySet().iterator();
+ while (iterator.hasNext()) {
+ var next = iterator.next();
+ var principal = next.getKey();
+ var accessRights = next.getValue();
+
+ var entryBuilder = factory.createObjectBuilder();
+
+ var grantedBuilder = factory.createArrayBuilder();
+ var granted = accessRights.getGranted();
+ var grantedIt = granted.iterator();
+ while (grantedIt.hasNext()) {
+ var next = grantedIt.next();
+ var name = next.getName();
+ grantedBuilder.add(name);
+ }
+ entryBuilder.add("granted", grantedBuilder);
+
+ var deniedBuilder = factory.createArrayBuilder();
+ var denied = accessRights.getDenied();
+ var deniedIt = denied.iterator();
+ while (deniedIt.hasNext()) {
+ var next = deniedIt.next();
+ var name = next.getName();
+ deniedBuilder.add(name);
+ }
+ entryBuilder.add("denied", deniedBuilder);
+
+ effectiveBuilder.add(principal.getName(), entryBuilder);
+ }
+ }
+ jsonObjBuilder.add("effectiveAccessRights", effectiveBuilder);
+}
+
+var jsonObj = jsonObjBuilder.build();
+var jsonWriter = Packages.javax.json.Json.createWriter(response.getWriter());
+jsonWriter.writeObject(jsonObj);
+jsonWriter.close();
+%>
\ No newline at end of file