View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  
22  package org.opencastproject.authorization.xacml;
23  
24  import org.opencastproject.mediapackage.MediaPackage;
25  import org.opencastproject.security.api.AccessControlEntry;
26  import org.opencastproject.security.api.AccessControlList;
27  import org.opencastproject.util.XmlSafeParser;
28  
29  import org.jboss.security.xacml.core.model.policy.ActionMatchType;
30  import org.jboss.security.xacml.core.model.policy.ActionType;
31  import org.jboss.security.xacml.core.model.policy.ActionsType;
32  import org.jboss.security.xacml.core.model.policy.ApplyType;
33  import org.jboss.security.xacml.core.model.policy.AttributeDesignatorType;
34  import org.jboss.security.xacml.core.model.policy.AttributeValueType;
35  import org.jboss.security.xacml.core.model.policy.ConditionType;
36  import org.jboss.security.xacml.core.model.policy.EffectType;
37  import org.jboss.security.xacml.core.model.policy.ObjectFactory;
38  import org.jboss.security.xacml.core.model.policy.PolicyType;
39  import org.jboss.security.xacml.core.model.policy.ResourceMatchType;
40  import org.jboss.security.xacml.core.model.policy.ResourceType;
41  import org.jboss.security.xacml.core.model.policy.ResourcesType;
42  import org.jboss.security.xacml.core.model.policy.RuleType;
43  import org.jboss.security.xacml.core.model.policy.SubjectAttributeDesignatorType;
44  import org.jboss.security.xacml.core.model.policy.TargetType;
45  import org.slf4j.Logger;
46  import org.slf4j.LoggerFactory;
47  
48  import java.io.InputStream;
49  import java.io.StringWriter;
50  import java.util.List;
51  
52  import javax.xml.bind.JAXBContext;
53  import javax.xml.bind.JAXBElement;
54  import javax.xml.bind.JAXBException;
55  
56  /**
57   * Utility implementation for dealing with XACML data.
58   */
59  public final class XACMLUtils {
60  
61    /** XACML rule for combining policies */
62    public static final String RULE_COMBINING_ALG
63        = "urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:permit-overrides";
64    /** XACML urn for actions */
65    public static final String ACTION_IDENTIFIER = "urn:oasis:names:tc:xacml:1.0:action:action-id";
66    /** XACML urn for resources */
67    public static final String RESOURCE_IDENTIFIER = "urn:oasis:names:tc:xacml:1.0:resource:resource-id";
68    /** XACML urn for subject */
69    public static final String SUBJECT_IDENTIFIER = "urn:oasis:names:tc:xacml:1.0:subject:subject-id";
70    /** XACML urn for roles */
71    public static final String SUBJECT_ROLE_IDENTIFIER = "urn:oasis:names:tc:xacml:2.0:subject:role";
72    /** XACML urn for string equality */
73    public static final String XACML_STRING_EQUAL = "urn:oasis:names:tc:xacml:1.0:function:string-equal";
74    /** XACML urn for string equality */
75    public static final String XACML_STRING_IS_IN = "urn:oasis:names:tc:xacml:1.0:function:string-is-in";
76    /** W3C String data type */
77    public static final String W3C_STRING = "http://www.w3.org/2001/XMLSchema#string";
78    /** The policy assertion issuer */
79    public static final String ISSUER = "matterhorn";
80    /** The JAXB Context to use for marshaling XACML security policy documents */
81    protected static JAXBContext jBossXacmlJaxbContext;
82    /** The logging facility */
83    private static final Logger logger = LoggerFactory.getLogger(XACMLUtils.class);
84  
85    /** Static initializer for the single JAXB context */
86    static {
87      try {
88        XACMLUtils.jBossXacmlJaxbContext = JAXBContext.newInstance("org.jboss.security.xacml.core.model.policy",
89                PolicyType.class.getClassLoader());
90      } catch (JAXBException e) {
91        throw new RuntimeException(e);
92      }
93    }
94  
95    /**
96     * Private constructor to disable clients from instantiating this class.
97     */
98    private XACMLUtils() {
99    }
100 
101   /**
102    * Parses a XACML into an {@link AccessControlList}.
103    * <p>
104    * Only rules which follow the structure of those created by {@link #getXacml(MediaPackage, AccessControlList)} may be
105    * successfully parsed. All other rules are ignored.
106    * 
107    * @param xacml
108    *          the XACML to parse
109    * @return the ACL, never {@code null}
110    * @throws XACMLParsingException
111    *           if parsing fails
112    */
113   public static AccessControlList parseXacml(InputStream xacml) throws XACMLParsingException {
114 
115     try {
116       @SuppressWarnings("unchecked")
117       final AccessControlList acl = new AccessControlList();
118       final List<AccessControlEntry> entries = acl.getEntries();
119       final PolicyType policy = ((JAXBElement<PolicyType>) XACMLUtils.jBossXacmlJaxbContext
120           .createUnmarshaller()
121           .unmarshal(XmlSafeParser.parse(xacml)))
122           .getValue();
123 
124       for (Object object : policy.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition()) {
125         if (!(object instanceof RuleType)) {
126           throw new XACMLParsingException("Object " + object + " of policy " + policy + " is not of type RuleType");
127         }
128         RuleType rule = (RuleType) object;
129         if (rule.getTarget() == null) {
130           if (rule.getRuleId().equals("DenyRule")) {
131             logger.trace("Skipping global deny rule");
132             continue;
133           }
134           throw new XACMLParsingException("Empty rule " + rule + " in policy " + policy);
135         }
136 
137         String role = null;
138         String actionForAce = null;
139         try {
140           ActionType action = rule.getTarget().getActions().getAction().get(0);
141           actionForAce = (String) action.getActionMatch().get(0).getAttributeValue().getContent().get(0);
142 
143           @SuppressWarnings("unchecked") JAXBElement<ApplyType> apply
144               = (JAXBElement<ApplyType>) rule.getCondition().getExpression();
145           for (JAXBElement<?> element : apply.getValue().getExpression()) {
146             if (element.getValue() instanceof AttributeValueType) {
147               role = (String) ((AttributeValueType) element.getValue()).getContent().get(0);
148               break;
149             }
150           }
151         } catch (Exception e) {
152           throw new XACMLParsingException("Rule " + rule + " of policy " + policy + " could not be parsed", e);
153         }
154         if (role == null) {
155           throw new XACMLParsingException("Unable to find role in rule " + rule + " of policy " + policy);
156         }
157         AccessControlEntry ace = new AccessControlEntry(role, actionForAce, rule.getEffect().equals(EffectType.PERMIT));
158         entries.add(ace);
159       }
160       return acl;
161     } catch (Exception e) {
162       if (e instanceof XACMLParsingException) {
163         throw (XACMLParsingException) e;
164       }
165       throw new XACMLParsingException("XACML could not be parsed", e);
166     }
167   }
168 
169   /**
170    * Builds an xml string containing the xacml for the mediapackage.
171    *
172    * @param mediapackage
173    *          the mediapackage
174    * @param accessControlList
175    *          the tuples of roles to actions
176    * @return
177    * @throws JAXBException
178    */
179   public static String getXacml(MediaPackage mediapackage, AccessControlList accessControlList) throws JAXBException {
180     ObjectFactory jbossXacmlObjectFactory = new ObjectFactory();
181     PolicyType policy = new PolicyType();
182     policy.setPolicyId(mediapackage.getIdentifier().toString());
183     policy.setVersion("2.0");
184     policy.setRuleCombiningAlgId(XACMLUtils.RULE_COMBINING_ALG);
185 
186     // TODO: Add target/resources to rule
187     TargetType policyTarget = new TargetType();
188     ResourcesType resources = new ResourcesType();
189     ResourceType resource = new ResourceType();
190     ResourceMatchType resourceMatch = new ResourceMatchType();
191     resourceMatch.setMatchId(XACMLUtils.XACML_STRING_EQUAL);
192     AttributeValueType resourceAttributeValue = new AttributeValueType();
193     resourceAttributeValue.setDataType(XACMLUtils.W3C_STRING);
194     resourceAttributeValue.getContent().add(mediapackage.getIdentifier().toString());
195     AttributeDesignatorType resourceDesignator = new AttributeDesignatorType();
196     resourceDesignator.setAttributeId(XACMLUtils.RESOURCE_IDENTIFIER);
197     resourceDesignator.setDataType(XACMLUtils.W3C_STRING);
198 
199     // now go back up the tree
200     resourceMatch.setResourceAttributeDesignator(resourceDesignator);
201     resourceMatch.setAttributeValue(resourceAttributeValue);
202     resource.getResourceMatch().add(resourceMatch);
203     resources.getResource().add(resource);
204     policyTarget.setResources(resources);
205     policy.setTarget(policyTarget);
206 
207     // Loop over roleActions and add a rule for each
208     for (AccessControlEntry ace : accessControlList.getEntries()) {
209       boolean allow = ace.isAllow();
210 
211       RuleType rule = new RuleType();
212       rule.setRuleId(ace.getRole() + "_" + ace.getAction() + (allow ? "_Permit" : "_Deny"));
213       if (allow) {
214         rule.setEffect(EffectType.PERMIT);
215       } else {
216         rule.setEffect(EffectType.DENY);
217       }
218 
219       TargetType target = new TargetType();
220       ActionsType actions = new ActionsType();
221       ActionType action = new ActionType();
222       ActionMatchType actionMatch = new ActionMatchType();
223       actionMatch.setMatchId(XACMLUtils.XACML_STRING_EQUAL);
224       AttributeValueType attributeValue = new AttributeValueType();
225       attributeValue.setDataType(XACMLUtils.W3C_STRING);
226       attributeValue.getContent().add(ace.getAction());
227       AttributeDesignatorType designator = new AttributeDesignatorType();
228       designator.setAttributeId(XACMLUtils.ACTION_IDENTIFIER);
229       designator.setDataType(XACMLUtils.W3C_STRING);
230 
231       // now go back up the tree
232       actionMatch.setActionAttributeDesignator(designator);
233       actionMatch.setAttributeValue(attributeValue);
234       action.getActionMatch().add(actionMatch);
235       actions.getAction().add(action);
236       target.setActions(actions);
237       rule.setTarget(target);
238 
239       ConditionType condition = new ConditionType();
240       ApplyType apply = new ApplyType();
241       apply.setFunctionId(XACMLUtils.XACML_STRING_IS_IN);
242 
243       AttributeValueType conditionAttributeValue = new AttributeValueType();
244       conditionAttributeValue.setDataType(XACMLUtils.W3C_STRING);
245       conditionAttributeValue.getContent().add(ace.getRole());
246 
247       SubjectAttributeDesignatorType subjectDesignator = new SubjectAttributeDesignatorType();
248       subjectDesignator.setDataType(XACMLUtils.W3C_STRING);
249       subjectDesignator.setAttributeId(XACMLUtils.SUBJECT_ROLE_IDENTIFIER);
250       apply.getExpression().add(jbossXacmlObjectFactory.createAttributeValue(conditionAttributeValue));
251       apply.getExpression().add(jbossXacmlObjectFactory.createSubjectAttributeDesignator(subjectDesignator));
252 
253       condition.setExpression(jbossXacmlObjectFactory.createApply(apply));
254       rule.setCondition(condition);
255       policy.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(rule);
256     }
257 
258     // Add the global deny rule
259     RuleType deny = new RuleType();
260     deny.setEffect(EffectType.DENY);
261     deny.setRuleId("DenyRule");
262     policy.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(deny);
263 
264     // serialize to xml
265     StringWriter writer = new StringWriter();
266     XACMLUtils.jBossXacmlJaxbContext.createMarshaller().marshal(jbossXacmlObjectFactory.createPolicy(policy), writer);
267     return writer.getBuffer().toString();
268   }
269 
270 }