blob: 3959885b018c045588e03072dc9af337c7a611e1 [file] [log] [blame]
/*
* 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.lang.reflect.Array;
import java.security.Principal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
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.ValueFormatException;
import javax.jcr.security.AccessControlEntry;
import javax.jcr.security.Privilege;
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.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.impl.PrivilegesHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@SuppressWarnings("serial")
public abstract class AbstractGetAclServlet extends SlingAllMethodsServlet {
protected static final String KEY_ORDER = "order";
protected static final String KEY_DENIED = "denied";
protected static final String KEY_GRANTED = "granted";
/**
* default log
*/
private final transient Logger log = LoggerFactory.getLogger(getClass());
/* (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("UTF-8");
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 (Throwable throwable) {
log.debug(String.format("Exception while handling GET %s with %s",
request.getResource().getPath(), getClass().getName()), throwable);
throw new ServletException(throwable);
}
}
@SuppressWarnings("unchecked")
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");
}
// Calculate a map of privileges to all the aggregate privileges it is contained in.
// Use for fast lookup during the mergePrivilegeSets calls below.
Map<Privilege, Set<Privilege>> privilegeToAncestorMap = PrivilegesHelper.buildPrivilegeToAncestorMap(jcrSession, resourcePath);
AccessControlEntry[] declaredAccessControlEntries = getAccessControlEntries(jcrSession, resourcePath);
Map<String, Map<String, Object>> aclMap = new LinkedHashMap<>();
Map<String, Map<String, Object>> restrictionMap = new LinkedHashMap<>();
int sequence = 0;
for (AccessControlEntry ace : declaredAccessControlEntries) {
Principal principal = ace.getPrincipal();
Map<String, Object> map = aclMap.get(principal.getName());
if (map == null) {
map = new LinkedHashMap<>();
aclMap.put(principal.getName(), map);
map.put(KEY_ORDER, sequence++);
}
}
//evaluate these in reverse order so the most entries with highest specificity are last
for (int i = declaredAccessControlEntries.length - 1; i >= 0; i--) {
AccessControlEntry ace = declaredAccessControlEntries[i];
Principal principal = ace.getPrincipal();
if (ace instanceof JackrabbitAccessControlEntry) {
JackrabbitAccessControlEntry jace = (JackrabbitAccessControlEntry)ace;
String[] restrictionNames = jace.getRestrictionNames();
if (restrictionNames != null) {
Map<String, Object> restrictions = restrictionMap.get(principal.getName());
if (restrictions == null) {
restrictions = new HashMap<>();
restrictionMap.put(principal.getName(), restrictions);
}
for (String rname : restrictionNames) {
try {
//try as a single-value restriction
Value value = jace.getRestriction(rname);
restrictions.put(rname, value);
} catch (ValueFormatException vfe) {
//try as a multi-value restriction
Value[] values = jace.getRestrictions(rname);
restrictions.put(rname, values);
}
}
}
}
Map<String, Object> map = aclMap.get(principal.getName());
Set<Privilege> grantedSet = (Set<Privilege>) map.get(KEY_GRANTED);
if (grantedSet == null) {
grantedSet = new LinkedHashSet<>();
map.put(KEY_GRANTED, grantedSet);
}
Set<Privilege> deniedSet = (Set<Privilege>) map.get(KEY_DENIED);
if (deniedSet == null) {
deniedSet = new LinkedHashSet<>();
map.put(KEY_DENIED, deniedSet);
}
boolean allow = AccessControlUtil.isAllow(ace);
if (allow) {
Privilege[] privileges = ace.getPrivileges();
for (Privilege privilege : privileges) {
PrivilegesHelper.mergePrivilegeSets(privilege,
privilegeToAncestorMap,
grantedSet, deniedSet);
}
} else {
Privilege[] privileges = ace.getPrivileges();
for (Privilege privilege : privileges) {
PrivilegesHelper.mergePrivilegeSets(privilege,
privilegeToAncestorMap,
deniedSet, grantedSet);
}
}
}
List<JsonObject> aclList = new ArrayList<>();
Set<Entry<String, Map<String, Object>>> entrySet = aclMap.entrySet();
for (Entry<String, Map<String, Object>> entry : entrySet) {
String principalName = entry.getKey();
Map<String, Object> value = entry.getValue();
JsonObjectBuilder aceObject = Json.createObjectBuilder();
aceObject.add("principal", principalName);
Set<Privilege> grantedSet = (Set<Privilege>) value.get(KEY_GRANTED);
if (grantedSet != null && !grantedSet.isEmpty()) {
JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
for (Privilege v : grantedSet)
{
arrayBuilder.add(v.getName());
}
aceObject.add(KEY_GRANTED, arrayBuilder);
}
Set<Privilege> deniedSet = (Set<Privilege>) value.get(KEY_DENIED);
if (deniedSet != null && !deniedSet.isEmpty()) {
JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
for (Privilege v : deniedSet)
{
arrayBuilder.add(v.getName());
}
aceObject.add(KEY_DENIED, arrayBuilder);
}
aceObject.add(KEY_ORDER, (Integer) value.get(KEY_ORDER));
Map<String, Object> restrictions = restrictionMap.get(principalName);
if (restrictions != null && !restrictions.isEmpty()) {
Set<Entry<String, Object>> entrySet2 = restrictions.entrySet();
JsonObjectBuilder jsonRestrictions = Json.createObjectBuilder();
for (Entry<String, Object> entry2 : entrySet2) {
Object rvalue = entry2.getValue();
if (rvalue != null) {
if (rvalue.getClass().isArray()) {
JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
int length = Array.getLength(rvalue);
for (int i= 0; i < length; i++) {
Object object = Array.get(rvalue, i);
addTo(arrayBuilder, object);
}
jsonRestrictions.add(entry2.getKey(), arrayBuilder);
} else {
addTo(jsonRestrictions, entry2.getKey(), rvalue);
}
}
}
aceObject.add("restrictions", jsonRestrictions);
}
aclList.add(aceObject.build());
}
JsonObjectBuilder jsonAclMap = Json.createObjectBuilder();
for (Map.Entry<String, Map<String, Object>> entry : aclMap.entrySet())
{
JsonObjectBuilder builder = Json.createObjectBuilder();
for (Map.Entry<String, Object> inner : entry.getValue().entrySet())
{
addTo(builder, inner.getKey(), inner.getValue());
}
jsonAclMap.add(entry.getKey(), builder);
}
for (JsonObject jsonObj : aclList) {
jsonAclMap.add(jsonObj.getString("principal"), jsonObj);
}
return jsonAclMap.build();
}
private 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;
}
private 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;
}
protected abstract AccessControlEntry[] getAccessControlEntries(Session session, String absPath) throws RepositoryException;
}