blob: f58eceb7e481e07e244b5623a93a7256faeffe35 [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.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.jcr.Item;
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.servlet.Servlet;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.ResourceNotFoundException;
import org.apache.sling.jcr.base.util.AccessControlUtil;
import org.apache.sling.jcr.jackrabbit.accessmanager.DeleteAces;
import org.apache.sling.servlets.post.Modification;
import org.apache.sling.servlets.post.PostResponse;
import org.apache.sling.servlets.post.PostResponseCreator;
import org.apache.sling.servlets.post.SlingPostConstants;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>
* Sling Post Servlet implementation for deleting the ACE for a set of principals on a JCR
* resource.
* </p>
* <h2>Rest Service Description</h2>
* <p>
* Delete a set of Ace's from a node, the node is identified as a resource by the request
* url &gt;resource&lt;.deleteAce.html
* </p>
* <h4>Methods</h4>
* <ul>
* <li>POST</li>
* </ul>
* <h4>Post Parameters</h4>
* <dl>
* <dt>:applyTo</dt>
* <dd>An array of ace principal names to delete. Note the principal name is the primary
* key of the Ace in the Acl</dd>
* </dl>
*
* <h4>Response</h4>
* <dl>
* <dt>200</dt>
* <dd>Success.</dd>
* <dt>404</dt>
* <dd>The resource was not found.</dd>
* <dt>500</dt>
* <dd>Failure. HTML explains the failure.</dd>
* </dl>
*/
@Component(service = {Servlet.class, DeleteAces.class},
property= {
"sling.servlet.resourceTypes=sling/servlet/default",
"sling.servlet.methods=POST",
"sling.servlet.selectors=deleteAce"
})
public class DeleteAcesServlet extends AbstractAccessPostServlet implements DeleteAces {
private static final long serialVersionUID = 3784866802938282971L;
/**
* default log
*/
private final Logger log = LoggerFactory.getLogger(getClass());
/**
* Overridden since the @Reference annotation is not inherited from the super method
*
* @see org.apache.sling.jackrabbit.usermanager.impl.post.AbstractPostServlet#bindPostResponseCreator(org.apache.sling.servlets.post.PostResponseCreator, java.util.Map)
*/
@Override
@Reference(service = PostResponseCreator.class,
cardinality = ReferenceCardinality.MULTIPLE,
policy = ReferencePolicy.DYNAMIC)
protected void bindPostResponseCreator(PostResponseCreator creator, Map<String, Object> properties) {
super.bindPostResponseCreator(creator, properties);
}
/* (non-Javadoc)
* @see org.apache.sling.jackrabbit.usermanager.impl.post.AbstractPostServlet#unbindPostResponseCreator(org.apache.sling.servlets.post.PostResponseCreator, java.util.Map)
*/
@Override
protected void unbindPostResponseCreator(PostResponseCreator creator, Map<String, Object> properties) {
super.unbindPostResponseCreator(creator, properties);
}
/* (non-Javadoc)
* @see org.apache.sling.jackrabbit.accessmanager.post.AbstractAccessPostServlet#handleOperation(org.apache.sling.api.SlingHttpServletRequest, org.apache.sling.servlets.post.PostResponse, java.util.List)
*/
@Override
protected void handleOperation(SlingHttpServletRequest request,
PostResponse htmlResponse, List<Modification> changes)
throws RepositoryException {
Session session = request.getResourceResolver().adaptTo(Session.class);
String resourcePath = request.getResource().getPath();
String[] applyTo = request.getParameterValues(SlingPostConstants.RP_APPLY_TO);
deleteAces(session, resourcePath, applyTo);
}
/* (non-Javadoc)
* @see org.apache.sling.jcr.jackrabbit.accessmanager.DeleteAces#deleteAces(javax.jcr.Session, java.lang.String, java.lang.String[])
*/
public void deleteAces(Session jcrSession, String resourcePath,
String[] principalNamesToDelete) throws RepositoryException {
if (principalNamesToDelete == null) {
throw new RepositoryException("principalIds were not sumitted.");
} else {
if (jcrSession == null) {
throw new RepositoryException("JCR Session not found");
}
if (resourcePath == null) {
throw new ResourceNotFoundException("Resource path was not supplied.");
}
Item item = jcrSession.getItem(resourcePath);
if (item != null) {
resourcePath = item.getPath();
} else {
throw new ResourceNotFoundException("Resource is not a JCR Node");
}
//load the principalIds array into a set for quick lookup below
Set<String> pidSet = new HashSet<String>();
pidSet.addAll(Arrays.asList(principalNamesToDelete));
try {
AccessControlManager accessControlManager = AccessControlUtil.getAccessControlManager(jcrSession);
AccessControlList updatedAcl = getAccessControlListOrNull(accessControlManager, resourcePath, false);
// if there is no AccessControlList, then there is nothing to be deleted
if (updatedAcl == null) {
// log the warning about principals where no ACE was found
for (String pid : pidSet) {
log.warn("No AccessControlEntry was found to be deleted for principal: " + pid);
}
} else {
//keep track of the existing Aces for the target principal
AccessControlEntry[] accessControlEntries = updatedAcl.getAccessControlEntries();
List<AccessControlEntry> oldAces = new ArrayList<AccessControlEntry>();
for (AccessControlEntry ace : accessControlEntries) {
if (pidSet.contains(ace.getPrincipal().getName())) {
oldAces.add(ace);
}
}
// track which of the submitted principals had an ACE removed
Set<String> removedPidSet = new HashSet<>();
//remove the old aces
if (!oldAces.isEmpty()) {
for (AccessControlEntry ace : oldAces) {
updatedAcl.removeAccessControlEntry(ace);
// remove from the candidate set
removedPidSet.add(ace.getPrincipal().getName());
}
}
// log the warning about principals where no ACE was found
for (String pid : pidSet) {
if (!removedPidSet.contains(pid)) {
log.warn("No AccessControlEntry was found to be deleted for principal: " + pid);
}
}
//apply the changed policy
accessControlManager.setPolicy(resourcePath, updatedAcl);
}
} catch (RepositoryException re) {
throw new RepositoryException("Failed to delete access control.", re);
}
}
}
}