/**
* 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.security;
import java.util.ArrayList;
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.Set;
import org.fcrepo.server.errors.ObjectNotFoundException;
import org.jdom2.Content;
import org.jdom2.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import edu.unc.lib.dl.acl.util.UserRole;
import edu.unc.lib.dl.fedora.PID;
import edu.unc.lib.dl.util.ContentModelHelper;
import edu.unc.lib.dl.util.ContentModelHelper.Datastream;
import edu.unc.lib.dl.util.ParentBond;
public class AccessControlUtils {
private PID collectionsPid;
private static final Logger LOG = LoggerFactory
.getLogger(AccessControlUtils.class);
private edu.unc.lib.dl.util.TripleStoreQueryService tripleStoreQueryService = null;
private AncestorFactory ancestorFactory = null;
private GroupRolesFactory groupRolesFactory = null;
private EmbargoFactory embargoFactory = null;
private PatronAccessFactory patronAccessFactory = null;
private Map<String, Set<String>> globalRoles;
private String adminGroup;
private String curatorGroup;
public String getCuratorGroup() {
return curatorGroup;
}
public void setCuratorGroup(String curatorGroup) {
this.curatorGroup = curatorGroup;
}
public EmbargoFactory getEmbargoFactory() {
return embargoFactory;
}
public void setEmbargoFactory(EmbargoFactory embargoFactory) {
this.embargoFactory = embargoFactory;
}
public AncestorFactory getAncestorFactory() {
return ancestorFactory;
}
public void setAncestorFactory(AncestorFactory ancestorFactory) {
this.ancestorFactory = ancestorFactory;
}
public GroupRolesFactory getGroupRolesFactory() {
return groupRolesFactory;
}
public void setGroupRolesFactory(GroupRolesFactory groupRolesFactory) {
this.groupRolesFactory = groupRolesFactory;
}
public PatronAccessFactory getPatronAccessFactory() {
return patronAccessFactory;
}
public void setPatronAccessFactory(PatronAccessFactory patronAccessFactory) {
this.patronAccessFactory = patronAccessFactory;
}
public edu.unc.lib.dl.util.TripleStoreQueryService getTripleStoreQueryService() {
return tripleStoreQueryService;
}
public void setTripleStoreQueryService(
edu.unc.lib.dl.util.TripleStoreQueryService tripleStoreQueryService) {
this.tripleStoreQueryService = tripleStoreQueryService;
}
public String getAdminGroup() {
return adminGroup;
}
public void setAdminGroup(String adminGroup) {
this.adminGroup = adminGroup;
}
public void init() {
collectionsPid = tripleStoreQueryService
.fetchByRepositoryPath("/Collections");
if (collectionsPid == null)
throw new Error("Cannot find collections pid, failing.");
}
public AccessControlUtils() {
}
/**
* Retrieves list of active embargoes for the specified item, computed from
* it and its ancestors.
*
* @param pid
* identifier of the object being queried
* @return a list of embargo dates for this object
*/
public List<String> getEmbargoes(PID pid) {
try {
List<PID> ancestors = this.ancestorFactory.getInheritanceList(pid);
List<String> activeEmbargo = new ArrayList<String>();
// get embargo dates
Set<PID> embargoPids = new HashSet<PID>();
embargoPids.add(pid);
embargoPids.addAll(ancestors);
activeEmbargo = getEmbargoFactory().getEmbargoDates(embargoPids);
return activeEmbargo;
} catch (ObjectNotFoundException e) {
LOG.error("Cannot find object in question", e);
}
return null;
}
/**
* Given a PID, return a map containing the group permissions organized by
* rights type and active embargoes
*
* @param pid
* @return
*/
public Map<String, Object> getAllCdrAccessControls(PID pid) {
Map<String, Object> result = new HashMap<String, Object>();
result.put("roles", this.getRoles(pid));
result.put("globals", this.getGlobalRoles());
result.put("embargoes", this.getEmbargoes(pid));
result.put("objectState", this.getObjectState(pid));
result.put("publicationStatus", this.getPublished(pid));
return result;
}
/**
* Given a PID, return the group permissions organized by rights type in an
* XML structure: <permissions><roles><originalsRead>rla</originalsRead>
* <originalsRead>abc</originalsRead></roles></permissions>
*
* @param inputPid
* @return XML representation of the access control for this PID.
*/
public Content getAllCdrAccessControlsXML(PID pid) {
Map<String, Object> stuff = this.getAllCdrAccessControls(pid);
@SuppressWarnings("unchecked")
Map<String, Set<String>> summary = (Map<String, Set<String>>) stuff
.get("roles");
@SuppressWarnings("unchecked")
List<String> activeEmbargo = (List<String>) stuff.get("embargoes");
// serialize JDOM
Element permsEl = new Element("permissions");
Element rolesEl = new Element("roles");
permsEl.addContent(rolesEl);
for (String role : summary.keySet()) {
Element roleEl = new Element("role");
rolesEl.addContent(roleEl);
roleEl.setAttribute("roleId", role);
for (String groupName : summary.get(role)) {
Element groupEl = new Element("group");
roleEl.addContent(groupEl);
groupEl.setText(groupName);
}
}
Element embargoesEl = new Element("embargoes");
permsEl.addContent(embargoesEl);
for (String date : activeEmbargo) {
Element embargoEl = new Element("embargo");
embargoesEl.addContent(embargoEl);
embargoEl.setText(date);
}
return permsEl;
}
public List<String> getAllEmbargoes(PID pid) {
@SuppressWarnings("unchecked")
List<String> result = Collections.EMPTY_LIST;
try {
Set<PID> embargoPIDs = new HashSet<PID>();
embargoPIDs.addAll(this.getAncestorFactory()
.getInheritanceList(pid));
embargoPIDs.add(pid);
result = this.getEmbargoFactory().getEmbargoDates(embargoPIDs);
} catch (ObjectNotFoundException e) {
// TODO Auto-generated catch block
LOG.error("Cannot find object to look up ancestors", e);
}
return result;
}
/**
* Retrieves a map representing the roles and groups assigned to those roles
* for the specified object, computed from it and its ancestors.
*
* @param pid
* identifier of the object being queried
* @return
*/
public Map<String, Set<String>> getRoles(PID pid) {
Map<String, Set<String>> summary = new HashMap<String, Set<String>>();
try {
Map<String, Set<String>> local = groupRolesFactory
.getAllRolesAndGroups(pid);
summary.putAll(local);
List<PID> ancestors = this.ancestorFactory.getInheritanceList(pid);
for (PID ancestor : ancestors) {
Map<String, Set<String>> inherited = groupRolesFactory
.getAllRolesAndGroups(ancestor);
if (inherited != null && !inherited.isEmpty()) {
for (String role : inherited.keySet()) {
if (summary.containsKey(role)) {
summary.get(role).addAll(inherited.get(role));
} else {
summary.put(role, inherited.get(role));
}
}
}
}
// special list inheritance
// if my bond with parent is non-inheriting,
// then all groups with roles (inherited or not) on parent have the
// list role
ParentBond bond = this.ancestorFactory.getParentBond(pid);
if (bond != null && !bond.inheritsRoles) {
Set<String> listGroups = new HashSet<String>();
Map<String, Set<String>> rolesMap = groupRolesFactory
.getAllRolesAndGroups(bond.parentPid);
for (Set<String> rgroups : rolesMap.values()) {
listGroups.addAll(rgroups);
}
List<PID> parentAncestors = this.ancestorFactory
.getInheritanceList(bond.parentPid);
for (PID ancestor : parentAncestors) {
Map<String, Set<String>> inherited = groupRolesFactory
.getAllRolesAndGroups(ancestor);
for (Set<String> rgroups : inherited.values()) {
listGroups.addAll(rgroups);
}
}
if (listGroups.size() > 0) {
summary.put(UserRole.list.getURI().toString(), listGroups);
}
}
} catch (ObjectNotFoundException e) {
LOG.error("Cannot find object in question", e);
}
return summary;
}
public Map<String, Set<String>> getGlobalRoles() {
if (globalRoles == null) {
globalRoles = new HashMap<String, Set<String>>();
// Add the admin group into the results
if (this.getAdminGroup() != null) {
Set<String> adminGroups = globalRoles.get(UserRole.administrator
.getURI().toString());
if (adminGroups == null) {
adminGroups = new HashSet<String>();
globalRoles.put(UserRole.administrator.getURI().toString(), adminGroups);
}
adminGroups.add(getAdminGroup());
}
// Add the admin group into the results
if (this.getCuratorGroup() != null) {
Set<String> curatorGroups = globalRoles.get(UserRole.curator
.getURI().toString());
if (curatorGroups == null) {
curatorGroups = new HashSet<String>();
globalRoles.put(UserRole.curator.getURI().toString(), curatorGroups);
}
curatorGroups.add(this.getCuratorGroup());
}
}
return globalRoles;
}
public Set<String> getRolesForGroups(Collection<String> groups, PID pid) {
Set<String> result = new HashSet<String>();
LOG.debug("getRolesForGroups: " + pid + " " + groups);
try {
if(this.getAdminGroup() != null && groups.contains(this.getAdminGroup())) {
result.add(UserRole.administrator.getURI().toString());
}
if(this.getCuratorGroup() != null && groups.contains(this.getCuratorGroup())) {
result.add(UserRole.curator.getURI().toString());
}
Map<String, Set<String>> roles2Groups = groupRolesFactory
.getAllRolesAndGroups(pid);
if (roles2Groups != null) {
for (String role : roles2Groups.keySet()) {
if (!Collections.disjoint(roles2Groups.get(role), groups))
result.add(role);
}
}
// special list inheritance logic
// if my bond with parent is non-inheriting,
// then all groups with roles (local or inherited) on parent have
// list
ParentBond bond = this.ancestorFactory.getParentBond(pid);
if (bond == null) {
LOG.debug("got no parent bond, returning");
return result;
}
if (!bond.inheritsRoles) {
boolean foundList = false;
Map<String, Set<String>> rolesMap = groupRolesFactory
.getAllRolesAndGroups(bond.parentPid);
for (Set<String> rgroups : rolesMap.values()) {
if (!Collections.disjoint(groups, rgroups)) {
result.add(UserRole.list.getURI().toString());
foundList = true;
break;
}
}
if (!foundList) {
List<PID> parentAncestors = this.ancestorFactory
.getInheritanceList(bond.parentPid);
foo: for (PID ancestor : parentAncestors) {
Map<String, Set<String>> inheritedRoles = groupRolesFactory
.getAllRolesAndGroups(ancestor);
for (Set<String> rgroups : inheritedRoles.values()) {
if (!Collections.disjoint(groups, rgroups)) {
result.add(UserRole.list.getURI().toString());
break foo;
}
}
}
}
} else {
// list of ancestors from which this pid inherits access
// controls
List<PID> ancestors = this.ancestorFactory
.getInheritanceList(pid);
for (PID ancestor : ancestors) {
Map<String, Set<String>> aroles2Groups = groupRolesFactory
.getAllRolesAndGroups(ancestor);
if (aroles2Groups != null) {
for (String role : aroles2Groups.keySet()) {
if (!Collections.disjoint(aroles2Groups.get(role),
groups))
result.add(role);
}
}
}
}
} catch (ObjectNotFoundException e) {
LOG.error("Cannot find object in question", e);
}
LOG.debug("found roles: " + result);
return result;
}
/**
* Retrieves the set of groups in the requested role, including the
* parent-implied list role.
*/
public Set<String> getGroupsInRole(PID pid, String role) {
Set<String> groups = new HashSet<String>();
LOG.debug("getGroupsInRole: " + pid + " " + role);
try {
Set<String> local = groupRolesFactory.getGroupsInRole(pid, role);
if (local != null) {
groups.addAll(local);
}
if (UserRole.list.getURI().equals(role)) {
LOG.debug("looking for list role");
// special list inheritance logic
// if my bond with parent is non-inheriting,
// then all groups with roles on parent have list
ParentBond bond = this.ancestorFactory.getParentBond(pid);
if (!bond.inheritsRoles) {
Map<String, Set<String>> rolesMap = groupRolesFactory
.getAllRolesAndGroups(bond.parentPid);
for (Set<String> rgroups : rolesMap.values()) {
groups.addAll(rgroups);
}
}
} else {
// list of ancestors from which this pid inherits access
// controls
List<PID> ancestors = this.ancestorFactory
.getInheritanceList(pid);
for (PID ancestor : ancestors) {
LOG.debug("checking for roles on ancestor: " + ancestor);
Set<String> additions = groupRolesFactory.getGroupsInRole(
ancestor, role);
if (additions != null) {
groups.addAll(additions);
}
}
}
} catch (ObjectNotFoundException e) {
LOG.error("Cannot find object in question", e);
}
LOG.debug("getGroupsInRole: found the following groups in role " + role
+ " on pid " + pid);
if (LOG.isDebugEnabled()) {
StringBuilder b = new StringBuilder();
for (String s : groups) {
b.append(s).append("\t");
}
LOG.debug(b.toString());
}
return groups;
}
public boolean isPublished(PID pid) {
try {
List<PID> ancestors = this.ancestorFactory.getInheritanceList(pid);
ancestors.add(pid);
for (PID ancestor: ancestors) {
Boolean status = patronAccessFactory.isPublished(ancestor);
if (!status) {
return false;
}
}
} catch (ObjectNotFoundException e) {
LOG.error("Cannot find object in question", e);
}
return true;
}
public boolean isActive(PID pid) {
try {
List<PID> ancestors = this.ancestorFactory.getInheritanceList(pid);
ancestors.add(pid);
for (PID ancestor: ancestors) {
Boolean isActive = patronAccessFactory.isStateActive(ancestor);
if (!isActive) {
return false;
}
}
} catch (ObjectNotFoundException e) {
LOG.error("Cannot find object in question", e);
}
return true;
}
public List<String> getObjectState(PID pid) {
try {
// Compute inherited publication state
boolean inheritedStatus = true;
List<PID> ancestors = this.ancestorFactory.getInheritanceList(pid);
for (PID ancestor: ancestors) {
Boolean status = patronAccessFactory.isStateActive(ancestor);
if (!status) {
inheritedStatus = false;
break;
}
}
// Get the publication state for this particular item
Boolean status = patronAccessFactory.isStateActive(pid);
List<String> answer = new ArrayList<String>(2);
if (!inheritedStatus)
answer.add("Deleted Ancestor");
if (!status)
answer.add("Deleted");
else if (inheritedStatus)
answer.add("Active");
return answer;
} catch (ObjectNotFoundException e) {
LOG.error("Cannot find object in question", e);
}
return null;
}
public List<String> getPublished(PID pid) {
try {
// Compute inherited publication state
boolean inheritedStatus = true;
List<PID> ancestors = this.ancestorFactory.getInheritanceList(pid);
for (PID ancestor: ancestors) {
Boolean status = patronAccessFactory.isPublished(ancestor);
if (!status) {
inheritedStatus = false;
break;
}
}
// Get the publication state for this particular item
Boolean status = patronAccessFactory.isPublished(pid);
List<String> answer = new ArrayList<String>(2);
if (!inheritedStatus)
answer.add("Unpublished Ancestor");
if (!status)
answer.add("Unpublished");
else if (inheritedStatus)
answer.add("Published");
return answer;
} catch (ObjectNotFoundException e) {
LOG.error("Cannot find object in question", e);
}
return null;
}
public List<String> getDatastreamCategories(String datastreamId) {
List<String> result = new ArrayList<String>();
for(Datastream ds : ContentModelHelper.Datastream.values()) {
if(ds.getName().equals(datastreamId)) {
result.add(ds.getCategory().name());
break;
}
}
return result;
}
}