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.att.research.xacml.util.XACMLPolicyWriter;
36 import com.google.gson.Gson;
38 import java.io.ByteArrayOutputStream;
39 import java.io.IOException;
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.Collection;
43 import java.util.List;
45 import java.util.Map.Entry;
47 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType;
48 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpressionType;
49 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType;
50 import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType;
51 import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType;
52 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObjectFactory;
53 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionType;
54 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionsType;
55 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType;
56 import oasis.names.tc.xacml._3_0.core.schema.wd_17.RuleType;
57 import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType;
59 import org.json.JSONObject;
60 import org.onap.policy.models.decisions.concepts.DecisionRequest;
61 import org.onap.policy.models.decisions.concepts.DecisionResponse;
62 import org.onap.policy.pdp.xacml.application.common.ToscaDictionary;
63 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException;
64 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslator;
65 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslatorUtils;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
69 public class StdMatchableTranslator implements ToscaPolicyTranslator {
71 private static final Logger LOGGER = LoggerFactory.getLogger(StdMatchableTranslator.class);
73 public StdMatchableTranslator() {
77 @SuppressWarnings("unchecked")
79 public List<PolicyType> scanAndConvertPolicies(Map<String, Object> toscaObject)
80 throws ToscaPolicyConversionException {
84 List<PolicyType> scannedPolicies = new ArrayList<>();
86 // Iterate each of the Policies
88 List<Object> policies = (List<Object>) toscaObject.get("policies");
89 for (Object policyObject : policies) {
93 LOGGER.debug("Found policy {}", policyObject.getClass());
94 Map<String, Object> policyContents = (Map<String, Object>) policyObject;
95 for (Entry<String, Object> entrySet : policyContents.entrySet()) {
96 LOGGER.debug("Entry set {}", entrySet);
98 // Convert this policy
100 PolicyType policy = this.convertPolicy(entrySet);
101 try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
102 XACMLPolicyWriter.writePolicyFile(os, policy);
103 LOGGER.debug("{}", os);
104 } catch (IOException e) {
105 LOGGER.error("Failed to convert {}", e);
108 // Convert and add in the new policy
110 scannedPolicies.add(policy);
114 return scannedPolicies;
118 public Request convertRequest(DecisionRequest request) {
119 LOGGER.debug("Converting Request {}", request);
121 return RequestParser.parseRequest(StdMatchablePolicyRequest.createInstance(request));
122 } catch (IllegalArgumentException | IllegalAccessException | DataTypeException e) {
123 LOGGER.error("Failed to convert DecisionRequest: {}", e);
126 // TODO throw exception
132 public DecisionResponse convertResponse(Response xacmlResponse) {
133 LOGGER.debug("Converting Response {}", xacmlResponse);
134 DecisionResponse decisionResponse = new DecisionResponse();
136 // Iterate through all the results
138 for (Result xacmlResult : xacmlResponse.getResults()) {
142 if (xacmlResult.getDecision() == Decision.PERMIT) {
146 decisionResponse.setPolicies(new ArrayList<>());
148 // Go through obligations
150 scanObligations(xacmlResult.getObligations(), decisionResponse);
152 if (xacmlResult.getDecision() == Decision.NOTAPPLICABLE) {
154 // There is no policy
156 decisionResponse.setPolicies(new ArrayList<>());
158 if (xacmlResult.getDecision() == Decision.DENY
159 || xacmlResult.getDecision() == Decision.INDETERMINATE) {
161 // TODO we have to return an ErrorResponse object instead
163 decisionResponse.setStatus("A better error message");
167 return decisionResponse;
170 protected void scanObligations(Collection<Obligation> obligations, DecisionResponse decisionResponse) {
171 for (Obligation obligation : obligations) {
172 LOGGER.debug("Obligation: {}", obligation);
173 for (AttributeAssignment assignment : obligation.getAttributeAssignments()) {
174 LOGGER.debug("Attribute Assignment: {}", assignment);
176 // We care about the content attribute
178 if (ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_CONTENTS
179 .equals(assignment.getAttributeId())) {
181 // The contents are in Json form
183 Object stringContents = assignment.getAttributeValue().getValue();
184 if (LOGGER.isDebugEnabled()) {
185 LOGGER.debug("DCAE contents: {}{}", System.lineSeparator(), stringContents);
188 // Let's parse it into a map using Gson
190 Gson gson = new Gson();
191 @SuppressWarnings("unchecked")
192 Map<String, Object> result = gson.fromJson(stringContents.toString() ,Map.class);
193 decisionResponse.getPolicies().add(result);
200 @SuppressWarnings("unchecked")
201 protected PolicyType convertPolicy(Entry<String, Object> entrySet) throws ToscaPolicyConversionException {
203 // Policy name should be at the root
205 String policyName = entrySet.getKey();
206 Map<String, Object> policyDefinition = (Map<String, Object>) entrySet.getValue();
208 // Set it as the policy ID
210 PolicyType newPolicyType = new PolicyType();
211 newPolicyType.setPolicyId(policyName);
213 // Optional description
215 if (policyDefinition.containsKey("description")) {
216 newPolicyType.setDescription(policyDefinition.get("description").toString());
219 // There should be a metadata section
221 if (! policyDefinition.containsKey("metadata")) {
222 throw new ToscaPolicyConversionException(policyName + " missing metadata section");
224 this.fillMetadataSection(newPolicyType,
225 (Map<String, Object>) policyDefinition.get("metadata"));
227 // Set the combining rule
229 newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_FIRST_APPLICABLE.stringValue());
231 // Generate the TargetType
233 if (! policyDefinition.containsKey("properties")) {
234 throw new ToscaPolicyConversionException(policyName + " missing properties section");
236 policyDefinition.get("properties");
237 newPolicyType.setTarget(generateTargetType((Map<String, Object>) policyDefinition.get("properties")));
239 // Now create the Permit Rule
240 // No target since the policy has a target
243 RuleType rule = new RuleType();
244 rule.setDescription("Default is to PERMIT if the policy matches.");
245 rule.setRuleId(policyName + ":rule");
246 rule.setEffect(EffectType.PERMIT);
247 rule.setTarget(new TargetType());
249 // Now represent the policy as Json
251 JSONObject jsonObligation = new JSONObject();
252 jsonObligation.put(policyName, policyDefinition);
253 addObligation(rule, jsonObligation);
255 // Add the rule to the policy
257 newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(rule);
259 // Return our new policy
261 return newPolicyType;
265 * From the TOSCA metadata section, pull in values that are needed into the XACML policy.
267 * @param policy Policy Object to store the metadata
268 * @param metadata The Metadata TOSCA Map
269 * @return Same Policy Object
270 * @throws ToscaPolicyConversionException If there is something missing from the metadata
272 protected PolicyType fillMetadataSection(PolicyType policy,
273 Map<String, Object> metadata) throws ToscaPolicyConversionException {
274 if (! metadata.containsKey("policy-id")) {
275 throw new ToscaPolicyConversionException(policy.getPolicyId() + " missing metadata policy-id");
278 // Do nothing here - the XACML PolicyId is used from TOSCA Policy Name field
281 if (! metadata.containsKey("policy-version")) {
282 throw new ToscaPolicyConversionException(policy.getPolicyId() + " missing metadata policy-version");
285 // Add in the Policy Version
287 policy.setVersion(metadata.get("policy-version").toString());
293 * For generating target type, we are making an assumption that the
294 * policyScope and policyType are the fields that OOF wants to match on.
296 * <P>In the future, we would need to receive the Policy Type specification
297 * from the PAP so we can dynamically see which fields are matchable.
299 * <P>Note: I am making an assumption that the matchable fields are what
300 * the OOF wants to query a policy on.
302 * @param properties Properties section of policy
303 * @return TargetType object
305 @SuppressWarnings("unchecked")
306 protected TargetType generateTargetType(Map<String, Object> properties) {
307 TargetType targetType = new TargetType();
309 // Iterate the properties
311 for (Entry<String, Object> entrySet : properties.entrySet()) {
313 // Find policyScope and policyType
315 if (entrySet.getKey().equals("policyScope")) {
316 LOGGER.debug("Found policyScope: {}", entrySet.getValue());
317 if (entrySet.getValue() instanceof Collection) {
318 targetType.getAnyOf().add(generateMatches((Collection<Object>) entrySet.getValue(),
319 ToscaDictionary.ID_RESOURCE_POLICY_SCOPE_PROPERTY));
320 } else if (entrySet.getValue() instanceof String) {
321 targetType.getAnyOf().add(generateMatches(Arrays.asList(entrySet.getValue()),
322 ToscaDictionary.ID_RESOURCE_POLICY_SCOPE_PROPERTY));
325 if (entrySet.getKey().equals("policyType")) {
326 LOGGER.debug("Found policyType: {}", entrySet.getValue());
327 if (entrySet.getValue() instanceof Collection) {
328 targetType.getAnyOf().add(generateMatches((Collection<Object>) entrySet.getValue(),
329 ToscaDictionary.ID_RESOURCE_POLICY_TYPE_PROPERTY));
330 } else if (entrySet.getValue() instanceof String) {
331 targetType.getAnyOf().add(generateMatches(Arrays.asList(entrySet.getValue()),
332 ToscaDictionary.ID_RESOURCE_POLICY_TYPE_PROPERTY));
340 protected AnyOfType generateMatches(Collection<Object> matchables, Identifier attributeId) {
342 // This is our outer AnyOf - which is an OR
344 AnyOfType anyOf = new AnyOfType();
345 for (Object matchable : matchables) {
347 // Create a match for this
349 MatchType match = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
350 XACML3.ID_FUNCTION_STRING_EQUAL,
351 matchable.toString(),
352 XACML3.ID_DATATYPE_STRING,
354 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
356 // Now create an anyOf (OR)
358 anyOf.getAllOf().add(ToscaPolicyTranslatorUtils.buildAllOf(match));
363 protected RuleType addObligation(RuleType rule, JSONObject jsonPolicy) {
365 // Convert the YAML Policy to JSON Object
367 if (LOGGER.isDebugEnabled()) {
368 LOGGER.debug("JSON Optimization Policy {}{}", System.lineSeparator(), jsonPolicy);
371 // Create an AttributeValue for it
373 AttributeValueType value = new AttributeValueType();
374 value.setDataType(ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_DATATYPE.stringValue());
375 value.getContent().add(jsonPolicy.toString());
377 // Create our AttributeAssignmentExpression where we will
378 // store the contents of the policy in JSON format.
380 AttributeAssignmentExpressionType expressionType = new AttributeAssignmentExpressionType();
381 expressionType.setAttributeId(ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_CONTENTS.stringValue());
382 ObjectFactory factory = new ObjectFactory();
383 expressionType.setExpression(factory.createAttributeValue(value));
385 // Create an ObligationExpression for it
387 ObligationExpressionType obligation = new ObligationExpressionType();
388 obligation.setFulfillOn(EffectType.PERMIT);
389 obligation.setObligationId(ToscaDictionary.ID_OBLIGATION_REST_BODY.stringValue());
390 obligation.getAttributeAssignmentExpression().add(expressionType);
392 // Now we can add it into the rule
394 ObligationExpressionsType obligations = new ObligationExpressionsType();
395 obligations.getObligationExpression().add(obligation);
396 rule.setObligationExpressions(obligations);