1679837917f9f173ce3a917f7ec4af3302b7d55f
[policy/xacml-pdp.git] / applications / common / src / main / java / org / onap / policy / pdp / xacml / application / common / std / StdCombinedPolicyResultsTranslator.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  *
19  * SPDX-License-Identifier: Apache-2.0
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.policy.pdp.xacml.application.common.std;
24
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.Obligation;
29 import com.att.research.xacml.api.Request;
30 import com.att.research.xacml.api.Response;
31 import com.att.research.xacml.api.Result;
32 import com.att.research.xacml.api.XACML3;
33 import com.att.research.xacml.std.annotations.RequestParser;
34 import com.att.research.xacml.util.XACMLPolicyWriter;
35 import com.google.gson.Gson;
36
37 import java.io.ByteArrayOutputStream;
38 import java.io.IOException;
39 import java.util.ArrayList;
40 import java.util.Collection;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.Map.Entry;
44
45 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType;
46 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpressionType;
47 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType;
48 import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType;
49 import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType;
50 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObjectFactory;
51 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionType;
52 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionsType;
53 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType;
54 import oasis.names.tc.xacml._3_0.core.schema.wd_17.RuleType;
55 import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType;
56
57 import org.json.JSONObject;
58 import org.onap.policy.models.decisions.concepts.DecisionRequest;
59 import org.onap.policy.models.decisions.concepts.DecisionResponse;
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;
66
67 public class StdCombinedPolicyResultsTranslator implements ToscaPolicyTranslator {
68
69     private static final Logger LOGGER = LoggerFactory.getLogger(StdCombinedPolicyResultsTranslator.class);
70
71     public StdCombinedPolicyResultsTranslator() {
72         super();
73     }
74
75     @SuppressWarnings("unchecked")
76     @Override
77     public List<PolicyType> scanAndConvertPolicies(Map<String, Object> toscaObject)
78             throws ToscaPolicyConversionException {
79         //
80         // Our return object
81         //
82         List<PolicyType> scannedPolicies = new ArrayList<>();
83         //
84         // Iterate each of the Policies
85         //
86         List<Object> policies = (List<Object>) toscaObject.get("policies");
87         for (Object policyObject : policies) {
88             //
89             // Get the contents
90             //
91             LOGGER.debug("Found policy {}", policyObject.getClass());
92             Map<String, Object> policyContents = (Map<String, Object>) policyObject;
93             for (Entry<String, Object> entrySet : policyContents.entrySet()) {
94                 LOGGER.debug("Entry set {}", entrySet);
95                 //
96                 // Convert this policy
97                 //
98                 PolicyType policy = this.convertPolicy(entrySet);
99                 try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
100                     XACMLPolicyWriter.writePolicyFile(os, policy);
101                     LOGGER.debug("{}", os);
102                 } catch (IOException e) {
103                     LOGGER.error("Failed to convert {}", e);
104                 }
105                 //
106                 // Convert and add in the new policy
107                 //
108                 scannedPolicies.add(policy);
109             }
110         }
111
112         return scannedPolicies;
113     }
114
115     @Override
116     public Request convertRequest(DecisionRequest request) {
117         LOGGER.debug("Converting Request {}", request);
118         try {
119             return RequestParser.parseRequest(StdCombinedPolicyRequest.createInstance(request));
120         } catch (IllegalArgumentException | IllegalAccessException | DataTypeException e) {
121             LOGGER.error("Failed to convert DecisionRequest: {}", e);
122         }
123         //
124         // TODO throw exception
125         //
126         return null;
127     }
128
129     @Override
130     public DecisionResponse convertResponse(Response xacmlResponse) {
131         LOGGER.debug("Converting Response {}", xacmlResponse);
132         DecisionResponse decisionResponse = new DecisionResponse();
133         //
134         // Iterate through all the results
135         //
136         for (Result xacmlResult : xacmlResponse.getResults()) {
137             //
138             // Check the result
139             //
140             if (xacmlResult.getDecision() == Decision.PERMIT) {
141                 //
142                 // Setup policies
143                 //
144                 decisionResponse.setPolicies(new ArrayList<>());
145                 //
146                 // Go through obligations
147                 //
148                 scanObligations(xacmlResult.getObligations(), decisionResponse);
149             }
150             if (xacmlResult.getDecision() == Decision.NOTAPPLICABLE) {
151                 //
152                 // There is no policy
153                 //
154                 decisionResponse.setPolicies(new ArrayList<>());
155             }
156             if (xacmlResult.getDecision() == Decision.DENY
157                     || xacmlResult.getDecision() == Decision.INDETERMINATE) {
158                 //
159                 // TODO we have to return an ErrorResponse object instead
160                 //
161                 decisionResponse.setStatus("A better error message");
162             }
163         }
164
165         return decisionResponse;
166     }
167
168     protected void scanObligations(Collection<Obligation> obligations, DecisionResponse decisionResponse) {
169         for (Obligation obligation : obligations) {
170             LOGGER.debug("Obligation: {}", obligation);
171             for (AttributeAssignment assignment : obligation.getAttributeAssignments()) {
172                 LOGGER.debug("Attribute Assignment: {}", assignment);
173                 //
174                 // We care about the content attribute
175                 //
176                 if (ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_CONTENTS
177                         .equals(assignment.getAttributeId())) {
178                     //
179                     // The contents are in Json form
180                     //
181                     Object stringContents = assignment.getAttributeValue().getValue();
182                     if (LOGGER.isDebugEnabled()) {
183                         LOGGER.debug("DCAE contents: {}{}", System.lineSeparator(), stringContents);
184                     }
185                     //
186                     // Let's parse it into a map using Gson
187                     //
188                     Gson gson = new Gson();
189                     @SuppressWarnings("unchecked")
190                     Map<String, Object> result = gson.fromJson(stringContents.toString() ,Map.class);
191                     decisionResponse.getPolicies().add(result);
192                 }
193             }
194         }
195     }
196
197     @SuppressWarnings("unchecked")
198     protected PolicyType convertPolicy(Entry<String, Object> entrySet) throws ToscaPolicyConversionException {
199         //
200         // Policy name should be at the root
201         //
202         String policyName = entrySet.getKey();
203         Map<String, Object> policyDefinition = (Map<String, Object>) entrySet.getValue();
204         //
205         // Set it as the policy ID
206         //
207         PolicyType newPolicyType = new PolicyType();
208         newPolicyType.setPolicyId(policyName);
209         //
210         // Optional description
211         //
212         if (policyDefinition.containsKey("description")) {
213             newPolicyType.setDescription(policyDefinition.get("description").toString());
214         }
215         //
216         // There should be a metadata section
217         //
218         if (! policyDefinition.containsKey("metadata")) {
219             throw new ToscaPolicyConversionException(policyName + " missing metadata section");
220         }
221         this.fillMetadataSection(newPolicyType,
222                 (Map<String, Object>) policyDefinition.get("metadata"));
223         //
224         // Set the combining rule
225         //
226         newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_FIRST_APPLICABLE.stringValue());
227         //
228         // Generate the TargetType
229         //
230         if (! policyDefinition.containsKey("type")) {
231             throw new ToscaPolicyConversionException(policyName + " missing type value");
232         }
233         if (! policyDefinition.containsKey("version")) {
234             throw new ToscaPolicyConversionException(policyName + " missing version value");
235         }
236         TargetType target = this.generateTargetType(policyName,
237                 policyDefinition.get("type").toString(),
238                 policyDefinition.get("version").toString());
239         newPolicyType.setTarget(target);
240         //
241         // Now create the Permit Rule
242         // No target since the policy has a target
243         // With obligations.
244         //
245         RuleType rule = new RuleType();
246         rule.setDescription("Default is to PERMIT if the policy matches.");
247         rule.setRuleId(policyName + ":rule");
248         rule.setEffect(EffectType.PERMIT);
249         rule.setTarget(new TargetType());
250         //
251         // Now represent the policy as Json
252         //
253         JSONObject jsonObligation = new JSONObject();
254         jsonObligation.put(policyName, policyDefinition);
255         addObligation(rule, jsonObligation);
256         //
257         // Add the rule to the policy
258         //
259         newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(rule);
260         //
261         // Return our new policy
262         //
263         return newPolicyType;
264     }
265
266     /**
267      * From the TOSCA metadata section, pull in values that are needed into the XACML policy.
268      *
269      * @param policy Policy Object to store the metadata
270      * @param metadata The Metadata TOSCA Map
271      * @return Same Policy Object
272      * @throws ToscaPolicyConversionException If there is something missing from the metadata
273      */
274     protected PolicyType fillMetadataSection(PolicyType policy,
275             Map<String, Object> metadata) throws ToscaPolicyConversionException {
276         if (! metadata.containsKey("policy-id")) {
277             throw new ToscaPolicyConversionException(policy.getPolicyId() + " missing metadata policy-id");
278         } else {
279             //
280             // Do nothing here - the XACML PolicyId is used from TOSCA Policy Name field
281             //
282         }
283         if (! metadata.containsKey("policy-version")) {
284             throw new ToscaPolicyConversionException(policy.getPolicyId() + " missing metadata policy-version");
285         } else {
286             //
287             // Add in the Policy Version
288             //
289             policy.setVersion(metadata.get("policy-version").toString());
290         }
291         return policy;
292     }
293
294     protected TargetType generateTargetType(String policyId, String policyType, String policyTypeVersion) {
295         //
296         // Create all the match's that are possible
297         //
298         // This is for the Policy Id
299         //
300         MatchType matchPolicyId = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
301                 XACML3.ID_FUNCTION_STRING_EQUAL,
302                 policyId,
303                 XACML3.ID_DATATYPE_STRING,
304                 ToscaDictionary.ID_RESOURCE_POLICY_ID,
305                 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
306         //
307         // This is for the Policy Type
308         //
309         MatchType matchPolicyType = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
310                 XACML3.ID_FUNCTION_STRING_EQUAL,
311                 policyType,
312                 XACML3.ID_DATATYPE_STRING,
313                 ToscaDictionary.ID_RESOURCE_POLICY_TYPE,
314                 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
315         //
316         // This is for the Policy Type version
317         //
318         MatchType matchPolicyTypeVersion = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
319                 XACML3.ID_FUNCTION_STRING_EQUAL,
320                 policyTypeVersion,
321                 XACML3.ID_DATATYPE_STRING,
322                 ToscaDictionary.ID_RESOURCE_POLICY_TYPE_VERSION,
323                 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
324         //
325         // This is our outer AnyOf - which is an OR
326         //
327         AnyOfType anyOf = new AnyOfType();
328         //
329         // Create AllOf (AND) of just Policy Id
330         //
331         anyOf.getAllOf().add(ToscaPolicyTranslatorUtils.buildAllOf(matchPolicyId));
332         //
333         // Create AllOf (AND) of just Policy Type
334         //
335         anyOf.getAllOf().add(ToscaPolicyTranslatorUtils.buildAllOf(matchPolicyType));
336         //
337         // Create AllOf (AND) of Policy Type and Policy Type Version
338         //
339         anyOf.getAllOf().add(ToscaPolicyTranslatorUtils.buildAllOf(matchPolicyType, matchPolicyTypeVersion));
340         //
341         // Now we can create the TargetType, add the top-level anyOf (OR),
342         // and return the value.
343         //
344         TargetType target = new TargetType();
345         target.getAnyOf().add(anyOf);
346         return target;
347     }
348
349     protected RuleType addObligation(RuleType rule, JSONObject jsonPolicy) {
350         //
351         // Convert the YAML Policy to JSON Object
352         //
353         if (LOGGER.isDebugEnabled()) {
354             LOGGER.debug("JSON DCAE Policy {}{}", System.lineSeparator(), jsonPolicy);
355         }
356         //
357         // Create an AttributeValue for it
358         //
359         AttributeValueType value = new AttributeValueType();
360         value.setDataType(ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_DATATYPE.stringValue());
361         value.getContent().add(jsonPolicy.toString());
362         //
363         // Create our AttributeAssignmentExpression where we will
364         // store the contents of the policy in JSON format.
365         //
366         AttributeAssignmentExpressionType expressionType = new AttributeAssignmentExpressionType();
367         expressionType.setAttributeId(ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_CONTENTS.stringValue());
368         ObjectFactory factory = new ObjectFactory();
369         expressionType.setExpression(factory.createAttributeValue(value));
370         //
371         // Create an ObligationExpression for it
372         //
373         ObligationExpressionType obligation = new ObligationExpressionType();
374         obligation.setFulfillOn(EffectType.PERMIT);
375         obligation.setObligationId(ToscaDictionary.ID_OBLIGATION_REST_BODY.stringValue());
376         obligation.getAttributeAssignmentExpression().add(expressionType);
377         //
378         // Now we can add it into the rule
379         //
380         ObligationExpressionsType obligations = new ObligationExpressionsType();
381         obligations.getObligationExpression().add(obligation);
382         rule.setObligationExpressions(obligations);
383         return rule;
384     }
385
386 }