/** * 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.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.fcrepo.server.security.xacml.pdp.finder.AttributeFinderException; import org.fcrepo.server.security.xacml.pdp.finder.attribute.DesignatorAttributeFinderModule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Node; import org.jboss.security.xacml.sunxacml.EvaluationCtx; import org.jboss.security.xacml.sunxacml.attr.AttributeFactory; import org.jboss.security.xacml.sunxacml.attr.AttributeValue; import org.jboss.security.xacml.sunxacml.attr.BagAttribute; import org.jboss.security.xacml.sunxacml.attr.StringAttribute; import org.jboss.security.xacml.sunxacml.attr.StandardAttributeFactory; import org.jboss.security.xacml.sunxacml.cond.EvaluationResult; import edu.unc.lib.dl.fedora.PID; import edu.unc.lib.dl.util.ContentModelHelper; public class CdrRIAttributeFinder extends DesignatorAttributeFinderModule { private static final Logger log = LoggerFactory.getLogger(CdrRIAttributeFinder.class); URI embargoUntil = ContentModelHelper.CDRProperty.embargoUntil.getURI(); URI dataAccessCategory = ContentModelHelper.CDRProperty.dataAccessCategory.getURI(); URI userRole = ContentModelHelper.CDRProperty.userRole.getURI(); URI isPublished = ContentModelHelper.CDRProperty.isPublished.getURI(); URI isActive = ContentModelHelper.CDRProperty.isActive.getURI(); static URI datastreamIdAttribute = null; static URI fedoraSubjectRoleAttribute = null; static URI stringDataType = null; static URI accessSubjectCategory = null; static { try { fedoraSubjectRoleAttribute = new URI("urn:fedora:names:fedora:2.1:subject:role"); stringDataType = new URI("http://www.w3.org/2001/XMLSchema#string"); datastreamIdAttribute = new URI("urn:fedora:names:fedora:2.1:resource:datastream:id"); accessSubjectCategory = new URI("urn:oasis:names:tc:xacml:1.0:subject-category:access-subject"); } catch (URISyntaxException e) { throw new Error(e); } } private AttributeFactory attributeFactory = null; private AccessControlUtils accessControlUtils; public CdrRIAttributeFinder() { super(); attributeFactory = StandardAttributeFactory.getFactory(); } public void init() { log.info("Initialised AttributeFinder: {}", this.getClass().getName()); if (log.isDebugEnabled()) { log.debug("registering the following attributes: "); for (Integer desNum : this.getSupportedDesignatorTypes()) { log.debug("Designator Type: {}", desNum); for (String attrName : m_attributes.get(desNum).keySet()) { log.debug("\t{}", attrName); } } } } /** * Used to get an attribute. If one of those values isn't being asked for, or if the types are wrong, then an empty * bag is returned. * * @param attributeType * the datatype of the attributes to find, which must be time, date, or dateTime for this module to resolve * a value * @param attributeId * the identifier of the attributes to find, which must be one of the three ENVIRONMENT_* fields for this * module to resolve a value * @param issuer * the issuer of the attributes, or null if unspecified * @param subjectCategory * the category of the attribute or null, which ignored since this only handles non-subjects * @param context * the representation of the request data * @param designatorType * the type of designator * @return the result of attribute retrieval, which will be a bag with a single attribute, an empty bag, or an error */ @Override public EvaluationResult findAttribute(URI attributeType, URI attributeId, URI issuer, URI subjectCategory, EvaluationCtx context, int designatorType) { long startTime = System.currentTimeMillis(); String resourceId = context.getResourceId().encode(); if (log.isDebugEnabled()) { log.debug("CdrRIAttributeFinder: [" + attributeType.toString() + "] " + attributeId + ", rid=" + resourceId); } if (resourceId == null || resourceId.equals("")) { return new EvaluationResult(BagAttribute.createEmptyBag(attributeType)); } if (resourceId.equals("/FedoraRepository")) { return new EvaluationResult(BagAttribute.createEmptyBag(attributeType)); } // figure out which attribute we're looking for String attrName = attributeId.toString(); // we only know about registered attributes from config file if (!getSupportedDesignatorTypes().contains(new Integer(designatorType))) { if (log.isDebugEnabled()) { log.debug("Does not know about designatorType: {}", designatorType); } return new EvaluationResult(BagAttribute.createEmptyBag(attributeType)); } Set<String> allowedAttributes = m_attributes.get(designatorType).keySet(); if (!allowedAttributes.contains(attrName)) { if (log.isDebugEnabled()) { log.debug("Does not know about attribute: {}", attrName); } return new EvaluationResult(BagAttribute.createEmptyBag(attributeType)); } EvaluationResult result = null; try { result = getEvaluationResult(resourceId, attributeId, context, designatorType, attributeType); } catch (Exception e) { log.error("Error finding attribute: " + e.getMessage(), e); return new EvaluationResult(BagAttribute.createEmptyBag(attributeType)); } log.info("Total time for CDR role lookup: {} milliseconds", (System.currentTimeMillis() - startTime)); return result; } private Set<String> getShibbolethGroups(EvaluationCtx context) { Set<String> groups = new HashSet<String>(); EvaluationResult result = context.getSubjectAttribute(stringDataType, fedoraSubjectRoleAttribute, accessSubjectCategory); if (result.indeterminate()) { return groups; } if (result.getStatus() != null) { return groups; } AttributeValue attributeValue = result.getAttributeValue(); if (attributeValue == null) { return groups; } if (!attributeValue.isBag()) { return groups; } BagAttribute bag = (BagAttribute) attributeValue; Iterator iterator = bag.iterator(); while (iterator.hasNext()) { Object value = iterator.next(); if (value instanceof StringAttribute) { StringAttribute attribute = (StringAttribute) value; log.debug("adding attribute: {}", attribute); groups.add(attribute.getValue()); } } return groups; } private String getDatastreamID(EvaluationCtx context) { EvaluationResult result = context.getResourceAttribute(stringDataType, datastreamIdAttribute, null); if (result.indeterminate()) { return null; } if (result.getStatus() != null) { return null; } AttributeValue attributeValue = result.getAttributeValue(); if (attributeValue == null) { return null; } if (!attributeValue.isBag()) { return null; } BagAttribute bag = (BagAttribute) attributeValue; Iterator iterator = bag.iterator(); while (iterator.hasNext()) { Object value = iterator.next(); if (value instanceof StringAttribute) { StringAttribute attribute = (StringAttribute) value; log.debug("returning attribute: {}", attribute); return attribute.getValue(); } } return null; } /** * * @param resourceID * - the hierarchical XACML resource ID * @param attribute * - attribute to get - this is a URI that maps to a Fedora relationship name * @param type * @return * @throws AttributeFinderException */ private EvaluationResult getEvaluationResult(String resourceID, URI attribute, EvaluationCtx context, int designatorType, URI type) throws AttributeFinderException { // split up the path of the hierarchical resource id // either the last part is the pid, or the last-but one is the pid and // the last is the datastream String resourceParts[] = resourceID.split("/"); String subject; // the full subject, ie pid or pid/ds String pid; if (resourceParts.length > 1) { if (resourceParts[resourceParts.length - 1].contains(":")) { // ends with a pid, we have pid only subject = resourceParts[resourceParts.length - 1]; pid = subject; } else { // datastream pid = resourceParts[resourceParts.length - 2]; subject = pid + "/" + resourceParts[resourceParts.length - 1]; } } else { // eg /FedoraRepository, not a valid path to PID or PID/DS log.debug("Resource ID not valid path to PID or datastream: {}", resourceID); return new EvaluationResult(BagAttribute.createEmptyBag(type)); } log.debug("Getting attribute {} for resource {}", attribute, pid); if (userRole.equals(attribute)) { Set<String> groups = getShibbolethGroups(context); Set<String> roles = accessControlUtils.getRolesForGroups(groups, new PID(pid)); return makeStringBagResult(roles, type); } else if (embargoUntil.equals(attribute)) { List<String> embargoes = getAccessControlUtils().getAllEmbargoes(new PID(pid)); return makeStringBagResult(embargoes, type); } else if (dataAccessCategory.equals(attribute)) { String datastreamId = getDatastreamID(context); if (datastreamId == null) { return new EvaluationResult(BagAttribute.createEmptyBag(type)); } else { List<String> categories = getAccessControlUtils().getDatastreamCategories(datastreamId); return makeStringBagResult(categories, type); } } else if (isPublished.equals(attribute)) { List<String> statuses = new ArrayList<String>(1); statuses.add(Boolean.toString(accessControlUtils.isPublished(new PID(pid)))); return makeStringBagResult(statuses, type); } else if (isActive.equals(attribute)) { List<String> statuses = new ArrayList<String>(1); statuses.add(Boolean.toString(accessControlUtils.isActive(new PID(pid)))); return makeStringBagResult(statuses, type); } return new EvaluationResult(BagAttribute.createEmptyBag(type)); } private EvaluationResult makeStringBagResult(Collection<String> values, URI type) { Set<AttributeValue> bagValues = new HashSet<AttributeValue>(); for (String s : values) { AttributeValue attributeValue = null; try { attributeValue = attributeFactory.createValue(type, s); } catch (Exception e) { log.error("Error creating attribute: {}", e.getMessage(), e); continue; } bagValues.add(attributeValue); } BagAttribute bag = new BagAttribute(type, bagValues); return new EvaluationResult(bag); } public AccessControlUtils getAccessControlUtils() { return accessControlUtils; } public void setAccessControlUtils(AccessControlUtils accessControlUtils) { this.accessControlUtils = accessControlUtils; } }