This is an automated email from the ASF dual-hosted git repository.

enorman pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-jackrabbit-accessmanager.git


The following commit(s) were added to refs/heads/master by this push:
     new daf66b0  SLING-11271 servlet to get json for an ACE or Effective ACE 
(#7)
daf66b0 is described below

commit daf66b026293560552aae49a13e84d6b915cfb88
Author: Eric Norman <[email protected]>
AuthorDate: Fri Apr 22 11:05:47 2022 -0700

    SLING-11271 servlet to get json for an ACE or Effective ACE (#7)
---
 pom.xml                                            |   2 +-
 .../sling/jcr/jackrabbit/accessmanager/GetAce.java |  48 ++++
 .../jackrabbit/accessmanager/GetEffectiveAce.java  |  48 ++++
 .../jackrabbit/accessmanager/impl/JsonConvert.java | 167 +++++++++++
 .../post/AbstractAccessGetServlet.java             | 187 +++++++++++++
 .../accessmanager/post/AbstractGetAceServlet.java  |  89 ++++++
 .../accessmanager/post/AbstractGetAclServlet.java  | 201 ++------------
 .../accessmanager/post/GetAceServlet.java          | 112 ++++++++
 .../accessmanager/post/GetEffectiveAceServlet.java | 112 ++++++++
 .../accessmanager/impl/JsonConvertTest.java        | 309 +++++++++++++++++++++
 .../jcr/jackrabbit/accessmanager/it/GetAceIT.java  | 138 +++++++++
 .../accessmanager/it/GetAceServiceIT.java          | 225 +++++++++++++++
 .../jcr/jackrabbit/accessmanager/it/GetAclIT.java  |  23 ++
 .../jcr/jackrabbit/accessmanager/it/GetEaceIT.java | 137 +++++++++
 .../accessmanager/it/GetEaceServiceIT.java         | 225 +++++++++++++++
 15 files changed, 1851 insertions(+), 172 deletions(-)

diff --git a/pom.xml b/pom.xml
index 5c64daf..879e373 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,7 +29,7 @@
 
     <artifactId>org.apache.sling.jcr.jackrabbit.accessmanager</artifactId>
     <version>3.0.11-SNAPSHOT</version>
-    <name>Apache Sling JCR Jackrabbit Access Manager </name>
+    <name>Apache Sling JCR Jackrabbit Access Manager</name>
     <description>
         Provides POST operations for JCR Jackrabbit Access Management
     </description>
diff --git 
a/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/GetAce.java 
b/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/GetAce.java
new file mode 100644
index 0000000..cef013b
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/GetAce.java
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.json.JsonObject;
+
+/**
+ * The <code>GetAce</code> service api.
+ * <p>
+ * This interface is not intended to be implemented by bundles. It is
+ * implemented by this bundle and may be used by client bundles.
+ * </p>
+ */
+public interface GetAce {
+
+    /**
+     * Gets the declared access control entry for a resource and principal
+     * 
+     * @param jcrSession the JCR session of the user updating the user
+     * @param resourcePath The path of the resource to get the ACE for 
(required)
+     * @param principalId the principal to get the ACE for (required)
+     * @return the ACE as a JSON object 
+     * @throws RepositoryException if any errors reading the information
+     */
+    JsonObject getAce(Session jcrSession,
+                            String resourcePath,
+                            String principalId
+                ) throws RepositoryException;
+
+}
diff --git 
a/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/GetEffectiveAce.java
 
b/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/GetEffectiveAce.java
new file mode 100644
index 0000000..8cf0103
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/GetEffectiveAce.java
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.json.JsonObject;
+
+/**
+ * The <code>GetEffectiveAce</code> service api.
+ * <p>
+ * This interface is not intended to be implemented by bundles. It is
+ * implemented by this bundle and may be used by client bundles.
+ * </p>
+ */
+public interface GetEffectiveAce {
+
+    /**
+     * Gets the effective access control entry for a resource and principal
+     * 
+     * @param jcrSession the JCR session of the user updating the user
+     * @param resourcePath The path of the resource to get the ACE for 
(required)
+     * @param principalId the principal to get the ACE for (required)
+     * @return the ACE as a JSON object 
+     * @throws RepositoryException if any errors reading the information
+     */
+    JsonObject getEffectiveAce(Session jcrSession,
+                            String resourcePath,
+                            String principalId
+                ) throws RepositoryException;
+
+}
diff --git 
a/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/impl/JsonConvert.java
 
b/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/impl/JsonConvert.java
new file mode 100644
index 0000000..6918aa9
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/impl/JsonConvert.java
@@ -0,0 +1,167 @@
+/*
+ * 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.impl;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.security.Principal;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.security.Privilege;
+import javax.json.Json;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonObjectBuilder;
+
+import org.apache.sling.jcr.jackrabbit.accessmanager.LocalPrivilege;
+import org.apache.sling.jcr.jackrabbit.accessmanager.LocalRestriction;
+
+/**
+ * Utilities to help convert ACL/ACE data to JSON
+ */
+public class JsonConvert {
+    public static final String KEY_PRINCIPAL = "principal";
+    public static final String KEY_ORDER = "order";
+    public static final String KEY_PRIVILEGES = "privileges";
+    public static final String KEY_ALLOW = "allow";
+    public static final String KEY_DENY = "deny";
+
+    private JsonConvert() {
+        // no-op
+    }
+
+    public static JsonObjectBuilder convertToJson(Principal principal, 
Map<Privilege, LocalPrivilege> entry,
+            int order) {
+        JsonObjectBuilder principalObj = Json.createObjectBuilder();
+        principalObj.add(JsonConvert.KEY_PRINCIPAL, principal.getName());
+        if (order != -1) {
+            principalObj.add(JsonConvert.KEY_ORDER, order);
+        }
+        Collection<LocalPrivilege> privileges = entry.values();
+        if (!privileges.isEmpty()) {
+            JsonObjectBuilder privilegesObj = Json.createObjectBuilder();
+            for (LocalPrivilege pi : privileges) {
+                if (pi.isNone()) {
+                    continue;
+                }
+                JsonObjectBuilder privilegeObj = Json.createObjectBuilder();
+
+                if (pi.isAllow()) {
+                    JsonConvert.addRestrictions(privilegeObj, 
JsonConvert.KEY_ALLOW, pi.getAllowRestrictions());
+                }
+                if (pi.isDeny()) {
+                    JsonConvert.addRestrictions(privilegeObj, 
JsonConvert.KEY_DENY, pi.getDenyRestrictions());
+                }
+                privilegesObj.add(pi.getName(), privilegeObj);
+            }
+            principalObj.add(JsonConvert.KEY_PRIVILEGES, privilegesObj);
+        }
+        return principalObj;
+    }
+
+    public static void addRestrictions(JsonObjectBuilder privilegeObj, String 
key, Set<LocalRestriction> restrictions) {
+        if (restrictions.isEmpty()) {
+            privilegeObj.add(key, true);
+        } else {
+            JsonObjectBuilder allowObj = Json.createObjectBuilder();
+            for (LocalRestriction ri : restrictions) {
+                if (ri.isMultiValue()) {
+                    JsonArrayBuilder rvalues = Json.createArrayBuilder();
+                    for (Value value: ri.getValues()) {
+                        addTo(rvalues, value);
+                    }
+                    allowObj.add(ri.getName(), rvalues);
+                } else {
+                    addTo(allowObj, ri.getName(), ri.getValue());
+                }
+            }
+            privilegeObj.add(key, allowObj);
+        }
+    }
+
+    public static JsonObjectBuilder addTo(JsonObjectBuilder builder, String 
key, Object value) {
+        value = convertJcrValue(value);
+        if (value instanceof Byte || value instanceof Short || value 
instanceof Integer || value instanceof Long) {
+            builder.add(key, ((Number) value).longValue());
+        } else if (value instanceof BigDecimal) {
+            builder.add(key, (BigDecimal) value);
+        } else if (value instanceof BigInteger) {
+            builder.add(key, (BigInteger) value);
+        } else if (value instanceof Boolean) {
+            builder.add(key, (Boolean) value);
+        } else if (value instanceof Float || value instanceof Double) {
+            builder.add(key, ((Number) value).doubleValue());
+        } else if (value instanceof Privilege) {
+            JsonObjectBuilder privilegeBuilder = Json.createObjectBuilder();
+            privilegeBuilder.add("name", ((Privilege) value).getName());
+            builder.add(key, privilegeBuilder);
+        } else if (value instanceof String) {
+            builder.add(key, (String) value);
+        } else {
+            builder.add(key, value.toString());
+        }
+        return builder;
+    }
+
+    public static JsonArrayBuilder addTo(JsonArrayBuilder builder, Object 
value) {
+        value = convertJcrValue(value);
+        if (value instanceof Byte || value instanceof Short || value 
instanceof Integer || value instanceof Long) {
+            builder.add(((Number) value).longValue());
+        } else if (value instanceof BigDecimal) {
+            builder.add((BigDecimal) value);
+        } else if (value instanceof BigInteger) {
+            builder.add((BigInteger) value);
+        } else if (value instanceof Boolean) {
+            builder.add((Boolean) value);
+        } else if (value instanceof Float || value instanceof Double) {
+            builder.add(((Number) value).doubleValue());
+        } else if (value instanceof String) {
+            builder.add((String) value);
+        } else {
+            builder.add(value.toString());
+        }
+        return builder;
+    }
+
+    private static Object convertJcrValue(Object value) {
+        if (value instanceof Value) {
+            try {
+                Value jcrValue = (Value)value;
+                int valueType = jcrValue.getType();
+                if (valueType == PropertyType.DOUBLE) {
+                    value = jcrValue.getDouble();
+                } else if (valueType == PropertyType.DECIMAL) {
+                    value = jcrValue.getDecimal();
+                } else if (valueType == PropertyType.LONG) {
+                    value = jcrValue.getLong();
+                } else if (valueType == PropertyType.BOOLEAN) {
+                    value = jcrValue.getBoolean();
+                } else {
+                    value = jcrValue.getString();
+                }
+            } catch (RepositoryException re) {
+                // should never get here
+            }
+        }
+        return value;
+    }
+
+}
diff --git 
a/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/AbstractAccessGetServlet.java
 
b/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/AbstractAccessGetServlet.java
new file mode 100644
index 0000000..04968bf
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/AbstractAccessGetServlet.java
@@ -0,0 +1,187 @@
+/*
+ * 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.post;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.AccessDeniedException;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.security.Privilege;
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.stream.JsonGenerator;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry;
+import org.apache.jackrabbit.api.security.principal.PrincipalManager;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionDefinition;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.SlingHttpServletResponse;
+import org.apache.sling.api.resource.ResourceNotFoundException;
+import org.apache.sling.api.servlets.SlingAllMethodsServlet;
+import org.apache.sling.jcr.base.util.AccessControlUtil;
+import org.apache.sling.jcr.jackrabbit.accessmanager.LocalPrivilege;
+import org.apache.sling.jcr.jackrabbit.accessmanager.LocalRestriction;
+import org.apache.sling.jcr.jackrabbit.accessmanager.impl.PrivilegesHelper;
+import org.jetbrains.annotations.NotNull;
+
+@SuppressWarnings("serial")
+public abstract class AbstractAccessGetServlet extends SlingAllMethodsServlet {
+
+    private transient RestrictionProvider restrictionProvider;
+
+    // @Reference
+    protected void bindRestrictionProvider(RestrictionProvider rp) {
+        this.restrictionProvider = rp;
+    }
+
+    /**
+     * Return the RestrictionProvider service
+     */
+    protected RestrictionProvider getRestrictionProvider() {
+        return restrictionProvider;
+    }
+
+    /* (non-Javadoc)
+     * @see 
org.apache.sling.api.servlets.SlingSafeMethodsServlet#doGet(org.apache.sling.api.SlingHttpServletRequest,
 org.apache.sling.api.SlingHttpServletResponse)
+     */
+    @Override
+    protected void doGet(SlingHttpServletRequest request,
+            SlingHttpServletResponse response) throws ServletException,
+            IOException {
+
+        try {
+            Session session = 
request.getResourceResolver().adaptTo(Session.class);
+            String resourcePath = request.getResource().getPath();
+            String principalId = request.getParameter("pid");
+
+            JsonObject jsonObj = internalJson(session, resourcePath, 
principalId);
+            response.setContentType("application/json");
+            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
+
+            boolean isTidy = false;
+            final String[] selectors = 
request.getRequestPathInfo().getSelectors();
+            if (selectors.length > 0) {
+                for (final String level : selectors) {
+                    if("tidy".equals(level)) {
+                        isTidy = true;
+                        break;
+                    }
+                }
+            }
+
+            Map<String, Object> options = new HashMap<>();
+            options.put(JsonGenerator.PRETTY_PRINTING, isTidy);
+            try (JsonGenerator generator = 
Json.createGeneratorFactory(options).createGenerator(response.getWriter())) {
+                generator.write(jsonObj).flush();
+            }
+        } catch (AccessDeniedException ade) {
+            response.sendError(HttpServletResponse.SC_NOT_FOUND);
+        } catch (ResourceNotFoundException rnfe) {
+            response.sendError(HttpServletResponse.SC_NOT_FOUND, 
rnfe.getMessage());
+        } catch (Exception throwable) {
+            throw new ServletException(String.format("Exception while handling 
GET %s with %s",
+                                            request.getResource().getPath(), 
getClass().getName()),
+                                        throwable);
+        }
+    }
+
+    protected abstract JsonObject internalJson(Session session, String 
resourcePath, String principalId) throws RepositoryException;
+
+    /**
+     * Verify that the user supplied arguments are valid
+     * 
+     * @param jcrSession the JCR session
+     * @param resourcePath the resource path
+     * @param principalId the principal id
+     * @return the principal for the requested principalId
+     */
+    protected @NotNull Principal validateArgs(Session jcrSession, String 
resourcePath, String principalId) throws RepositoryException {
+        validateArgs(jcrSession, resourcePath);
+
+        if (principalId == null) {
+            throw new RepositoryException("principalId was not submitted.");
+        }
+
+        // validate that the submitted name is valid
+        PrincipalManager principalManager = 
AccessControlUtil.getPrincipalManager(jcrSession);
+        Principal principal = principalManager.getPrincipal(principalId);
+        if (principal == null) {
+            throw new RepositoryException("Invalid principalId was 
submitted.");
+        }
+
+        return principal;
+    }
+
+    /**
+     * Verify that the user supplied arguments are valid
+     * 
+     * @param jcrSession the JCR session
+     * @param resourcePath the resource path
+     * @param principalId the principal id
+     * @return the principal for the requested principalId
+     */
+    protected @NotNull void validateArgs(Session jcrSession, String 
resourcePath) throws RepositoryException {
+        if (jcrSession == null) {
+            throw new RepositoryException("JCR Session not found");
+        }
+
+        if (resourcePath == null) {
+            throw new ResourceNotFoundException("Resource path was not 
supplied.");
+        }
+
+        if (!jcrSession.nodeExists(resourcePath)) {
+            throw new ResourceNotFoundException("Resource is not a JCR Node");
+        }
+    }
+
+    protected void processACE(Map<String, RestrictionDefinition> srMap,
+            JackrabbitAccessControlEntry jrAccessControlEntry, Privilege[] 
privileges,
+            Map<Privilege, LocalPrivilege> map) throws RepositoryException {
+        boolean isAllow = jrAccessControlEntry.isAllow();
+        // populate the declared restrictions
+        @NotNull
+        String[] restrictionNames = jrAccessControlEntry.getRestrictionNames();
+        Set<LocalRestriction> restrictionItems = new HashSet<>();
+        for (String restrictionName : restrictionNames) {
+            RestrictionDefinition rd = srMap.get(restrictionName);
+            boolean isMulti = rd.getRequiredType().isArray();
+            if (isMulti) {
+                restrictionItems.add(new LocalRestriction(rd, 
jrAccessControlEntry.getRestrictions(restrictionName)));
+            } else {
+                restrictionItems.add(new LocalRestriction(rd, 
jrAccessControlEntry.getRestriction(restrictionName)));
+            }
+        }
+
+        if (isAllow) {
+            PrivilegesHelper.allow(map, restrictionItems, 
Arrays.asList(privileges));
+        } else {
+            PrivilegesHelper.deny(map, restrictionItems, 
Arrays.asList(privileges));
+        }
+    }
+
+}
diff --git 
a/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/AbstractGetAceServlet.java
 
b/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/AbstractGetAceServlet.java
new file mode 100644
index 0000000..46a5ddf
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/AbstractGetAceServlet.java
@@ -0,0 +1,89 @@
+/*
+ * 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.post;
+
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.security.AccessControlEntry;
+import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.Privilege;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionDefinition;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+import org.apache.sling.api.resource.ResourceNotFoundException;
+import org.apache.sling.jcr.base.util.AccessControlUtil;
+import org.apache.sling.jcr.jackrabbit.accessmanager.LocalPrivilege;
+import org.apache.sling.jcr.jackrabbit.accessmanager.impl.JsonConvert;
+import org.apache.sling.jcr.jackrabbit.accessmanager.impl.PrivilegesHelper;
+
+@SuppressWarnings("serial")
+public abstract class AbstractGetAceServlet extends AbstractAccessGetServlet {
+
+    @Override
+    protected JsonObject internalJson(Session session, String resourcePath, 
String principalId) throws RepositoryException {
+        return internalGetAce(session, resourcePath, principalId);
+    }
+
+    protected JsonObject internalGetAce(Session jcrSession, String 
resourcePath, String principalId) throws RepositoryException {
+        Principal principal = validateArgs(jcrSession, resourcePath, 
principalId);
+
+        AccessControlEntry[] accessControlEntries = 
getAccessControlEntries(jcrSession, resourcePath, principal);
+        if (accessControlEntries == null || accessControlEntries.length == 0) {
+            throw new ResourceNotFoundException(resourcePath, "No access 
control entries were found");
+        }
+
+        //make a temp map for quick lookup below
+        Set<RestrictionDefinition> supportedRestrictions = 
getRestrictionProvider().getSupportedRestrictions(resourcePath);
+        Map<String, RestrictionDefinition> srMap = new HashMap<>();
+        for (RestrictionDefinition restrictionDefinition : 
supportedRestrictions) {
+            srMap.put(restrictionDefinition.getName(), restrictionDefinition);
+        }
+
+        Map<Privilege, LocalPrivilege> privilegeToLocalPrivilegesMap = new 
HashMap<>();
+        //evaluate these in reverse order so the entries with highest 
specificity are processed last
+        for (int i = accessControlEntries.length - 1; i >= 0; i--) {
+            AccessControlEntry accessControlEntry = accessControlEntries[i];
+            if (accessControlEntry instanceof JackrabbitAccessControlEntry) {
+                JackrabbitAccessControlEntry jrAccessControlEntry = 
(JackrabbitAccessControlEntry)accessControlEntry;
+                Privilege[] privileges = jrAccessControlEntry.getPrivileges();
+                if (privileges != null) {
+                    processACE(srMap, jrAccessControlEntry, privileges, 
privilegeToLocalPrivilegesMap);
+                }
+            }
+        }
+
+        // combine any aggregates that are still valid
+        AccessControlManager acm = 
AccessControlUtil.getAccessControlManager(jcrSession);
+        Map<Privilege, Integer> privilegeLongestDepthMap = 
PrivilegesHelper.buildPrivilegeLongestDepthMap(acm.privilegeFromName(PrivilegeConstants.JCR_ALL));
+        PrivilegesHelper.consolidateAggregates(acm, resourcePath, 
privilegeToLocalPrivilegesMap, privilegeLongestDepthMap);
+
+        // convert the data to JSON
+        JsonObjectBuilder jsonObj = JsonConvert.convertToJson(principal, 
privilegeToLocalPrivilegesMap, -1);
+        return jsonObj.build();
+    }
+
+    protected abstract AccessControlEntry[] getAccessControlEntries(Session 
session, String absPath, Principal principal) throws RepositoryException;
+
+}
diff --git 
a/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/AbstractGetAclServlet.java
 
b/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/AbstractGetAclServlet.java
index 9b56f91..0328d45 100644
--- 
a/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/AbstractGetAclServlet.java
+++ 
b/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/AbstractGetAclServlet.java
@@ -16,25 +16,17 @@
  */
 package org.apache.sling.jcr.jackrabbit.accessmanager.post;
 
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
 import java.security.Principal;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 
-import javax.jcr.AccessDeniedException;
-import javax.jcr.Item;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
-import javax.jcr.Value;
 import javax.jcr.security.AccessControlEntry;
 import javax.jcr.security.AccessControlManager;
 import javax.jcr.security.Privilege;
@@ -42,112 +34,43 @@ import javax.json.Json;
 import javax.json.JsonArrayBuilder;
 import javax.json.JsonObject;
 import javax.json.JsonObjectBuilder;
-import javax.json.stream.JsonGenerator;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletResponse;
 
 import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry;
 import 
org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionDefinition;
-import 
org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider;
 import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
-import org.apache.sling.api.SlingHttpServletRequest;
-import org.apache.sling.api.SlingHttpServletResponse;
-import org.apache.sling.api.resource.ResourceNotFoundException;
-import org.apache.sling.api.servlets.SlingAllMethodsServlet;
 import org.apache.sling.jcr.base.util.AccessControlUtil;
 import org.apache.sling.jcr.jackrabbit.accessmanager.LocalPrivilege;
 import org.apache.sling.jcr.jackrabbit.accessmanager.LocalRestriction;
+import org.apache.sling.jcr.jackrabbit.accessmanager.impl.JsonConvert;
 import org.apache.sling.jcr.jackrabbit.accessmanager.impl.PrivilegesHelper;
-import org.jetbrains.annotations.NotNull;
 
 @SuppressWarnings("serial")
-public abstract class AbstractGetAclServlet extends SlingAllMethodsServlet {
+public abstract class AbstractGetAclServlet extends AbstractAccessGetServlet {
 
-    protected static final String KEY_PRINCIPAL = "principal";
-    protected static final String KEY_ORDER = "order";
-    protected static final String KEY_PRIVILEGES = "privileges";
-    protected static final String KEY_ALLOW = "allow";
-    protected static final String KEY_DENY = "deny";
     /**
-     * @deprecated since 3.0.12, To be removed before the exported package 
version goes to 4.0
+     * @deprecated since 3.0.12, To be removed when the exported package 
version goes to 4.0
+     *      use {@link JsonConvert#KEY_ORDER} instead
      */
     @Deprecated
-    protected static final String KEY_DENIED = "denied";
+    protected static final String KEY_ORDER = JsonConvert.KEY_ORDER;
     /**
-     * @deprecated since 3.0.12, To be removed before the exported package 
version goes to 4.0
+     * @deprecated since 3.0.12, To be removed when the exported package 
version goes to 4.0
      */
     @Deprecated
-    protected static final String KEY_GRANTED = "granted";
-
-    private transient RestrictionProvider restrictionProvider;
-
-    // @Reference
-    protected void bindRestrictionProvider(RestrictionProvider rp) {
-        this.restrictionProvider = rp;
-    }
-
+    protected static final String KEY_DENIED = "denied";
     /**
-     * Return the RestrictionProvider service
+     * @deprecated since 3.0.12, To be removed when the exported package 
version goes to 4.0
      */
-    protected RestrictionProvider getRestrictionProvider() {
-        return restrictionProvider;
-    }
+    @Deprecated
+    protected static final String KEY_GRANTED = "granted";
 
-    /* (non-Javadoc)
-     * @see 
org.apache.sling.api.servlets.SlingSafeMethodsServlet#doGet(org.apache.sling.api.SlingHttpServletRequest,
 org.apache.sling.api.SlingHttpServletResponse)
-     */
     @Override
-    protected void doGet(SlingHttpServletRequest request,
-            SlingHttpServletResponse response) throws ServletException,
-            IOException {
-
-        try {
-            Session session = 
request.getResourceResolver().adaptTo(Session.class);
-            String resourcePath = request.getResource().getPath();
-
-            JsonObject acl = internalGetAcl(session, resourcePath);
-            response.setContentType("application/json");
-            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
-
-            boolean isTidy = false;
-            final String[] selectors = 
request.getRequestPathInfo().getSelectors();
-            if (selectors.length > 0) {
-                for (final String level : selectors) {
-                    if("tidy".equals(level)) {
-                        isTidy = true;
-                        break;
-                    }
-                }
-            }
-
-            Map<String, Object> options = new HashMap<>();
-            options.put(JsonGenerator.PRETTY_PRINTING, isTidy);
-            try (JsonGenerator generator = 
Json.createGeneratorFactory(options).createGenerator(response.getWriter())) {
-                generator.write(acl).flush();
-            }
-        } catch (AccessDeniedException ade) {
-            response.sendError(HttpServletResponse.SC_NOT_FOUND);
-        } catch (ResourceNotFoundException rnfe) {
-            response.sendError(HttpServletResponse.SC_NOT_FOUND, 
rnfe.getMessage());
-        } catch (Exception throwable) {
-            throw new ServletException(String.format("Exception while handling 
GET %s with %s",
-                                            request.getResource().getPath(), 
getClass().getName()),
-                                        throwable);
-        }
+    protected JsonObject internalJson(Session session, String resourcePath, 
String principalId) throws RepositoryException {
+        return internalGetAcl(session, resourcePath);
     }
 
     protected JsonObject internalGetAcl(Session jcrSession, String 
resourcePath) throws RepositoryException {
-
-        if (jcrSession == null) {
-            throw new RepositoryException("JCR Session not found");
-        }
-
-        Item item = jcrSession.getItem(resourcePath);
-        if (item != null) {
-            resourcePath = item.getPath();
-        } else {
-            throw new ResourceNotFoundException("Resource is not a JCR Node");
-        }
+        validateArgs(jcrSession, resourcePath);
 
         //make a temp map for quick lookup below
         Set<RestrictionDefinition> supportedRestrictions = 
getRestrictionProvider().getSupportedRestrictions(resourcePath);
@@ -166,29 +89,11 @@ public abstract class AbstractGetAclServlet extends 
SlingAllMethodsServlet {
                 JackrabbitAccessControlEntry jrAccessControlEntry = 
(JackrabbitAccessControlEntry)accessControlEntry;
                 Privilege[] privileges = jrAccessControlEntry.getPrivileges();
                 if (privileges != null) {
-                    boolean isAllow = jrAccessControlEntry.isAllow();
                     Principal principal = accessControlEntry.getPrincipal();
                     principalToOrderMap.put(principal, i);
                     Map<Privilege, LocalPrivilege> map = 
principalToPrivilegesMap.computeIfAbsent(principal, k -> new HashMap<>());
-                    // populate the declared restrictions
-                    @NotNull
-                    String[] restrictionNames = 
jrAccessControlEntry.getRestrictionNames();
-                    Set<LocalRestriction> restrictionItems = new HashSet<>();
-                    for (String restrictionName : restrictionNames) {
-                        RestrictionDefinition rd = srMap.get(restrictionName);
-                        boolean isMulti = rd.getRequiredType().isArray();
-                        if (isMulti) {
-                            restrictionItems.add(new LocalRestriction(rd, 
jrAccessControlEntry.getRestrictions(restrictionName)));
-                        } else {
-                            restrictionItems.add(new LocalRestriction(rd, 
jrAccessControlEntry.getRestriction(restrictionName)));
-                        }
-                    }
 
-                    if (isAllow) {
-                        PrivilegesHelper.allow(map, restrictionItems, 
Arrays.asList(privileges));
-                    } else {
-                        PrivilegesHelper.deny(map, restrictionItems, 
Arrays.asList(privileges));
-                    }
+                    processACE(srMap, jrAccessControlEntry, privileges, map);
                 }
             }
         }
@@ -211,85 +116,39 @@ public abstract class AbstractGetAclServlet extends 
SlingAllMethodsServlet {
         return jsonObj.build();
     }
 
-
     protected JsonObjectBuilder convertToJson(List<Entry<Principal, 
Map<Privilege, LocalPrivilege>>> entrySetList) {
         JsonObjectBuilder jsonObj = Json.createObjectBuilder();
         for (int i = 0; i < entrySetList.size(); i++) {
             Entry<Principal, Map<Privilege, LocalPrivilege>> entry = 
entrySetList.get(i);
             Principal principal = entry.getKey();
-            JsonObjectBuilder principalObj = Json.createObjectBuilder();
-            principalObj.add(KEY_PRINCIPAL, principal.getName());
-            principalObj.add(KEY_ORDER, i);
-            JsonObjectBuilder privilegesObj = Json.createObjectBuilder();
-            Collection<LocalPrivilege> privileges = entry.getValue().values();
-            for (LocalPrivilege pi : privileges) {
-                if (pi.isNone()) {
-                    continue;
-                }
-                JsonObjectBuilder privilegeObj = Json.createObjectBuilder();
-
-                if (pi.isAllow()) {
-                    addRestrictions(privilegeObj, KEY_ALLOW, 
pi.getAllowRestrictions());
-                }
-                if (pi.isDeny()) {
-                    addRestrictions(privilegeObj, KEY_DENY, 
pi.getDenyRestrictions());
-                }
-                privilegesObj.add(pi.getName(), privilegeObj);
-            }
-            principalObj.add(KEY_PRIVILEGES, privilegesObj);
+            JsonObjectBuilder principalObj = 
JsonConvert.convertToJson(entry.getKey(), entry.getValue(), i);
             jsonObj.add(principal.getName(), principalObj);
         }
         return jsonObj;
     }
 
+    /**
+     * @deprecated use {@link JsonConvert#addRestrictions(JsonObjectBuilder, 
String, Set)} instead
+     */
+    @Deprecated
     protected void addRestrictions(JsonObjectBuilder privilegeObj, String key, 
Set<LocalRestriction> restrictions) {
-        if (restrictions.isEmpty()) {
-            privilegeObj.add(key, true);
-        } else {
-            JsonObjectBuilder allowObj = Json.createObjectBuilder();
-            for (LocalRestriction ri : restrictions) {
-                if (ri.isMultiValue()) {
-                    JsonArrayBuilder rvalues = Json.createArrayBuilder();
-                    for (Value value: ri.getValues()) {
-                        addTo(rvalues, value);
-                    }
-                    allowObj.add(ri.getName(), rvalues);
-                } else {
-                    addTo(allowObj, ri.getName(), ri.getValue());
-                }
-            }
-            privilegeObj.add(key, allowObj);
-        }
+        JsonConvert.addRestrictions(privilegeObj, key, restrictions);
     }
 
+    /**
+     * @deprecated use {@link JsonConvert#addTo(javax.json.JsonArrayBuilder, 
Object)} instead
+     */
+    @Deprecated
     protected JsonObjectBuilder addTo(JsonObjectBuilder builder, String key, 
Object value) {
-        if (value instanceof Byte || value instanceof Short || value 
instanceof Integer || value instanceof Long) {
-            builder.add(key, ((Number) value).longValue());
-        } else if (value instanceof Float || value instanceof Double) {
-            builder.add(key, ((Number) value).doubleValue());
-        } else if (value instanceof Privilege) {
-            JsonObjectBuilder privilegeBuilder = Json.createObjectBuilder();
-            privilegeBuilder.add("name", ((Privilege) value).getName());
-            builder.add(key, privilegeBuilder);
-        } else if (value instanceof String) {
-            builder.add(key, (String) value);
-        } else {
-            builder.add(key, value.toString());
-        }
-        return builder;
+        return JsonConvert.addTo(builder, key, value);
     }
 
+    /**
+     * @deprecated use {@link JsonConvert#addTo(JsonObjectBuilder, String, 
Object)} instead
+     */
+    @Deprecated
     protected JsonArrayBuilder addTo(JsonArrayBuilder builder, Object value) {
-        if (value instanceof Byte || value instanceof Short || value 
instanceof Integer || value instanceof Long) {
-            builder.add(((Number) value).longValue());
-        } else if (value instanceof Float || value instanceof Double) {
-            builder.add(((Number) value).doubleValue());
-        } else if (value instanceof String) {
-            builder.add((String) value);
-        } else {
-            builder.add(value.toString());
-        }
-        return builder;
+        return JsonConvert.addTo(builder, value);
     }
 
     protected abstract AccessControlEntry[] getAccessControlEntries(Session 
session, String absPath) throws RepositoryException;
diff --git 
a/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/GetAceServlet.java
 
b/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/GetAceServlet.java
new file mode 100644
index 0000000..82e71de
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/GetAceServlet.java
@@ -0,0 +1,112 @@
+/*
+ * 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.post;
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.security.AccessControlEntry;
+import javax.jcr.security.AccessControlList;
+import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.AccessControlPolicy;
+import javax.json.JsonObject;
+import javax.servlet.Servlet;
+
+import 
org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider;
+import org.apache.sling.jcr.base.util.AccessControlUtil;
+import org.apache.sling.jcr.jackrabbit.accessmanager.GetAce;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * <p>
+ * Sling Get Servlet implementation for getting the ACE for a principal on a 
JCR
+ * resource.
+ * </p>
+ * <h2>Rest Service Description</h2>
+ * <p>
+ * Get a principal's ACE for the node identified as a resource by the request
+ * URL &gt;resource&lt;.ace.json?pid=[principal_id]
+ * </p>
+ * <h3>Transport Details:</h3>
+ * <h4>Methods</h4>
+ * <ul>
+ * <li>GET</li>
+ * </ul>
+ * <h4>Get Parameters</h4>
+ * <dl>
+ * <dt>pid</dt>
+ * <dd>The principal id of the ACE to get in the ACL specified by the 
path.</dd>
+ * </dl>
+ *
+ * <h4>Response</h4>
+ * <dl>
+ * <dt>200</dt>
+ * <dd>Success.</dd>
+ * <dt>404</dt>
+ * <dd>The resource was not found or no access control entries exist for the 
principal.</dd>
+ * <dt>500</dt>
+ * <dd>Failure. JSON explains the failure.</dd>
+ * </dl>
+ */
+@Component(service = {Servlet.class, GetAce.class},
+property= {
+        "sling.servlet.resourceTypes=sling/servlet/default",
+        "sling.servlet.methods=GET",
+        "sling.servlet.selectors=ace",
+        "sling.servlet.selectors=tidy.ace",
+        "sling.servlet.extensions=json",
+        "sling.servlet.prefix:Integer=-1"
+},
+reference = {
+        @Reference(name="RestrictionProvider",
+                bind = "bindRestrictionProvider",
+                service = RestrictionProvider.class)
+}
+)
+public class GetAceServlet extends AbstractGetAceServlet implements GetAce {
+    private static final long serialVersionUID = 1654062732084983394L;
+
+    @Override
+    public JsonObject getAce(Session jcrSession, String resourcePath, String 
principalId)
+            throws RepositoryException {
+        return internalGetAce(jcrSession, resourcePath, principalId);
+    }
+
+    @Override
+    protected AccessControlEntry[] getAccessControlEntries(Session session, 
String absPath, Principal principal) throws RepositoryException {
+        AccessControlManager acMgr = 
AccessControlUtil.getAccessControlManager(session);
+        AccessControlPolicy[] policies = acMgr.getPolicies(absPath);
+        List<AccessControlEntry> allEntries = new ArrayList<>(); 
+        for (AccessControlPolicy accessControlPolicy : policies) {
+            if (accessControlPolicy instanceof AccessControlList) {
+                AccessControlEntry[] accessControlEntries = 
((AccessControlList)accessControlPolicy).getAccessControlEntries();
+                Stream.of(accessControlEntries)
+                    .filter(entry -> principal.equals(entry.getPrincipal()))
+                    .forEach(allEntries::add);
+            }
+        }
+        return allEntries.toArray(new AccessControlEntry[allEntries.size()]);
+    }
+
+}
diff --git 
a/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/GetEffectiveAceServlet.java
 
b/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/GetEffectiveAceServlet.java
new file mode 100644
index 0000000..5d89d63
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/GetEffectiveAceServlet.java
@@ -0,0 +1,112 @@
+/*
+ * 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.post;
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.security.AccessControlEntry;
+import javax.jcr.security.AccessControlList;
+import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.AccessControlPolicy;
+import javax.json.JsonObject;
+import javax.servlet.Servlet;
+
+import 
org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider;
+import org.apache.sling.jcr.base.util.AccessControlUtil;
+import org.apache.sling.jcr.jackrabbit.accessmanager.GetEffectiveAce;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * <p>
+ * Sling Get Servlet implementation for getting the effective ACE for a 
principal on a JCR
+ * resource.
+ * </p>
+ * <h2>Rest Service Description</h2>
+ * <p>
+ * Get a principal's ACE for the node identified as a resource by the request
+ * URL &gt;resource&lt;.eace.json?pid=[principal_id]
+ * </p>
+ * <h3>Transport Details:</h3>
+ * <h4>Methods</h4>
+ * <ul>
+ * <li>GET</li>
+ * </ul>
+ * <h4>Get Parameters</h4>
+ * <dl>
+ * <dt>pid</dt>
+ * <dd>The principal id of the ACE to get in the effective ACL specified by 
the path.</dd>
+ * </dl>
+ *
+ * <h4>Response</h4>
+ * <dl>
+ * <dt>200</dt>
+ * <dd>Success.</dd>
+ * <dt>404</dt>
+ * <dd>The resource was not found or no access control entries exist for the 
principal.</dd>
+ * <dt>500</dt>
+ * <dd>Failure. JSON explains the failure.</dd>
+ * </dl>
+ */
+@Component(service = {Servlet.class, GetEffectiveAce.class},
+property= {
+        "sling.servlet.resourceTypes=sling/servlet/default",
+        "sling.servlet.methods=GET",
+        "sling.servlet.selectors=eace",
+        "sling.servlet.selectors=tidy.eace",
+        "sling.servlet.extensions=json",
+        "sling.servlet.prefix:Integer=-1"
+},
+reference = {
+        @Reference(name="RestrictionProvider",
+                bind = "bindRestrictionProvider",
+                service = RestrictionProvider.class)
+}
+)
+public class GetEffectiveAceServlet extends AbstractGetAceServlet implements 
GetEffectiveAce {
+    private static final long serialVersionUID = 1654062732084983394L;
+
+    @Override
+    public JsonObject getEffectiveAce(Session jcrSession, String resourcePath, 
String principalId)
+            throws RepositoryException {
+        return internalGetAce(jcrSession, resourcePath, principalId);
+    }
+
+    @Override
+    protected AccessControlEntry[] getAccessControlEntries(Session session, 
String absPath, Principal principal) throws RepositoryException {
+        AccessControlManager acMgr = 
AccessControlUtil.getAccessControlManager(session);
+        AccessControlPolicy[] policies = acMgr.getEffectivePolicies(absPath);
+        List<AccessControlEntry> allEntries = new ArrayList<>(); 
+        for (AccessControlPolicy policy : policies) {
+            if (policy instanceof AccessControlList) {
+                AccessControlEntry[] accessControlEntries = 
((AccessControlList)policy).getAccessControlEntries();
+                Stream.of(accessControlEntries)
+                    .filter(entry -> principal.equals(entry.getPrincipal()))
+                    .forEach(allEntries::add);
+            }
+        }
+        return allEntries.toArray(new AccessControlEntry[allEntries.size()]);
+    }
+
+}
diff --git 
a/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/impl/JsonConvertTest.java
 
b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/impl/JsonConvertTest.java
new file mode 100644
index 0000000..1ad476f
--- /dev/null
+++ 
b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/impl/JsonConvertTest.java
@@ -0,0 +1,309 @@
+/*
+ * 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.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.Privilege;
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+
+import 
org.apache.jackrabbit.oak.security.authorization.restriction.RestrictionProviderImpl;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionDefinition;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+import org.apache.jackrabbit.value.ValueFactoryImpl;
+import org.apache.sling.jcr.base.util.AccessControlUtil;
+import org.apache.sling.jcr.jackrabbit.accessmanager.LocalPrivilege;
+import org.apache.sling.jcr.jackrabbit.accessmanager.LocalRestriction;
+import org.apache.sling.testing.mock.sling.ResourceResolverType;
+import org.apache.sling.testing.mock.sling.junit.SlingContext;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class JsonConvertTest {
+
+    @Rule
+    public final SlingContext context = new 
SlingContext(ResourceResolverType.JCR_OAK);
+
+    private AccessControlManager acm;
+
+    private Map<String, RestrictionDefinition> srMap;
+
+    @Before
+    public void buildPrivilegesMap() throws RepositoryException {
+        context.registerService(new RestrictionProviderImpl());
+        Session session = context.resourceResolver().adaptTo(Session.class);
+        acm = AccessControlUtil.getAccessControlManager(session);
+    }
+
+    private Privilege priv(String privilegeName) throws RepositoryException {
+        return acm.privilegeFromName(privilegeName);
+    }
+
+    private RestrictionDefinition rd(String restrictionName) {
+        if (srMap == null) {
+            //make a temp map for quick lookup below
+            RestrictionProvider restrictionProvider = 
context.getService(RestrictionProvider.class);
+            Set<RestrictionDefinition> supportedRestrictions = 
restrictionProvider.getSupportedRestrictions("/");
+            srMap = new HashMap<>();
+            for (RestrictionDefinition restrictionDefinition : 
supportedRestrictions) {
+                srMap.put(restrictionDefinition.getName(), 
restrictionDefinition);
+            }
+        }
+        return srMap.get(restrictionName);
+    }
+
+    private Value val(String value) {
+        return ValueFactoryImpl.getInstance().createValue(value);
+    }
+    private Value[] vals(String ... value) {
+        Value[] values = new Value[value.length];
+        ValueFactory vf = ValueFactoryImpl.getInstance();
+        for (int i = 0; i < value.length; i++) {
+            values[i] = vf.createValue(value[i]);
+        }
+        return values;
+    }
+
+    /**
+     * Test method for {@link 
org.apache.sling.jcr.jackrabbit.accessmanager.impl.JsonConvert#convertToJson(java.security.Principal,
 java.util.Map, int)}.
+     */
+    @Test
+    public void testConvertToJson() throws RepositoryException {
+        Principal principal = new PrincipalImpl("testuser");
+        LocalPrivilege lp1 = new 
LocalPrivilege(priv(PrivilegeConstants.JCR_READ));
+        lp1.setAllow(true);
+        LocalPrivilege lp2 = new 
LocalPrivilege(priv(PrivilegeConstants.JCR_WRITE));
+        lp2.setDeny(true);
+        LocalPrivilege lp3 = new 
LocalPrivilege(priv(PrivilegeConstants.JCR_READ_ACCESS_CONTROL));
+        LocalPrivilege lp4 = new 
LocalPrivilege(priv(PrivilegeConstants.JCR_NODE_TYPE_MANAGEMENT));
+        lp4.setAllow(true);
+        lp4.setAllowRestrictions(Collections.singleton(new 
LocalRestriction(rd(AccessControlConstants.REP_GLOB), val("/hello"))));
+        LocalPrivilege lp5 = new 
LocalPrivilege(priv(PrivilegeConstants.JCR_MODIFY_ACCESS_CONTROL));
+        lp5.setDeny(true);
+        lp5.setDenyRestrictions(Collections.singleton(new 
LocalRestriction(rd(AccessControlConstants.REP_ITEM_NAMES), vals("item1", 
"item2"))));
+        Map<Privilege, LocalPrivilege> entry = new HashMap<>();
+        entry.put(lp1.getPrivilege(), lp1);
+        entry.put(lp2.getPrivilege(), lp2);
+        entry.put(lp3.getPrivilege(), lp3);
+        entry.put(lp4.getPrivilege(), lp4);
+        entry.put(lp5.getPrivilege(), lp5);
+        int order = 1;
+        JsonObjectBuilder principalObj = JsonConvert.convertToJson(principal, 
entry, order);
+        assertNotNull(principalObj);
+        JsonObject build = principalObj.build();
+        assertEquals("testuser", build.getString(JsonConvert.KEY_PRINCIPAL));
+        assertEquals(1, build.getInt(JsonConvert.KEY_ORDER));
+        JsonObject privilegesObj = 
build.getJsonObject(JsonConvert.KEY_PRIVILEGES);
+        assertNotNull(privilegesObj);
+        assertEquals(4, privilegesObj.size());
+
+        JsonValue jsonValue1 = privilegesObj.get(PrivilegeConstants.JCR_READ);
+        assertTrue(jsonValue1 instanceof JsonObject);
+        assertTrue(((JsonObject)jsonValue1).getBoolean(JsonConvert.KEY_ALLOW));
+
+        JsonValue jsonValue2 = privilegesObj.get(PrivilegeConstants.JCR_WRITE);
+        assertTrue(jsonValue2 instanceof JsonObject);
+        assertTrue(((JsonObject)jsonValue2).getBoolean(JsonConvert.KEY_DENY));
+
+        JsonValue jsonValue4 = 
privilegesObj.get(PrivilegeConstants.JCR_NODE_TYPE_MANAGEMENT);
+        assertTrue(jsonValue4 instanceof JsonObject);
+        JsonObject allowObj4 = 
((JsonObject)jsonValue4).getJsonObject(JsonConvert.KEY_ALLOW);
+        assertNotNull(allowObj4);
+        Object globRestrictionObj4 = 
allowObj4.get(AccessControlConstants.REP_GLOB);
+        assertTrue(globRestrictionObj4 instanceof JsonString);
+        assertEquals("/hello", ((JsonString)globRestrictionObj4).getString());
+
+        JsonValue jsonValue5 = 
privilegesObj.get(PrivilegeConstants.JCR_MODIFY_ACCESS_CONTROL);
+        assertTrue(jsonValue5 instanceof JsonObject);
+        JsonObject allowObj5 = 
((JsonObject)jsonValue5).getJsonObject(JsonConvert.KEY_DENY);
+        assertNotNull(allowObj5);
+        Object itemNamesRestrictionObj5 = 
allowObj5.get(AccessControlConstants.REP_ITEM_NAMES);
+        assertTrue(itemNamesRestrictionObj5 instanceof JsonArray);
+        assertEquals("item1", 
((JsonArray)itemNamesRestrictionObj5).getString(0));
+        assertEquals("item2", 
((JsonArray)itemNamesRestrictionObj5).getString(1));
+    }
+
+    /**
+     * Test method for {@link 
org.apache.sling.jcr.jackrabbit.accessmanager.impl.JsonConvert#addRestrictions(javax.json.JsonObjectBuilder,
 java.lang.String, java.util.Set)}.
+     */
+    @Test
+    public void testAddRestrictions() {
+        JsonObjectBuilder privilegeObj = Json.createObjectBuilder();
+        String key = JsonConvert.KEY_ALLOW;
+        Set<LocalRestriction> restrictions = new HashSet<>();
+        restrictions.add(new 
LocalRestriction(rd(AccessControlConstants.REP_GLOB), val("/hello")));
+        restrictions.add(new 
LocalRestriction(rd(AccessControlConstants.REP_ITEM_NAMES), vals("item1", 
"item2")));
+        JsonConvert.addRestrictions(privilegeObj, key, restrictions);
+        JsonObject build = privilegeObj.build();
+        assertNotNull(build);
+        JsonObject allowObj = build.getJsonObject(JsonConvert.KEY_ALLOW);
+        assertNotNull(allowObj);
+        Object globRestrictionObj = 
allowObj.get(AccessControlConstants.REP_GLOB);
+        assertTrue(globRestrictionObj instanceof JsonString);
+        assertEquals("/hello", ((JsonString)globRestrictionObj).getString());
+        Object itemNamesRestrictionObj = 
allowObj.get(AccessControlConstants.REP_ITEM_NAMES);
+        assertTrue(itemNamesRestrictionObj instanceof JsonArray);
+        assertEquals("item1", 
((JsonArray)itemNamesRestrictionObj).getString(0));
+        assertEquals("item2", 
((JsonArray)itemNamesRestrictionObj).getString(1));
+    }
+
+    /**
+     * Test method for {@link 
org.apache.sling.jcr.jackrabbit.accessmanager.impl.JsonConvert#addTo(javax.json.JsonObjectBuilder,
 java.lang.String, java.lang.Object)}.
+     */
+    @Test
+    public void testAddToJsonObjectBuilderStringObject() throws 
RepositoryException {
+        JsonObjectBuilder builder = Json.createObjectBuilder();
+
+        String key = JsonConvert.KEY_ALLOW;
+        Function<JsonObject, Object> doubleFn = json -> 
json.getJsonNumber(key).doubleValue();
+        Function<JsonObject, Object> bigDecimalFn = json -> 
json.getJsonNumber(key).bigDecimalValue();
+        Function<JsonObject, Object> bigIntegerFn = json -> 
json.getJsonNumber(key).bigIntegerValue();
+        Function<JsonObject, Object> longFn = json -> 
json.getJsonNumber(key).longValue();
+        Function<JsonObject, Object> intFn = json -> 
json.getJsonNumber(key).intValue();
+        Function<JsonObject, Object> booleanFn = json -> json.getBoolean(key);
+        Function<JsonObject, Object> stringFn = json -> json.getString(key);
+        Function<JsonObject, Object> privFn = json -> 
json.getJsonObject(key).getString("name");
+
+        ValueFactory vf = ValueFactoryImpl.getInstance();
+        // data to test [label, value, expectedValueFromJson, lookupFromJsonFn]
+        Object[][] candidates = new Object[][] {
+            // JCR Value types
+            {"JCR double Value", vf.createValue((double)1.1), 1.1, doubleFn},
+            {"JCR BigDecimal Value", vf.createValue(new BigDecimal("1.1")), 
new BigDecimal("1.1"), bigDecimalFn},
+            {"JCR long Value", vf.createValue(1L), 1L, longFn},
+            {"JCR boolean Value", vf.createValue(true), true, booleanFn},
+            {"JCR string Value", vf.createValue("hello"), "hello", stringFn},
+
+            // non-JCR values
+            {"byte value", (byte)1, 1, intFn},
+            {"short value", (short)1, 1, intFn},
+            {"int value", 1, 1, intFn},
+            {"long value", 1L, 1L, longFn},
+            {"BigDecimal value", new BigDecimal("1.1"), new BigDecimal("1.1"), 
bigDecimalFn},
+            {"BigInteger value", new BigInteger("1"), new BigInteger("1"), 
bigIntegerFn},
+            {"true boolean value", true, true, booleanFn},
+            {"false boolean value", false, false, booleanFn},
+            {"float value", (float)1.1, (double)1.1, doubleFn},
+            {"double value", 1.1, 1.1, doubleFn},
+            {"string value", "hello", "hello", stringFn},
+            {"privilege value", priv(PrivilegeConstants.JCR_READ), 
PrivilegeConstants.JCR_READ, privFn},
+            {"object value", new StringBuilder().append("hello"), "hello", 
stringFn}
+        };
+        for (Object[] objects : candidates) {
+            Object value = objects[1];
+            JsonConvert.addTo(builder, key, value);
+            JsonObject build = builder.build();
+            assertNotNull(build);
+            assertEquals(1, build.size());
+            @SuppressWarnings("unchecked")
+            Function<JsonObject, Object> supplier = (Function<JsonObject, 
Object>)objects[3];
+            if (objects[2] instanceof Double) {
+                double epsilon = 0.000001d;
+                assertEquals(String.format("%s was not the expected value", 
objects[0]), (double)objects[2], (double)supplier.apply(build), epsilon);
+            } else {
+                assertEquals(String.format("%s was not the expected value", 
objects[0]), objects[2], supplier.apply(build));
+            }
+        }
+    }
+
+    /**
+     * Test method for {@link 
org.apache.sling.jcr.jackrabbit.accessmanager.impl.JsonConvert#addTo(javax.json.JsonArrayBuilder,
 java.lang.Object)}.
+     */
+    @Test
+    public void testAddToJsonArrayBuilderObject() {
+        JsonArrayBuilder builder = Json.createArrayBuilder();
+
+        Function<JsonArray, Object> doubleFn = json -> 
json.getJsonNumber(0).doubleValue();
+        Function<JsonArray, Object> bigDecimalFn = json -> 
json.getJsonNumber(0).bigDecimalValue();
+        Function<JsonArray, Object> bigIntegerFn = json -> 
json.getJsonNumber(0).bigIntegerValue();
+        Function<JsonArray, Object> longFn = json -> 
json.getJsonNumber(0).longValue();
+        Function<JsonArray, Object> intFn = json -> 
json.getJsonNumber(0).intValue();
+        Function<JsonArray, Object> booleanFn = json -> json.getBoolean(0);
+        Function<JsonArray, Object> stringFn = json -> json.getString(0);
+
+        ValueFactory vf = ValueFactoryImpl.getInstance();
+        // data to test [label, value, expectedValueFromJson, lookupFromJsonFn]
+        Object[][] candidates = new Object[][] {
+            // JCR Value types
+            {"JCR double Value", vf.createValue((double)1.1), 1.1, doubleFn},
+            {"JCR BigDecimal Value", vf.createValue(new BigDecimal("1.1")), 
new BigDecimal("1.1"), bigDecimalFn},
+            {"JCR long Value", vf.createValue(1L), 1L, longFn},
+            {"JCR boolean Value", vf.createValue(true), true, booleanFn},
+            {"JCR string Value", vf.createValue("hello"), "hello", stringFn},
+
+            // non-JCR values
+            {"byte value", (byte)1, 1, intFn},
+            {"short value", (short)1, 1, intFn},
+            {"int value", 1, 1, intFn},
+            {"long value", 1L, 1L, longFn},
+            {"BigDecimal value", new BigDecimal("1.1"), new BigDecimal("1.1"), 
bigDecimalFn},
+            {"BigInteger value", new BigInteger("1"), new BigInteger("1"), 
bigIntegerFn},
+            {"true boolean value", true, true, booleanFn},
+            {"false boolean value", false, false, booleanFn},
+            {"float value", (float)1.1, (double)1.1, doubleFn},
+            {"double value", 1.1, 1.1, doubleFn},
+            {"string value", "hello", "hello", stringFn},
+            {"object value", new StringBuilder().append("hello"), "hello", 
stringFn}
+        };
+        for (Object[] objects : candidates) {
+            Object value = objects[1];
+            JsonConvert.addTo(builder, value);
+            JsonArray build = builder.build();
+            assertNotNull(build);
+            assertEquals(1, build.size());
+            @SuppressWarnings("unchecked")
+            Function<JsonArray, Object> supplier = (Function<JsonArray, 
Object>)objects[3];
+            if (objects[2] instanceof Double) {
+                double epsilon = 0.000001d;
+                assertEquals(String.format("%s was not the expected value", 
objects[0]), (double)objects[2], (double)supplier.apply(build), epsilon);
+            } else {
+                assertEquals(String.format("%s was not the expected value", 
objects[0]), objects[2], supplier.apply(build));
+            }
+        }
+    }
+
+}
diff --git 
a/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/GetAceIT.java 
b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/GetAceIT.java
new file mode 100644
index 0000000..4cf608e
--- /dev/null
+++ 
b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/GetAceIT.java
@@ -0,0 +1,138 @@
+/*
+ * 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.util.List;
+
+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.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 'ace' Sling Get Operation
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class GetAceIT extends AccessManagerClientTestSupport {
+
+    protected void commonDeclaredAceForUser(String selector) throws 
IOException {
+        testUserId = createTestUser();
+        testFolderUrl = createTestFolder(null, "sling-tests",
+                "{ \"jcr:primaryType\": \"nt:unstructured\", \"child\" : { 
\"childPropOne\" : true } }");
+
+        //1. create an initial set of privileges
+        List<NameValuePair> postParams = new AcePostParamsBuilder(testUserId)
+                .withPrivilege(PrivilegeConstants.JCR_WRITE, 
PrivilegeValues.ALLOW)
+                .build();
+        addOrUpdateAce(testFolderUrl, postParams);
+
+        Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+
+        //fetch the JSON for the ace to verify the settings.
+        String getUrl = testFolderUrl + "." + selector + ".json?pid=" + 
testUserId;
+
+        String json = getAuthenticatedContent(creds, getUrl, 
CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+        assertNotNull(json);
+        JsonObject aceObject = parseJson(json);
+
+        String principalString = aceObject.getString("principal");
+        assertEquals(testUserId, principalString);
+
+        JsonObject privilegesObject = aceObject.getJsonObject("privileges");
+        assertNotNull(privilegesObject);
+        assertEquals(1, privilegesObject.size());
+        //allow privilege
+        assertPrivilege(privilegesObject, true, PrivilegeValues.ALLOW, 
PrivilegeConstants.JCR_WRITE);
+    }
+
+    /**
+     * ACE servlet returns correct information
+     */
+    @Test
+    public void testDeclaredAceForUser() throws IOException, JsonException {
+        commonDeclaredAceForUser("ace");
+    }
+
+    /**
+     * ACE servlet returns correct information
+     */
+    @Test
+    public void testTidyDeclaredAceForUser() throws IOException, JsonException 
{
+        commonDeclaredAceForUser("tidy.ace");
+    }
+
+    /**
+     * ACE servlet returns 404 when no declared ACE
+     */
+    @Test
+    public void testNoDeclaredAceForUser() throws IOException, JsonException {
+        testUserId = createTestUser();
+        testFolderUrl = createTestFolder(null, "sling-tests",
+                "{ \"jcr:primaryType\": \"nt:unstructured\", \"child\" : { 
\"childPropOne\" : true } }");
+
+        //1. create an initial set of privileges
+        List<NameValuePair> postParams = new AcePostParamsBuilder(testUserId)
+                .withPrivilege(PrivilegeConstants.JCR_WRITE, 
PrivilegeValues.ALLOW)
+                .build();
+        addOrUpdateAce(testFolderUrl, postParams);
+
+        Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+
+        //fetch the JSON for the ace to verify the settings.
+        String getUrl = testFolderUrl + "/child.ace.json?pid=" + testUserId;
+        // no declared access control entry returns a 404
+        assertAuthenticatedHttpStatus(creds, getUrl, 
HttpServletResponse.SC_NOT_FOUND, "Did not expect an ace to be returned");
+    }
+
+    /**
+     * ACE servlet returns 404 when no read access rights permissions
+     */
+    @Test
+    public void testNoAccessToDeclaredAceForUser() throws IOException, 
JsonException {
+        testUserId = createTestUser();
+        testFolderUrl = createTestFolder(null, "sling-tests",
+                "{ \"jcr:primaryType\": \"nt:unstructured\", \"child\" : { 
\"childPropOne\" : true } }");
+
+        //1. create an initial set of privileges
+        List<NameValuePair> postParams = new AcePostParamsBuilder(testUserId)
+                .withPrivilege(PrivilegeConstants.JCR_READ_ACCESS_CONTROL, 
PrivilegeValues.DENY)
+                .build();
+        addOrUpdateAce(testFolderUrl, postParams);
+
+        Credentials creds = new UsernamePasswordCredentials(testUserId, 
"testPwd");
+
+        //fetch the JSON for the ace to verify the settings.
+        String getUrl = testFolderUrl + "/child.ace.json?pid=" + testUserId;
+        // no declared access control entry returns a 404
+        assertAuthenticatedHttpStatus(creds, getUrl, 
HttpServletResponse.SC_NOT_FOUND, "Did not expect an ace to be returned");
+    }
+
+}
diff --git 
a/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/GetAceServiceIT.java
 
b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/GetAceServiceIT.java
new file mode 100644
index 0000000..3cbb1d5
--- /dev/null
+++ 
b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/GetAceServiceIT.java
@@ -0,0 +1,225 @@
+/*
+ * 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.fail;
+
+import java.util.Collections;
+
+import javax.inject.Inject;
+import javax.jcr.Node;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+import javax.json.JsonObject;
+
+import 
org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+import org.apache.sling.api.resource.ResourceNotFoundException;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.jcr.jackrabbit.accessmanager.GetAce;
+import org.apache.sling.jcr.jackrabbit.accessmanager.ModifyAce;
+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;
+
+/**
+ * Tests for the 'modifyAce' inproc service
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class GetAceServiceIT extends AccessManagerClientTestSupport {
+
+    @Inject
+    private ModifyAce modifyAce;
+
+    @Inject
+    private GetAce getAce;
+
+    @Inject
+    protected SlingRepository repository;
+
+    protected Session adminSession;
+
+    private Node testNode;
+
+    @Before
+    public void setup() throws RepositoryException {
+        adminSession = repository.login(new SimpleCredentials("admin", 
"admin".toCharArray()));
+        assertNotNull("Expected adminSession to not be null", adminSession);
+        testNode = adminSession.getRootNode().addNode("testNode");
+        adminSession.save();
+    }
+
+    @After
+    public void teardown() throws RepositoryException {
+        adminSession.refresh(false);
+        testNode.remove();
+        if (adminSession.hasPendingChanges()) {
+            adminSession.save();
+        }
+        adminSession.logout();
+    }
+
+    protected JsonObject ace(String path, String principalId) throws 
RepositoryException {
+        assertNotNull(getAce);
+        JsonObject aceObject = getAce.getAce(adminSession, path, principalId);
+        assertNotNull(aceObject);
+        return aceObject;
+    }
+
+    protected JsonObject acePrivleges(String path, String principalId) throws 
RepositoryException {
+        JsonObject ace = ace(path, principalId);
+        JsonObject privilegesObject = ace.getJsonObject("privileges");
+        assertNotNull(privilegesObject);
+        return privilegesObject;
+    }
+
+    private Value val(int type, String value) throws RepositoryException {
+        return adminSession.getValueFactory().createValue(value, type);
+    }
+    private Value[] vals(int type, String ... value) throws 
RepositoryException {
+        Value[] values = new Value[value.length];
+        ValueFactory vf = adminSession.getValueFactory();
+        for (int i = 0; i < value.length; i++) {
+            values[i] = vf.createValue(value[i], type);
+        }
+        return values;
+    }
+
+    @Test
+    public void testGetAceWithPrivileges() throws RepositoryException {
+        assertNotNull(modifyAce);
+        modifyAce.modifyAce(adminSession,
+                testNode.getPath(),
+                "everyone",
+                Collections.singletonMap(PrivilegeConstants.JCR_READ, "allow"),
+                "first");
+        // autosaved, so nothing should be pending
+        assertFalse(adminSession.hasPendingChanges());
+
+        JsonObject aceObject = ace(testNode.getPath(), "everyone");
+
+        JsonObject privilegesObject = aceObject.getJsonObject("privileges");
+        assertNotNull(privilegesObject);
+        assertEquals(1, privilegesObject.size());
+        //allow
+        assertPrivilege(privilegesObject, true, PrivilegeValues.ALLOW, 
PrivilegeConstants.JCR_READ);
+    }
+
+    @Test
+    public void testGetAceWithRestrictions() throws RepositoryException {
+        assertNotNull(modifyAce);
+        modifyAce.modifyAce(adminSession,
+                testNode.getPath(),
+                "everyone",
+                Collections.singletonMap(PrivilegeConstants.JCR_READ, "allow"),
+                "first",
+                Collections.singletonMap(AccessControlConstants.REP_GLOB, 
val(PropertyType.STRING, "/hello")),
+                
Collections.singletonMap(AccessControlConstants.REP_ITEM_NAMES, 
vals(PropertyType.NAME, "child1", "child2")),
+                Collections.emptySet());
+        // autosaved, so should be nothing pending
+        assertFalse(adminSession.hasPendingChanges());
+
+        JsonObject aceObject = ace(testNode.getPath(), "everyone");
+
+        JsonObject privilegesObject = aceObject.getJsonObject("privileges");
+        assertNotNull(privilegesObject);
+        assertEquals(1, privilegesObject.size());
+        //allow
+        assertPrivilege(privilegesObject, true, PrivilegeValues.ALLOW, 
PrivilegeConstants.JCR_READ);
+    }
+
+    @Test
+    public void testGetAceWithNullSessionArg() throws RepositoryException {
+        assertNotNull(modifyAce);
+        String resourcePath = testNode.getPath();
+        try {
+            getAce.getAce(null,
+                    resourcePath,
+                    "everyone");
+            fail("Expected RepositoryException");
+        } catch (RepositoryException re) {
+            assertEquals("JCR Session not found", re.getMessage());
+        }
+    }
+
+    @Test
+    public void testGetAceWithNullResourcePathArg() throws RepositoryException 
{
+        assertNotNull(modifyAce);
+        try {
+            getAce.getAce(adminSession,
+                    null,
+                    "everyone");
+            fail("Expected ResourceNotFoundException");
+        } catch (ResourceNotFoundException rnfe) {
+            //expected
+        }
+    }
+
+    @Test
+    public void testGetAceWithNotExistingResourcePathArg() throws 
RepositoryException {
+        assertNotNull(modifyAce);
+        try {
+            getAce.getAce(adminSession,
+                    "/not_a_real_path",
+                    "everyone");
+            fail("Expected ResourceNotFoundException");
+        } catch (ResourceNotFoundException rnfe) {
+            //expected
+        }
+    }
+
+    @Test
+    public void testGetAceWithNullPrincipalIdArg() throws RepositoryException {
+        assertNotNull(modifyAce);
+        String resourcePath = testNode.getPath();
+        try {
+            getAce.getAce(adminSession,
+                    resourcePath,
+                    null);
+            fail("Expected RepositoryException");
+        } catch (RepositoryException re) {
+            assertEquals("principalId was not submitted.", re.getMessage());
+        }
+    }
+
+    @Test
+    public void testGetAceWithNotExistingPrincipalIdArg() throws 
RepositoryException {
+        assertNotNull(modifyAce);
+        String resourcePath = testNode.getPath();
+        try {
+            getAce.getAce(adminSession,
+                    resourcePath,
+                    "not_a_real_principalid");
+            fail("Expected RepositoryException");
+        } catch (RepositoryException re) {
+            assertEquals("Invalid principalId was submitted.", 
re.getMessage());
+        }
+    }
+
+}
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
index deb2fcd..8296f79 100644
--- 
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
@@ -371,4 +371,27 @@ public class GetAclIT extends 
AccessManagerClientTestSupport {
         assertEquals(1, privilegesObject.size());
         assertPrivilege(privilegesObject, true, PrivilegeValues.DENY, 
PrivilegeConstants.JCR_ALL);
     }
+
+    /**
+     * ACL servlet returns 404 when no read access rights permissions
+     */
+    @Test
+    public void testNoAccessToDeclaredAclForUser() throws IOException, 
JsonException {
+        testUserId = createTestUser();
+        testFolderUrl = createTestFolder(null, "sling-tests",
+                "{ \"jcr:primaryType\": \"nt:unstructured\", \"child\" : { 
\"childPropOne\" : true } }");
+
+        //1. create an initial set of privileges
+        List<NameValuePair> postParams = new AcePostParamsBuilder(testUserId)
+                .withPrivilege(PrivilegeConstants.JCR_READ_ACCESS_CONTROL, 
PrivilegeValues.DENY)
+                .build();
+        addOrUpdateAce(testFolderUrl, postParams);
+
+        Credentials creds = new UsernamePasswordCredentials(testUserId, 
"testPwd");
+
+        //fetch the JSON for the ace to verify the settings.
+        String getUrl = testFolderUrl + "/child.acl.json";
+        // no declared access control entry returns a 404
+        assertAuthenticatedHttpStatus(creds, getUrl, 
HttpServletResponse.SC_NOT_FOUND, "Did not expect an ace to be returned");
+    }
 }
diff --git 
a/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/GetEaceIT.java 
b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/GetEaceIT.java
new file mode 100644
index 0000000..b253492
--- /dev/null
+++ 
b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/GetEaceIT.java
@@ -0,0 +1,137 @@
+/*
+ * 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.util.List;
+
+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.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 'eace' Sling Get Operation
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class GetEaceIT extends AccessManagerClientTestSupport {
+
+    protected void commonEffectiveAceForUser(String selector) throws 
IOException {
+        testUserId = createTestUser();
+        testFolderUrl = createTestFolder(null, "sling-tests1",
+                "{ \"jcr:primaryType\": \"nt:unstructured\", \"child\" : { 
\"childPropOne\" : true } }");
+
+        //1. create an initial set of privileges
+        List<NameValuePair> postParams = new AcePostParamsBuilder(testUserId)
+                .withPrivilege(PrivilegeConstants.JCR_WRITE, 
PrivilegeValues.ALLOW)
+                .build();
+        addOrUpdateAce(testFolderUrl, postParams);
+
+        Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+
+        //fetch the JSON for the ace to verify the settings.
+        String getUrl = testFolderUrl + "/child." + selector + ".json?pid=" + 
testUserId;
+
+        String json = getAuthenticatedContent(creds, getUrl, 
CONTENT_TYPE_JSON, HttpServletResponse.SC_OK);
+        assertNotNull(json);
+        JsonObject aceObject = parseJson(json);
+
+        String principalString = aceObject.getString("principal");
+        assertEquals(testUserId, principalString);
+
+        JsonObject privilegesObject = aceObject.getJsonObject("privileges");
+        assertNotNull(privilegesObject);
+        assertEquals(1, privilegesObject.size());
+        //allow privilege
+        assertPrivilege(privilegesObject, true, PrivilegeValues.ALLOW, 
PrivilegeConstants.JCR_WRITE);
+    }
+
+    /**
+     * Effective ACE servlet returns correct information
+     */
+    @Test
+    public void testEffectiveAceForUser() throws IOException, JsonException {
+        commonEffectiveAceForUser("eace");
+    }
+
+    /**
+     * Effective ACE servlet returns correct information
+     */
+    @Test
+    public void testTidyEffectiveAceForUser() throws IOException, 
JsonException {
+        commonEffectiveAceForUser("tidy.eace");
+    }
+
+    /**
+     * Effective ACE servlet returns correct information
+     */
+    @Test
+    public void testNoEffectiveAceForUser() throws IOException, JsonException {
+        testUserId = createTestUser();
+        testUserId2 = createTestUser();
+        testFolderUrl = createTestFolder(null, "sling-tests2",
+                "{ \"jcr:primaryType\": \"nt:unstructured\", \"child\" : { 
\"childPropOne\" : true } }");
+
+        //1. create an initial set of privileges
+        List<NameValuePair> postParams = new AcePostParamsBuilder(testUserId)
+                .withPrivilege(PrivilegeConstants.JCR_WRITE, 
PrivilegeValues.ALLOW)
+                .build();
+        addOrUpdateAce(testFolderUrl, postParams);
+
+        Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+
+        //fetch the JSON for the ace to verify the settings.
+        String getUrl = testFolderUrl + "/child.eace.json?pid=" + testUserId2;
+        assertAuthenticatedHttpStatus(creds, getUrl, 
HttpServletResponse.SC_NOT_FOUND, "Did not expect an ace to be returned");
+    }
+
+    /**
+     * Effective ACE servlet returns 404 when no read access rights permissions
+     */
+    @Test
+    public void testNoAccessToEffectiveAceForUser() throws IOException, 
JsonException {
+        testUserId = createTestUser();
+        testFolderUrl = createTestFolder(null, "sling-tests2",
+                "{ \"jcr:primaryType\": \"nt:unstructured\", \"child\" : { 
\"childPropOne\" : true } }");
+
+        //1. create an initial set of privileges
+        List<NameValuePair> postParams = new AcePostParamsBuilder(testUserId)
+                .withPrivilege(PrivilegeConstants.JCR_READ_ACCESS_CONTROL, 
PrivilegeValues.DENY)
+                .build();
+        addOrUpdateAce(testFolderUrl, postParams);
+
+        Credentials creds = new UsernamePasswordCredentials(testUserId, 
"testPwd");
+
+        //fetch the JSON for the ace to verify the settings.
+        String getUrl = testFolderUrl + ".eace.json?pid=" + testUserId;
+        assertAuthenticatedHttpStatus(creds, getUrl, 
HttpServletResponse.SC_NOT_FOUND, "Did not expect an ace to be returned");
+    }
+
+}
diff --git 
a/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/GetEaceServiceIT.java
 
b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/GetEaceServiceIT.java
new file mode 100644
index 0000000..03fce5b
--- /dev/null
+++ 
b/src/test/java/org/apache/sling/jcr/jackrabbit/accessmanager/it/GetEaceServiceIT.java
@@ -0,0 +1,225 @@
+/*
+ * 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.fail;
+
+import java.util.Collections;
+
+import javax.inject.Inject;
+import javax.jcr.Node;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+import javax.json.JsonObject;
+
+import 
org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+import org.apache.sling.api.resource.ResourceNotFoundException;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.jcr.jackrabbit.accessmanager.GetEffectiveAce;
+import org.apache.sling.jcr.jackrabbit.accessmanager.ModifyAce;
+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;
+
+/**
+ * Tests for the 'modifyAce' inproc service
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class GetEaceServiceIT extends AccessManagerClientTestSupport {
+
+    @Inject
+    private ModifyAce modifyAce;
+
+    @Inject
+    private GetEffectiveAce getEffectiveAce;
+
+    @Inject
+    protected SlingRepository repository;
+
+    protected Session adminSession;
+
+    private Node testNode;
+
+    @Before
+    public void setup() throws RepositoryException {
+        adminSession = repository.login(new SimpleCredentials("admin", 
"admin".toCharArray()));
+        assertNotNull("Expected adminSession to not be null", adminSession);
+        testNode = adminSession.getRootNode().addNode("testNode");
+        adminSession.save();
+    }
+
+    @After
+    public void teardown() throws RepositoryException {
+        adminSession.refresh(false);
+        testNode.remove();
+        if (adminSession.hasPendingChanges()) {
+            adminSession.save();
+        }
+        adminSession.logout();
+    }
+
+    protected JsonObject ace(String path, String principalId) throws 
RepositoryException {
+        assertNotNull(getEffectiveAce);
+        JsonObject aceObject = getEffectiveAce.getEffectiveAce(adminSession, 
path, principalId);
+        assertNotNull(aceObject);
+        return aceObject;
+    }
+
+    protected JsonObject acePrivleges(String path, String principalId) throws 
RepositoryException {
+        JsonObject ace = ace(path, principalId);
+        JsonObject privilegesObject = ace.getJsonObject("privileges");
+        assertNotNull(privilegesObject);
+        return privilegesObject;
+    }
+
+    private Value val(int type, String value) throws RepositoryException {
+        return adminSession.getValueFactory().createValue(value, type);
+    }
+    private Value[] vals(int type, String ... value) throws 
RepositoryException {
+        Value[] values = new Value[value.length];
+        ValueFactory vf = adminSession.getValueFactory();
+        for (int i = 0; i < value.length; i++) {
+            values[i] = vf.createValue(value[i], type);
+        }
+        return values;
+    }
+
+    @Test
+    public void testGetAceWithPrivileges() throws RepositoryException {
+        assertNotNull(modifyAce);
+        modifyAce.modifyAce(adminSession,
+                testNode.getPath(),
+                "everyone",
+                Collections.singletonMap(PrivilegeConstants.JCR_READ, "allow"),
+                "first");
+        // autosaved, so nothing should be pending
+        assertFalse(adminSession.hasPendingChanges());
+
+        JsonObject aceObject = ace(testNode.getPath(), "everyone");
+
+        JsonObject privilegesObject = aceObject.getJsonObject("privileges");
+        assertNotNull(privilegesObject);
+        assertEquals(1, privilegesObject.size());
+        //allow
+        assertPrivilege(privilegesObject, true, PrivilegeValues.ALLOW, 
PrivilegeConstants.JCR_READ);
+    }
+
+    @Test
+    public void testGetAceWithRestrictions() throws RepositoryException {
+        assertNotNull(modifyAce);
+        modifyAce.modifyAce(adminSession,
+                testNode.getPath(),
+                "everyone",
+                Collections.singletonMap(PrivilegeConstants.JCR_READ, "allow"),
+                "first",
+                Collections.singletonMap(AccessControlConstants.REP_GLOB, 
val(PropertyType.STRING, "/hello")),
+                
Collections.singletonMap(AccessControlConstants.REP_ITEM_NAMES, 
vals(PropertyType.NAME, "child1", "child2")),
+                Collections.emptySet());
+        // autosaved, so should be nothing pending
+        assertFalse(adminSession.hasPendingChanges());
+
+        JsonObject aceObject = ace(testNode.getPath(), "everyone");
+
+        JsonObject privilegesObject = aceObject.getJsonObject("privileges");
+        assertNotNull(privilegesObject);
+        assertEquals(1, privilegesObject.size());
+        //allow
+        assertPrivilege(privilegesObject, true, PrivilegeValues.ALLOW, 
PrivilegeConstants.JCR_READ);
+    }
+
+    @Test
+    public void testGetAceWithNullSessionArg() throws RepositoryException {
+        assertNotNull(modifyAce);
+        String resourcePath = testNode.getPath();
+        try {
+            getEffectiveAce.getEffectiveAce(null,
+                    resourcePath,
+                    "everyone");
+            fail("Expected RepositoryException");
+        } catch (RepositoryException re) {
+            assertEquals("JCR Session not found", re.getMessage());
+        }
+    }
+
+    @Test
+    public void testGetAceWithNullResourcePathArg() throws RepositoryException 
{
+        assertNotNull(modifyAce);
+        try {
+            getEffectiveAce.getEffectiveAce(adminSession,
+                    null,
+                    "everyone");
+            fail("Expected ResourceNotFoundException");
+        } catch (ResourceNotFoundException rnfe) {
+            //expected
+        }
+    }
+
+    @Test
+    public void testGetAceWithNotExistingResourcePathArg() throws 
RepositoryException {
+        assertNotNull(modifyAce);
+        try {
+            getEffectiveAce.getEffectiveAce(adminSession,
+                    "/not_a_real_path",
+                    "everyone");
+            fail("Expected ResourceNotFoundException");
+        } catch (ResourceNotFoundException rnfe) {
+            //expected
+        }
+    }
+
+    @Test
+    public void testGetAceWithNullPrincipalIdArg() throws RepositoryException {
+        assertNotNull(modifyAce);
+        String resourcePath = testNode.getPath();
+        try {
+            getEffectiveAce.getEffectiveAce(adminSession,
+                    resourcePath,
+                    null);
+            fail("Expected RepositoryException");
+        } catch (RepositoryException re) {
+            assertEquals("principalId was not submitted.", re.getMessage());
+        }
+    }
+
+    @Test
+    public void testGetAceWithNotExistingPrincipalIdArg() throws 
RepositoryException {
+        assertNotNull(modifyAce);
+        String resourcePath = testNode.getPath();
+        try {
+            getEffectiveAce.getEffectiveAce(adminSession,
+                    resourcePath,
+                    "not_a_real_principalid");
+            fail("Expected RepositoryException");
+        } catch (RepositoryException re) {
+            assertEquals("Invalid principalId was submitted.", 
re.getMessage());
+        }
+    }
+
+}

Reply via email to