/** * Licensed to The Apereo Foundation under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * * The Apereo Foundation licenses this file to you under the Educational * Community 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://opensource.org/licenses/ecl2.txt * * 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.opencastproject.authorization.xacml; import org.opencastproject.mediapackage.MediaPackage; import org.opencastproject.security.api.AccessControlEntry; import org.opencastproject.security.api.AccessControlList; import org.jboss.security.xacml.core.model.policy.ActionMatchType; import org.jboss.security.xacml.core.model.policy.ActionType; import org.jboss.security.xacml.core.model.policy.ActionsType; import org.jboss.security.xacml.core.model.policy.ApplyType; import org.jboss.security.xacml.core.model.policy.AttributeDesignatorType; import org.jboss.security.xacml.core.model.policy.AttributeValueType; import org.jboss.security.xacml.core.model.policy.ConditionType; import org.jboss.security.xacml.core.model.policy.EffectType; import org.jboss.security.xacml.core.model.policy.ObjectFactory; import org.jboss.security.xacml.core.model.policy.PolicyType; import org.jboss.security.xacml.core.model.policy.ResourceMatchType; import org.jboss.security.xacml.core.model.policy.ResourceType; import org.jboss.security.xacml.core.model.policy.ResourcesType; import org.jboss.security.xacml.core.model.policy.RuleType; import org.jboss.security.xacml.core.model.policy.SubjectAttributeDesignatorType; import org.jboss.security.xacml.core.model.policy.TargetType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.InputStream; import java.io.StringWriter; import java.util.List; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; /** * Utility implementation for dealing with XACML data. */ public final class XACMLUtils { /** XACML rule for combining policies */ public static final String RULE_COMBINING_ALG = "urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:permit-overrides"; /** XACML urn for actions */ public static final String ACTION_IDENTIFIER = "urn:oasis:names:tc:xacml:1.0:action:action-id"; /** XACML urn for resources */ public static final String RESOURCE_IDENTIFIER = "urn:oasis:names:tc:xacml:1.0:resource:resource-id"; /** XACML urn for subject */ public static final String SUBJECT_IDENTIFIER = "urn:oasis:names:tc:xacml:1.0:subject:subject-id"; /** XACML urn for roles */ public static final String SUBJECT_ROLE_IDENTIFIER = "urn:oasis:names:tc:xacml:2.0:subject:role"; /** XACML urn for string equality */ public static final String XACML_STRING_EQUAL = "urn:oasis:names:tc:xacml:1.0:function:string-equal"; /** XACML urn for string equality */ public static final String XACML_STRING_IS_IN = "urn:oasis:names:tc:xacml:1.0:function:string-is-in"; /** W3C String data type */ public static final String W3C_STRING = "http://www.w3.org/2001/XMLSchema#string"; /** The policy assertion issuer */ public static final String ISSUER = "matterhorn"; /** The JAXB Context to use for marshaling XACML security policy documents */ protected static JAXBContext jBossXacmlJaxbContext; /** The logging facility */ private static final Logger logger = LoggerFactory.getLogger(XACMLUtils.class); /** Static initializer for the single JAXB context */ static { try { XACMLUtils.jBossXacmlJaxbContext = JAXBContext.newInstance("org.jboss.security.xacml.core.model.policy", PolicyType.class.getClassLoader()); } catch (JAXBException e) { throw new RuntimeException(e); } } /** * Private constructor to disable clients from instantiating this class. */ private XACMLUtils() { } /** * Parses a XACML into an {@link AccessControlList}. * <p> * Only rules which follow the structure of those created by {@link #getXacml(MediaPackage, AccessControlList)} may be * successfully parsed. All other rules are ignored. * * @param xacml * the XACML to parse * @return the ACL, never {@code null} * @throws JAXBException * if unmarshalling fails */ public static AccessControlList parseXacml(InputStream xacml) throws JAXBException { @SuppressWarnings("unchecked") final PolicyType policy = ((JAXBElement<PolicyType>) XACMLUtils.jBossXacmlJaxbContext.createUnmarshaller().unmarshal(xacml)).getValue(); final AccessControlList acl = new AccessControlList(); final List<AccessControlEntry> entries = acl.getEntries(); for (Object object : policy.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition()) { if (object instanceof RuleType) { RuleType rule = (RuleType) object; if (rule.getTarget() == null) { continue; } ActionType action = rule.getTarget().getActions().getAction().get(0); String actionForAce = (String) action.getActionMatch().get(0).getAttributeValue().getContent().get(0); String role = null; @SuppressWarnings("unchecked") JAXBElement<ApplyType> apply = (JAXBElement<ApplyType>) rule.getCondition().getExpression(); for (JAXBElement<?> element : apply.getValue().getExpression()) { if (element.getValue() instanceof AttributeValueType) { role = (String) ((AttributeValueType) element.getValue()).getContent().get(0); break; } } if (role == null) { logger.warn("Unable to find a role in rule {}", rule); continue; } AccessControlEntry ace = new AccessControlEntry(role, actionForAce, rule.getEffect().equals(EffectType.PERMIT)); entries.add(ace); } else { logger.debug("XACML rule '{}' out of policy '{} could not be parsed to ACE", object, policy); } } return acl; } /** * Builds an xml string containing the xacml for the mediapackage. * * @param mediapackage * the mediapackage * @param accessControlList * the tuples of roles to actions * @return * @throws JAXBException */ public static String getXacml(MediaPackage mediapackage, AccessControlList accessControlList) throws JAXBException { ObjectFactory jbossXacmlObjectFactory = new ObjectFactory(); PolicyType policy = new PolicyType(); policy.setPolicyId(mediapackage.getIdentifier().toString()); policy.setVersion("2.0"); policy.setRuleCombiningAlgId(XACMLUtils.RULE_COMBINING_ALG); // TODO: Add target/resources to rule TargetType policyTarget = new TargetType(); ResourcesType resources = new ResourcesType(); ResourceType resource = new ResourceType(); ResourceMatchType resourceMatch = new ResourceMatchType(); resourceMatch.setMatchId(XACMLUtils.XACML_STRING_EQUAL); AttributeValueType resourceAttributeValue = new AttributeValueType(); resourceAttributeValue.setDataType(XACMLUtils.W3C_STRING); resourceAttributeValue.getContent().add(mediapackage.getIdentifier().toString()); AttributeDesignatorType resourceDesignator = new AttributeDesignatorType(); resourceDesignator.setAttributeId(XACMLUtils.RESOURCE_IDENTIFIER); resourceDesignator.setDataType(XACMLUtils.W3C_STRING); // now go back up the tree resourceMatch.setResourceAttributeDesignator(resourceDesignator); resourceMatch.setAttributeValue(resourceAttributeValue); resource.getResourceMatch().add(resourceMatch); resources.getResource().add(resource); policyTarget.setResources(resources); policy.setTarget(policyTarget); // Loop over roleActions and add a rule for each for (AccessControlEntry ace : accessControlList.getEntries()) { boolean allow = ace.isAllow(); RuleType rule = new RuleType(); rule.setRuleId(ace.getRole() + "_" + ace.getAction() + (allow ? "_Permit" : "_Deny")); if (allow) { rule.setEffect(EffectType.PERMIT); } else { rule.setEffect(EffectType.DENY); } TargetType target = new TargetType(); ActionsType actions = new ActionsType(); ActionType action = new ActionType(); ActionMatchType actionMatch = new ActionMatchType(); actionMatch.setMatchId(XACMLUtils.XACML_STRING_EQUAL); AttributeValueType attributeValue = new AttributeValueType(); attributeValue.setDataType(XACMLUtils.W3C_STRING); attributeValue.getContent().add(ace.getAction()); AttributeDesignatorType designator = new AttributeDesignatorType(); designator.setAttributeId(XACMLUtils.ACTION_IDENTIFIER); designator.setDataType(XACMLUtils.W3C_STRING); // now go back up the tree actionMatch.setActionAttributeDesignator(designator); actionMatch.setAttributeValue(attributeValue); action.getActionMatch().add(actionMatch); actions.getAction().add(action); target.setActions(actions); rule.setTarget(target); ConditionType condition = new ConditionType(); ApplyType apply = new ApplyType(); apply.setFunctionId(XACMLUtils.XACML_STRING_IS_IN); AttributeValueType conditionAttributeValue = new AttributeValueType(); conditionAttributeValue.setDataType(XACMLUtils.W3C_STRING); conditionAttributeValue.getContent().add(ace.getRole()); SubjectAttributeDesignatorType subjectDesignator = new SubjectAttributeDesignatorType(); subjectDesignator.setDataType(XACMLUtils.W3C_STRING); subjectDesignator.setAttributeId(XACMLUtils.SUBJECT_ROLE_IDENTIFIER); apply.getExpression().add(jbossXacmlObjectFactory.createAttributeValue(conditionAttributeValue)); apply.getExpression().add(jbossXacmlObjectFactory.createSubjectAttributeDesignator(subjectDesignator)); condition.setExpression(jbossXacmlObjectFactory.createApply(apply)); rule.setCondition(condition); policy.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(rule); } // Add the global deny rule RuleType deny = new RuleType(); deny.setEffect(EffectType.DENY); deny.setRuleId("DenyRule"); policy.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(deny); // serialize to xml StringWriter writer = new StringWriter(); XACMLUtils.jBossXacmlJaxbContext.createMarshaller().marshal(jbossXacmlObjectFactory.createPolicy(policy), writer); return writer.getBuffer().toString(); } }