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 LOGGER.error("Failed to encode policy to json", e);
204 throw new ToscaPolicyConversionException(e);
206 addObligation(rule, jsonPolicy);
208 // Add the rule to the policy
210 newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(rule);
212 // Return our new policy
214 return newPolicyType;
218 * From the TOSCA metadata section, pull in values that are needed into the XACML policy.
220 * @param policy Policy Object to store the metadata
221 * @param map The Metadata TOSCA Map
222 * @return Same Policy Object
223 * @throws ToscaPolicyConversionException If there is something missing from the metadata
225 protected PolicyType fillMetadataSection(PolicyType policy,
226 Map<String, String> map) throws ToscaPolicyConversionException {
227 if (! map.containsKey("policy-id")) {
228 throw new ToscaPolicyConversionException(policy.getPolicyId() + " missing metadata policy-id");
231 // Do nothing here - the XACML PolicyId is used from TOSCA Policy Name field
234 if (! map.containsKey("policy-version")) {
235 throw new ToscaPolicyConversionException(policy.getPolicyId() + " missing metadata policy-version");
238 // Add in the Policy Version
240 policy.setVersion(map.get("policy-version").toString());
246 * For generating target type, we are making an assumption that the
247 * policyScope and policyType are the fields that OOF wants to match on.
249 * <P>In the future, we would need to receive the Policy Type specification
250 * from the PAP so we can dynamically see which fields are matchable.
252 * <P>Note: I am making an assumption that the matchable fields are what
253 * the OOF wants to query a policy on.
255 * @param properties Properties section of policy
256 * @return TargetType object
258 @SuppressWarnings("unchecked")
259 protected TargetType generateTargetType(Map<String, Object> properties) {
260 TargetType targetType = new TargetType();
262 // Iterate the properties
264 for (Entry<String, Object> entrySet : properties.entrySet()) {
266 // Find policyScope and policyType
268 if (entrySet.getKey().equals("policyScope")) {
269 LOGGER.debug("Found policyScope: {}", entrySet.getValue());
270 if (entrySet.getValue() instanceof Collection) {
271 targetType.getAnyOf().add(generateMatches((Collection<Object>) entrySet.getValue(),
272 ToscaDictionary.ID_RESOURCE_POLICY_SCOPE_PROPERTY));
273 } else if (entrySet.getValue() instanceof String) {
274 targetType.getAnyOf().add(generateMatches(Arrays.asList(entrySet.getValue()),
275 ToscaDictionary.ID_RESOURCE_POLICY_SCOPE_PROPERTY));
278 if (entrySet.getKey().equals("policyType")) {
279 LOGGER.debug("Found policyType: {}", entrySet.getValue());
280 if (entrySet.getValue() instanceof Collection) {
281 targetType.getAnyOf().add(generateMatches((Collection<Object>) entrySet.getValue(),
282 ToscaDictionary.ID_RESOURCE_POLICY_TYPE_PROPERTY));
283 } else if (entrySet.getValue() instanceof String) {
284 targetType.getAnyOf().add(generateMatches(Arrays.asList(entrySet.getValue()),
285 ToscaDictionary.ID_RESOURCE_POLICY_TYPE_PROPERTY));
293 protected AnyOfType generateMatches(Collection<Object> matchables, Identifier attributeId) {
295 // This is our outer AnyOf - which is an OR
297 AnyOfType anyOf = new AnyOfType();
298 for (Object matchable : matchables) {
300 // Create a match for this
302 MatchType match = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
303 XACML3.ID_FUNCTION_STRING_EQUAL,
304 matchable.toString(),
305 XACML3.ID_DATATYPE_STRING,
307 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
309 // Now create an anyOf (OR)
311 anyOf.getAllOf().add(ToscaPolicyTranslatorUtils.buildAllOf(match));
316 protected RuleType addObligation(RuleType rule, String jsonPolicy) {
318 // Convert the YAML Policy to JSON Object
320 if (LOGGER.isDebugEnabled()) {
321 LOGGER.debug("JSON Optimization Policy {}{}", System.lineSeparator(), jsonPolicy);
324 // Create an AttributeValue for it
326 AttributeValueType value = new AttributeValueType();
327 value.setDataType(ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_DATATYPE.stringValue());
328 value.getContent().add(jsonPolicy.toString());
330 // Create our AttributeAssignmentExpression where we will
331 // store the contents of the policy in JSON format.
333 AttributeAssignmentExpressionType expressionType = new AttributeAssignmentExpressionType();
334 expressionType.setAttributeId(ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_CONTENTS.stringValue());
335 ObjectFactory factory = new ObjectFactory();
336 expressionType.setExpression(factory.createAttributeValue(value));
338 // Create an ObligationExpression for it
340 ObligationExpressionType obligation = new ObligationExpressionType();
341 obligation.setFulfillOn(EffectType.PERMIT);
342 obligation.setObligationId(ToscaDictionary.ID_OBLIGATION_REST_BODY.stringValue());
343 obligation.getAttributeAssignmentExpression().add(expressionType);
345 // Now we can add it into the rule
347 ObligationExpressionsType obligations = new ObligationExpressionsType();
348 obligations.getObligationExpression().add(obligation);
349 rule.setObligationExpressions(obligations);