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.Arrays;
38 import java.util.Collection;
39 import java.util.HashMap;
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);
70 private static final String POLICY_ID = "policy-id";
72 public StdMatchableTranslator() {
77 public Request convertRequest(DecisionRequest request) {
78 LOGGER.debug("Converting Request {}", request);
80 return RequestParser.parseRequest(StdMatchablePolicyRequest.createInstance(request));
81 } catch (IllegalArgumentException | IllegalAccessException | DataTypeException e) {
82 LOGGER.error("Failed to convert DecisionRequest: {}", e);
85 // TODO throw exception
91 public DecisionResponse convertResponse(Response xacmlResponse) {
92 LOGGER.debug("Converting Response {}", xacmlResponse);
93 DecisionResponse decisionResponse = new DecisionResponse();
97 decisionResponse.setPolicies(new HashMap<>());
99 // Iterate through all the results
101 for (Result xacmlResult : xacmlResponse.getResults()) {
105 if (xacmlResult.getDecision() == Decision.PERMIT) {
107 // Go through obligations
109 scanObligations(xacmlResult.getObligations(), decisionResponse);
111 if (xacmlResult.getDecision() == Decision.DENY
112 || xacmlResult.getDecision() == Decision.INDETERMINATE) {
114 // TODO we have to return an ErrorResponse object instead
116 decisionResponse.setStatus("A better error message");
120 return decisionResponse;
123 protected void scanObligations(Collection<Obligation> obligations, DecisionResponse decisionResponse) {
124 for (Obligation obligation : obligations) {
125 LOGGER.debug("Obligation: {}", obligation);
126 for (AttributeAssignment assignment : obligation.getAttributeAssignments()) {
127 LOGGER.debug("Attribute Assignment: {}", assignment);
129 // We care about the content attribute
131 if (ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_CONTENTS
132 .equals(assignment.getAttributeId())) {
134 // The contents are in Json form
136 Object stringContents = assignment.getAttributeValue().getValue();
137 if (LOGGER.isDebugEnabled()) {
138 LOGGER.debug("Policy contents: {}{}", System.lineSeparator(), stringContents);
141 // Let's parse it into a map using Gson
143 Gson gson = new Gson();
144 @SuppressWarnings("unchecked")
145 Map<String, Object> result = gson.fromJson(stringContents.toString() ,Map.class);
147 // Find the metadata section
149 @SuppressWarnings("unchecked")
150 Map<String, Object> metadata = (Map<String, Object>) result.get("metadata");
151 if (metadata != null) {
152 decisionResponse.getPolicies().put(metadata.get(POLICY_ID).toString(), result);
154 LOGGER.error("Missing metadata section in policy contained in obligation.");
163 public PolicyType convertPolicy(ToscaPolicy toscaPolicy) throws ToscaPolicyConversionException {
165 // Policy name should be at the root
167 String policyName = toscaPolicy.getMetadata().get(POLICY_ID);
169 // Set it as the policy ID
171 PolicyType newPolicyType = new PolicyType();
172 newPolicyType.setPolicyId(policyName);
174 // Optional description
176 newPolicyType.setDescription(toscaPolicy.getDescription());
178 // There should be a metadata section
180 this.fillMetadataSection(newPolicyType, toscaPolicy.getMetadata());
182 // Set the combining rule
184 newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_FIRST_APPLICABLE.stringValue());
186 // Generate the TargetType
188 newPolicyType.setTarget(generateTargetType(toscaPolicy.getProperties()));
190 // Now create the Permit Rule
191 // No target since the policy has a target
194 RuleType rule = new RuleType();
195 rule.setDescription("Default is to PERMIT if the policy matches.");
196 rule.setRuleId(policyName + ":rule");
197 rule.setEffect(EffectType.PERMIT);
198 rule.setTarget(new TargetType());
200 // Now represent the policy as Json
202 StandardCoder coder = new StandardCoder();
205 jsonPolicy = coder.encode(toscaPolicy);
206 } catch (CoderException e) {
207 throw new ToscaPolicyConversionException("Failed to encode policy to json", e);
209 addObligation(rule, jsonPolicy);
211 // Add the rule to the policy
213 newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(rule);
215 // Return our new policy
217 return newPolicyType;
221 * From the TOSCA metadata section, pull in values that are needed into the XACML policy.
223 * @param policy Policy Object to store the metadata
224 * @param map The Metadata TOSCA Map
225 * @return Same Policy Object
226 * @throws ToscaPolicyConversionException If there is something missing from the metadata
228 protected PolicyType fillMetadataSection(PolicyType policy,
229 Map<String, String> map) throws ToscaPolicyConversionException {
230 if (! map.containsKey(POLICY_ID)) {
231 throw new ToscaPolicyConversionException(policy.getPolicyId() + " missing metadata policy-id");
234 // Do nothing here - the XACML PolicyId is used from TOSCA Policy Name field
237 if (! map.containsKey("policy-version")) {
238 throw new ToscaPolicyConversionException(policy.getPolicyId() + " missing metadata policy-version");
241 // Add in the Policy Version
243 policy.setVersion(map.get("policy-version"));
249 * For generating target type, we are making an assumption that the
250 * policyScope and policyType are the fields that OOF wants to match on.
252 * <P>In the future, we would need to receive the Policy Type specification
253 * from the PAP so we can dynamically see which fields are matchable.
255 * <P>Note: I am making an assumption that the matchable fields are what
256 * the OOF wants to query a policy on.
258 * @param properties Properties section of policy
259 * @return TargetType object
261 @SuppressWarnings("unchecked")
262 protected TargetType generateTargetType(Map<String, Object> properties) {
263 TargetType targetType = new TargetType();
265 // Iterate the properties
267 for (Entry<String, Object> entrySet : properties.entrySet()) {
269 // Find policyScope and policyType
271 if (entrySet.getKey().equals("policyScope")) {
272 LOGGER.debug("Found policyScope: {}", entrySet.getValue());
273 if (entrySet.getValue() instanceof Collection) {
274 targetType.getAnyOf().add(generateMatches((Collection<Object>) entrySet.getValue(),
275 ToscaDictionary.ID_RESOURCE_POLICY_SCOPE_PROPERTY));
276 } else if (entrySet.getValue() instanceof String) {
277 targetType.getAnyOf().add(generateMatches(Arrays.asList(entrySet.getValue()),
278 ToscaDictionary.ID_RESOURCE_POLICY_SCOPE_PROPERTY));
281 if (entrySet.getKey().equals("policyType")) {
282 LOGGER.debug("Found policyType: {}", entrySet.getValue());
283 if (entrySet.getValue() instanceof Collection) {
284 targetType.getAnyOf().add(generateMatches((Collection<Object>) entrySet.getValue(),
285 ToscaDictionary.ID_RESOURCE_POLICY_TYPE_PROPERTY));
286 } else if (entrySet.getValue() instanceof String) {
287 targetType.getAnyOf().add(generateMatches(Arrays.asList(entrySet.getValue()),
288 ToscaDictionary.ID_RESOURCE_POLICY_TYPE_PROPERTY));
296 protected AnyOfType generateMatches(Collection<Object> matchables, Identifier attributeId) {
298 // This is our outer AnyOf - which is an OR
300 AnyOfType anyOf = new AnyOfType();
301 for (Object matchable : matchables) {
303 // Create a match for this
305 MatchType match = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
306 XACML3.ID_FUNCTION_STRING_EQUAL,
307 matchable.toString(),
308 XACML3.ID_DATATYPE_STRING,
310 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
312 // Now create an anyOf (OR)
314 anyOf.getAllOf().add(ToscaPolicyTranslatorUtils.buildAllOf(match));
319 protected RuleType addObligation(RuleType rule, String jsonPolicy) {
321 // Convert the YAML Policy to JSON Object
323 if (LOGGER.isDebugEnabled()) {
324 LOGGER.debug("JSON Optimization Policy {}{}", System.lineSeparator(), jsonPolicy);
327 // Create an AttributeValue for it
329 AttributeValueType value = new AttributeValueType();
330 value.setDataType(ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_DATATYPE.stringValue());
331 value.getContent().add(jsonPolicy);
333 // Create our AttributeAssignmentExpression where we will
334 // store the contents of the policy in JSON format.
336 AttributeAssignmentExpressionType expressionType = new AttributeAssignmentExpressionType();
337 expressionType.setAttributeId(ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_CONTENTS.stringValue());
338 ObjectFactory factory = new ObjectFactory();
339 expressionType.setExpression(factory.createAttributeValue(value));
341 // Create an ObligationExpression for it
343 ObligationExpressionType obligation = new ObligationExpressionType();
344 obligation.setFulfillOn(EffectType.PERMIT);
345 obligation.setObligationId(ToscaDictionary.ID_OBLIGATION_REST_BODY.stringValue());
346 obligation.getAttributeAssignmentExpression().add(expressionType);
348 // Now we can add it into the rule
350 ObligationExpressionsType obligations = new ObligationExpressionsType();
351 obligations.getObligationExpression().add(obligation);
352 rule.setObligationExpressions(obligations);