/**
* Copyright 2008 The University of North Carolina at Chapel Hill
*
* Licensed 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 edu.unc.lib.dl.admin.controller;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.apache.solr.client.solrj.SolrServerException;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import edu.unc.lib.dl.acl.exception.AccessRestrictionException;
import edu.unc.lib.dl.acl.util.AccessGroupSet;
import edu.unc.lib.dl.acl.util.GroupsThreadStore;
import edu.unc.lib.dl.httpclient.HttpClientUtil;
import edu.unc.lib.dl.search.solr.model.BriefObjectMetadataBean;
import edu.unc.lib.dl.search.solr.model.SimpleIdRequest;
import edu.unc.lib.dl.search.solr.util.SearchFieldKeys;
import edu.unc.lib.dl.search.solr.util.SolrSettings;
import edu.unc.lib.dl.ui.exception.InvalidRecordRequestException;
import edu.unc.lib.dl.xml.JDOMNamespaceUtil;
@Controller
public class AccessControlController extends AbstractSwordController {
private static final Logger log = LoggerFactory.getLogger(AccessControlController.class);
@Autowired
SolrSettings solrSettings;
@Autowired
private String swordUrl;
@Autowired
private String swordUsername;
@Autowired
private String swordPassword;
private final List<String> targetResultFields = Arrays.asList(SearchFieldKeys.ID.name(), SearchFieldKeys.TITLE.name(),
SearchFieldKeys.STATUS.name(), SearchFieldKeys.ROLE_GROUP.name(), SearchFieldKeys.ANCESTOR_PATH.name());
private final List<String> parentResultFields = Arrays.asList(SearchFieldKeys.ID.name(), SearchFieldKeys.STATUS.name(),
SearchFieldKeys.ROLE_GROUP.name());
private String[] accessGroupFields;
@PostConstruct
public void init() {
accessGroupFields = new String[] { solrSettings.getFieldName(SearchFieldKeys.ADMIN_GROUP.name()),
solrSettings.getFieldName(SearchFieldKeys.READ_GROUP.name()) };
}
@RequestMapping(value = "acl/{pid}", method = RequestMethod.GET)
public String getAccessControl(@PathVariable("pid") String pid, Model model,
HttpServletResponse response) {
model.addAttribute("pid", pid);
// Retrieve ancestor information about the targeted object
AccessGroupSet accessGroups = GroupsThreadStore.getGroups();
SimpleIdRequest objectRequest = new SimpleIdRequest(pid, targetResultFields, accessGroups);
BriefObjectMetadataBean targetObject = queryLayer.getObjectById(objectRequest);
if (targetObject == null)
throw new InvalidRecordRequestException();
model.addAttribute("targetMetadata", targetObject);
// Get access information for the target's parent
BriefObjectMetadataBean parentObject = null;
if (targetObject.getAncestorPathFacet() != null) {
objectRequest = new SimpleIdRequest(targetObject.getAncestorPathFacet().getSearchKey(), parentResultFields,
accessGroups);
parentObject = queryLayer.getObjectById(objectRequest);
if (parentObject == null)
throw new InvalidRecordRequestException();
model.addAttribute("parentMetadata", parentObject);
}
// Retrieve the targeted objects directly attributed ACL document
String dataUrl = swordUrl + "em/" + pid + "/ACL";
CloseableHttpClient client = HttpClientUtil.getAuthenticatedClient(null, swordUsername, swordPassword);
HttpGet method = new HttpGet(dataUrl);
// Pass the users groups along with the request
AccessGroupSet groups = GroupsThreadStore.getGroups();
method.addHeader(HttpClientUtil.FORWARDED_GROUPS_HEADER, groups.joinAccessGroups(";"));
Element accessControlElement;
try (CloseableHttpResponse httpResp = client.execute(method)) {
int statusCode = httpResp.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_OK) {
String accessControlXML = EntityUtils.toString(httpResp.getEntity(), "UTF-8");
log.debug(accessControlXML);
SAXBuilder saxBuilder = new SAXBuilder();
accessControlElement = saxBuilder.build(new StringReader(accessControlXML)).getRootElement();
model.addAttribute("accessControlXML", accessControlXML);
model.addAttribute("targetACLs", accessControlElement);
} else {
log.error("Failed to retrieve access control document for " + pid + ": "
+ httpResp.getStatusLine());
response.setStatus(statusCode);
return null;
}
} catch (IOException | JDOMException e) {
response.setStatus(500);
log.error("Failed to retrieve access control document for " + pid, e);
return null;
}
Map<String, List<RoleGrant>> rolesGranted = new LinkedHashMap<String, List<RoleGrant>>();
for (Object elementObject : accessControlElement.getChildren()) {
Element childElement = (Element) elementObject;
if (childElement.getNamespace().equals(JDOMNamespaceUtil.CDR_ACL_NS)) {
String group = childElement.getAttributeValue("group", JDOMNamespaceUtil.CDR_ACL_NS);
String role = childElement.getAttributeValue("role", JDOMNamespaceUtil.CDR_ACL_NS);
List<RoleGrant> groupList = rolesGranted.get(role);
if (groupList == null) {
groupList = new ArrayList<RoleGrant>();
rolesGranted.put(role, groupList);
}
groupList.add(new RoleGrant(group, false));
}
}
if (parentObject != null) {
for (String parentRoles : parentObject.getRoleGroup()) {
String[] roleParts = parentRoles.split("\\|");
if (roleParts.length < 2)
continue;
String role = roleParts[0];
List<RoleGrant> groupList = rolesGranted.get(role);
if (groupList == null) {
groupList = new ArrayList<RoleGrant>();
rolesGranted.put(role, groupList);
}
String group = roleParts[1];
// If the map already contains this group, then it is marked explicitly as not inherited
groupList.add(new RoleGrant(group, true));
}
}
model.addAttribute("rolesGranted", rolesGranted);
model.addAttribute("template", "ajax");
return "edit/accessControl";
}
@RequestMapping(value = "acl/{pid}", method = RequestMethod.PUT)
public @ResponseBody
String saveAccessControl(@PathVariable("pid") String pid,
HttpServletRequest request, HttpServletResponse response) {
String datastream = "ACL";
return this.updateDatastream(pid, datastream, request, response);
}
@RequestMapping(value = "acl/getGroups", method = RequestMethod.GET)
public @ResponseBody
Collection<String> getAllAccessGroups() throws AccessRestrictionException, SolrServerException {
AccessGroupSet accessGroups = GroupsThreadStore.getGroups();
return this.queryLayer.getDistinctFieldValues(accessGroupFields, 500, accessGroups);
}
public static class RoleGrant {
public String roleName;
public boolean inherited;
public RoleGrant(String roleName, boolean inherited) {
this.roleName = roleName;
this.inherited = inherited;
}
public String getRoleName() {
return roleName;
}
public boolean isInherited() {
return inherited;
}
}
}