2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 * SPDX-License-Identifier: Apache-2.0
20 * ============LICENSE_END=========================================================
23 package org.onap.policy.pdp.xacml.application.common.std;
25 import com.att.research.xacml.api.AttributeAssignment;
26 import com.att.research.xacml.api.DataTypeException;
27 import com.att.research.xacml.api.Decision;
28 import com.att.research.xacml.api.Identifier;
29 import com.att.research.xacml.api.Obligation;
30 import com.att.research.xacml.api.Request;
31 import com.att.research.xacml.api.Response;
32 import com.att.research.xacml.api.Result;
33 import com.att.research.xacml.api.XACML3;
34 import com.att.research.xacml.std.annotations.RequestParser;
35 import com.google.gson.Gson;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.Collection;
41 import java.util.Map.Entry;
43 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType;
44 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpressionType;
45 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType;
46 import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType;
47 import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType;
48 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObjectFactory;
49 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionType;
50 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionsType;
51 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType;
52 import oasis.names.tc.xacml._3_0.core.schema.wd_17.RuleType;
53 import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType;
55 import org.onap.policy.common.utils.coder.CoderException;
56 import org.onap.policy.common.utils.coder.StandardCoder;
57 import org.onap.policy.models.decisions.concepts.DecisionRequest;
58 import org.onap.policy.models.decisions.concepts.DecisionResponse;
59 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
60 import org.onap.policy.pdp.xacml.application.common.ToscaDictionary;
61 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException;
62 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslator;
63 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslatorUtils;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
67 public class StdMatchableTranslator implements ToscaPolicyTranslator {
69 private static final Logger LOGGER = LoggerFactory.getLogger(StdMatchableTranslator.class);
71 public StdMatchableTranslator() {
76 public Request convertRequest(DecisionRequest request) {
77 LOGGER.debug("Converting Request {}", request);
79 return RequestParser.parseRequest(StdMatchablePolicyRequest.createInstance(request));
80 } catch (IllegalArgumentException | IllegalAccessException | DataTypeException e) {
81 LOGGER.error("Failed to convert DecisionRequest: {}", e);
84 // TODO throw exception
90 public DecisionResponse convertResponse(Response xacmlResponse) {
91 LOGGER.debug("Converting Response {}", xacmlResponse);
92 DecisionResponse decisionResponse = new DecisionResponse();
94 // Iterate through all the results
96 for (Result xacmlResult : xacmlResponse.getResults()) {
100 if (xacmlResult.getDecision() == Decision.PERMIT) {
104 decisionResponse.setPolicies(new ArrayList<>());
106 // Go through obligations
108 scanObligations(xacmlResult.getObligations(), decisionResponse);
110 if (xacmlResult.getDecision() == Decision.NOTAPPLICABLE) {
112 // There is no policy
114 decisionResponse.setPolicies(new ArrayList<>());
116 if (xacmlResult.getDecision() == Decision.DENY
117 || xacmlResult.getDecision() == Decision.INDETERMINATE) {
119 // TODO we have to return an ErrorResponse object instead
121 decisionResponse.setStatus("A better error message");
125 return decisionResponse;
128 protected void scanObligations(Collection<Obligation> obligations, DecisionResponse decisionResponse) {
129 for (Obligation obligation : obligations) {
130 LOGGER.debug("Obligation: {}", obligation);
131 for (AttributeAssignment assignment : obligation.getAttributeAssignments()) {
132 LOGGER.debug("Attribute Assignment: {}", assignment);
134 // We care about the content attribute
136 if (ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_CONTENTS
137 .equals(assignment.getAttributeId())) {
139 // The contents are in Json form
141 Object stringContents = assignment.getAttributeValue().getValue();
142 if (LOGGER.isDebugEnabled()) {
143 LOGGER.debug("Policy contents: {}{}", System.lineSeparator(), stringContents);
146 // Let's parse it into a map using Gson
148 Gson gson = new Gson();
149 @SuppressWarnings("unchecked")
150 Map<String, Object> result = gson.fromJson(stringContents.toString() ,Map.class);
151 decisionResponse.getPolicies().add(result);
159 public PolicyType convertPolicy(ToscaPolicy toscaPolicy) throws ToscaPolicyConversionException {
161 // Policy name should be at the root
163 String policyName = toscaPolicy.getMetadata().get("policy-id");
165 // Set it as the policy ID
167 PolicyType newPolicyType = new PolicyType();
168 newPolicyType.setPolicyId(policyName);
170 // Optional description
172 newPolicyType.setDescription(toscaPolicy.getDescription());
174 // There should be a metadata section
176 this.fillMetadataSection(newPolicyType, toscaPolicy.getMetadata());
178 // Set the combining rule
180 newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_FIRST_APPLICABLE.stringValue());
182 // Generate the TargetType
184 newPolicyType.setTarget(generateTargetType(toscaPolicy.getProperties()));
186 // Now create the Permit Rule
187 // No target since the policy has a target
190 RuleType rule = new RuleType();
191 rule.setDescription("Default is to PERMIT if the policy matches.");
192 rule.setRuleId(policyName + ":rule");
193 rule.setEffect(EffectType.PERMIT);
194 rule.setTarget(new TargetType());
196 // Now represent the policy as Json
198 StandardCoder coder = new StandardCoder();
201 jsonPolicy = coder.encode(toscaPolicy);
202 } catch (CoderException e) {
203 throw new ToscaPolicyConversionException("Failed to encode policy to json", e);
205 addObligation(rule, jsonPolicy);
207 // Add the rule to the policy
209 newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(rule);
211 // Return our new policy
213 return newPolicyType;
217 * From the TOSCA metadata section, pull in values that are needed into the XACML policy.
219 * @param policy Policy Object to store the metadata
220 * @param map The Metadata TOSCA Map
221 * @return Same Policy Object
222 * @throws ToscaPolicyConversionException If there is something missing from the metadata
224 protected PolicyType fillMetadataSection(PolicyType policy,
225 Map<String, String> map) throws ToscaPolicyConversionException {
226 if (! map.containsKey("policy-id")) {
227 throw new ToscaPolicyConversionException(policy.getPolicyId() + " missing metadata policy-id");
230 // Do nothing here - the XACML PolicyId is used from TOSCA Policy Name field
233 if (! map.containsKey("policy-version")) {
234 throw new ToscaPolicyConversionException(policy.getPolicyId() + " missing metadata policy-version");
237 // Add in the Policy Version
239 policy.setVersion(map.get("policy-version"));
245 * For generating target type, we are making an assumption that the
246 * policyScope and policyType are the fields that OOF wants to match on.
248 * <P>In the future, we would need to receive the Policy Type specification
249 * from the PAP so we can dynamically see which fields are matchable.
251 * <P>Note: I am making an assumption that the matchable fields are what
252 * the OOF wants to query a policy on.
254 * @param properties Properties section of policy
255 * @return TargetType object
257 @SuppressWarnings("unchecked")
258 protected TargetType generateTargetType(Map<String, Object> properties) {
259 TargetType targetType = new TargetType();
261 // Iterate the properties
263 for (Entry<String, Object> entrySet : properties.entrySet()) {
265 // Find policyScope and policyType
267 if (entrySet.getKey().equals("policyScope")) {
268 LOGGER.debug("Found policyScope: {}", entrySet.getValue());
269 if (entrySet.getValue() instanceof Collection) {
270 targetType.getAnyOf().add(generateMatches((Collection<Object>) entrySet.getValue(),
271 ToscaDictionary.ID_RESOURCE_POLICY_SCOPE_PROPERTY));
272 } else if (entrySet.getValue() instanceof String) {
273 targetType.getAnyOf().add(generateMatches(Arrays.asList(entrySet.getValue()),
274 ToscaDictionary.ID_RESOURCE_POLICY_SCOPE_PROPERTY));
277 if (entrySet.getKey().equals("policyType")) {
278 LOGGER.debug("Found policyType: {}", entrySet.getValue());
279 if (entrySet.getValue() instanceof Collection) {
280 targetType.getAnyOf().add(generateMatches((Collection<Object>) entrySet.getValue(),
281 ToscaDictionary.ID_RESOURCE_POLICY_TYPE_PROPERTY));
282 } else if (entrySet.getValue() instanceof String) {
283 targetType.getAnyOf().add(generateMatches(Arrays.asList(entrySet.getValue()),
284 ToscaDictionary.ID_RESOURCE_POLICY_TYPE_PROPERTY));
292 protected AnyOfType generateMatches(Collection<Object> matchables, Identifier attributeId) {
294 // This is our outer AnyOf - which is an OR
296 AnyOfType anyOf = new AnyOfType();
297 for (Object matchable : matchables) {
299 // Create a match for this
301 MatchType match = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
302 XACML3.ID_FUNCTION_STRING_EQUAL,
303 matchable.toString(),
304 XACML3.ID_DATATYPE_STRING,
306 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
308 // Now create an anyOf (OR)
310 anyOf.getAllOf().add(ToscaPolicyTranslatorUtils.buildAllOf(match));
315 protected RuleType addObligation(RuleType rule, String jsonPolicy) {
317 // Convert the YAML Policy to JSON Object
319 if (LOGGER.isDebugEnabled()) {
320 LOGGER.debug("JSON Optimization Policy {}{}", System.lineSeparator(), jsonPolicy);
323 // Create an AttributeValue for it
325 AttributeValueType value = new AttributeValueType();
326 value.setDataType(ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_DATATYPE.stringValue());
327 value.getContent().add(jsonPolicy);
329 // Create our AttributeAssignmentExpression where we will
330 // store the contents of the policy in JSON format.
332 AttributeAssignmentExpressionType expressionType = new AttributeAssignmentExpressionType();
333 expressionType.setAttributeId(ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_CONTENTS.stringValue());
334 ObjectFactory factory = new ObjectFactory();
335 expressionType.setExpression(factory.createAttributeValue(value));
337 // Create an ObligationExpression for it
339 ObligationExpressionType obligation = new ObligationExpressionType();
340 obligation.setFulfillOn(EffectType.PERMIT);
341 obligation.setObligationId(ToscaDictionary.ID_OBLIGATION_REST_BODY.stringValue());
342 obligation.getAttributeAssignmentExpression().add(expressionType);
344 // Now we can add it into the rule
346 ObligationExpressionsType obligations = new ObligationExpressionsType();
347 obligations.getObligationExpression().add(obligation);
348 rule.setObligationExpressions(obligations);