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.xacml.pdp.application.guard;
25 import com.att.research.xacml.api.DataTypeException;
26 import com.att.research.xacml.api.Decision;
27 import com.att.research.xacml.api.Identifier;
28 import com.att.research.xacml.api.Request;
29 import com.att.research.xacml.api.Response;
30 import com.att.research.xacml.api.Result;
31 import com.att.research.xacml.api.XACML3;
32 import com.att.research.xacml.std.annotations.RequestParser;
34 import java.util.Collection;
37 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AdviceExpressionType;
38 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AdviceExpressionsType;
39 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AllOfType;
40 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType;
41 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ApplyType;
42 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpressionType;
43 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType;
44 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType;
45 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ConditionType;
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.PolicyType;
50 import oasis.names.tc.xacml._3_0.core.schema.wd_17.RuleType;
51 import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType;
53 import org.onap.policy.models.decisions.concepts.DecisionRequest;
54 import org.onap.policy.models.decisions.concepts.DecisionResponse;
55 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
56 import org.onap.policy.pdp.xacml.application.common.ToscaDictionary;
57 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException;
58 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslator;
59 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslatorUtils;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
63 public class LegacyGuardTranslator implements ToscaPolicyTranslator {
65 private static final Logger LOGGER = LoggerFactory.getLogger(LegacyGuardTranslator.class);
67 private static final String FIELD_GUARD_ACTIVE_START = "guardActiveStart";
68 private static final String FIELD_GUARD_ACTIVE_END = "guardActiveEnd";
70 public LegacyGuardTranslator() {
75 public PolicyType convertPolicy(ToscaPolicy toscaPolicy) throws ToscaPolicyConversionException {
77 // Policy name should be at the root
79 String policyName = toscaPolicy.getMetadata().get("policy-id");
81 // Set it as the policy ID
83 PolicyType newPolicyType = new PolicyType();
84 newPolicyType.setPolicyId(policyName);
86 // Optional description
88 newPolicyType.setDescription(toscaPolicy.getDescription());
90 // There should be a metadata section
92 this.fillMetadataSection(newPolicyType, toscaPolicy.getMetadata());
94 // Set the combining rule
96 newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_DENY_UNLESS_PERMIT.stringValue());
98 // Generate the TargetType
100 newPolicyType.setTarget(this.generateTargetType(toscaPolicy.getProperties()));
102 // Now create the Permit Rule
104 RuleType rule = generatePermitRule(policyName, toscaPolicy.getType(), toscaPolicy.getProperties());
106 // Check if we were able to create the rule
109 LOGGER.warn("Failed to create rule");
113 // Add the rule to the policy
115 newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(rule);
117 // Return our new policy
119 return newPolicyType;
123 public Request convertRequest(DecisionRequest request) {
124 LOGGER.debug("Converting Request {}", request);
126 return RequestParser.parseRequest(LegacyGuardPolicyRequest.createInstance(request));
127 } catch (IllegalArgumentException | IllegalAccessException | DataTypeException e) {
128 LOGGER.error("Failed to convert DecisionRequest: {}", e);
131 // TODO throw exception
137 public DecisionResponse convertResponse(Response xacmlResponse) {
138 LOGGER.debug("Converting Response {}", xacmlResponse);
139 DecisionResponse decisionResponse = new DecisionResponse();
141 // Iterate through all the results
143 for (Result xacmlResult : xacmlResponse.getResults()) {
147 if (xacmlResult.getDecision() == Decision.PERMIT) {
149 // Just simply return a Permit response
151 decisionResponse.setStatus(Decision.PERMIT.toString());
153 if (xacmlResult.getDecision() == Decision.DENY) {
155 // Just simply return a Deny response
157 decisionResponse.setStatus(Decision.DENY.toString());
159 if (xacmlResult.getDecision() == Decision.NOTAPPLICABLE) {
161 // There is no guard policy, so we return a permit
163 decisionResponse.setStatus(Decision.PERMIT.toString());
167 return decisionResponse;
172 * From the TOSCA metadata section, pull in values that are needed into the XACML policy.
174 * @param policy Policy Object to store the metadata
175 * @param map The Metadata TOSCA Map
176 * @return Same Policy Object
177 * @throws ToscaPolicyConversionException If there is something missing from the metadata
179 protected PolicyType fillMetadataSection(PolicyType policy,
180 Map<String, String> map) throws ToscaPolicyConversionException {
181 if (! map.containsKey("policy-id")) {
182 throw new ToscaPolicyConversionException(policy.getPolicyId() + " missing metadata policy-id");
185 // Do nothing here - the XACML PolicyId is used from TOSCA Policy Name field
188 if (! map.containsKey("policy-version")) {
189 throw new ToscaPolicyConversionException(policy.getPolicyId() + " missing metadata policy-version");
192 // Add in the Policy Version
194 policy.setVersion(map.get("policy-version").toString());
199 protected TargetType generateTargetType(Map<String, Object> properties) {
201 // Go through potential properties
203 AllOfType allOf = new AllOfType();
204 if (properties.containsKey("actor")) {
205 addMatch(allOf, properties.get("actor"), ToscaDictionary.ID_RESOURCE_GUARD_ACTOR);
207 if (properties.containsKey("recipe")) {
208 addMatch(allOf, properties.get("recipe"), ToscaDictionary.ID_RESOURCE_GUARD_RECIPE);
210 if (properties.containsKey("targets")) {
211 addMatch(allOf, properties.get("targets"), ToscaDictionary.ID_RESOURCE_GUARD_TARGETID);
213 if (properties.containsKey("clname")) {
214 addMatch(allOf, properties.get("clname"), ToscaDictionary.ID_RESOURCE_GUARD_CLNAME);
216 if (properties.containsKey("targets")) {
217 addMatch(allOf, properties.get("targets"), ToscaDictionary.ID_RESOURCE_GUARD_TARGETID);
222 TargetType target = new TargetType();
223 AnyOfType anyOf = new AnyOfType();
224 anyOf.getAllOf().add(allOf);
225 target.getAnyOf().add(anyOf);
229 private static AllOfType addMatch(AllOfType allOf, Object value, Identifier attributeId) {
230 if (value instanceof String) {
231 if (".*".equals(value.toString())) {
233 // There's no point to even have a match
240 MatchType match = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
241 XACML3.ID_FUNCTION_STRING_EQUAL,
243 XACML3.ID_DATATYPE_STRING,
245 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
247 allOf.getMatch().add(match);
251 if (value instanceof Collection) {
253 // TODO support a collection of that attribute
259 private static RuleType generatePermitRule(String policyName, String policyType, Map<String, Object> properties)
260 throws ToscaPolicyConversionException {
262 // Now determine which policy type we are generating
264 if ("onap.policies.controlloop.guard.FrequencyLimiter".equals(policyType)) {
265 return generateFrequencyPermit(policyName, properties);
266 } else if ("onap.policies.controlloop.guard.MinMax".equals(policyType)) {
267 return generateMinMaxPermit(policyName, properties);
269 LOGGER.error("Missing policy type in the policy");
273 private static RuleType generateFrequencyPermit(String policyName, Map<String, Object> properties)
274 throws ToscaPolicyConversionException {
276 // See if its possible to generate a count
278 Integer limit = parseInteger(properties.get("limit").toString());
280 LOGGER.debug("Must have a limit value for frequency guard policy to be created");
284 // Get the properties that are common among guards
286 String timeWindow = null;
287 if (properties.containsKey("timeWindow")) {
288 Integer intTimeWindow = parseInteger(properties.get("timeWindow").toString());
289 if (intTimeWindow == null) {
290 throw new ToscaPolicyConversionException("timeWindow is not an integer");
292 timeWindow = intTimeWindow.toString();
294 String timeUnits = null;
295 if (properties.containsKey("timeUnits")) {
296 timeUnits = properties.get("timeUnits").toString();
298 String guardActiveStart = null;
299 if (properties.containsKey(FIELD_GUARD_ACTIVE_START)) {
300 guardActiveStart = properties.get(FIELD_GUARD_ACTIVE_START).toString();
302 String guardActiveEnd = null;
303 if (properties.containsKey(FIELD_GUARD_ACTIVE_END)) {
304 guardActiveEnd = properties.get(FIELD_GUARD_ACTIVE_END).toString();
307 // Generate the time in range
309 final ApplyType timeRange = generateTimeInRange(guardActiveStart, guardActiveEnd);
313 final ApplyType countCheck = generateCountCheck(limit, timeWindow, timeUnits);
315 // Now combine into an And
317 ApplyType applyAnd = new ApplyType();
318 applyAnd.setDescription("return true if time range and count checks are true.");
319 applyAnd.setFunctionId(XACML3.ID_FUNCTION_AND.stringValue());
320 applyAnd.getExpression().add(new ObjectFactory().createApply(timeRange));
321 applyAnd.getExpression().add(new ObjectFactory().createApply(countCheck));
324 // Create our condition
326 final ConditionType condition = new ConditionType();
327 condition.setExpression(new ObjectFactory().createApply(applyAnd));
330 // Now we can create our rule
332 RuleType permit = new RuleType();
333 permit.setDescription("Default is to PERMIT if the policy matches.");
334 permit.setRuleId(policyName + ":rule");
335 permit.setEffect(EffectType.PERMIT);
336 permit.setTarget(new TargetType());
340 permit.setCondition(condition);
342 // TODO Add the advice - Is the request id needed to be returned?
344 // permit.setAdviceExpressions(adviceExpressions);
351 private static RuleType generateMinMaxPermit(String policyName, Map<String, Object> properties) {
353 // Get the properties that are common among guards
355 String guardActiveStart = null;
356 if (properties.containsKey(FIELD_GUARD_ACTIVE_START)) {
357 guardActiveStart = properties.get(FIELD_GUARD_ACTIVE_START).toString();
359 String guardActiveEnd = null;
360 if (properties.containsKey(FIELD_GUARD_ACTIVE_END)) {
361 guardActiveEnd = properties.get(FIELD_GUARD_ACTIVE_END).toString();
364 // Generate the time in range
366 final ApplyType timeRange = generateTimeInRange(guardActiveStart, guardActiveEnd);
368 // See if its possible to generate a count
371 if (properties.containsKey("min")) {
372 min = parseInteger(properties.get("min").toString());
375 if (properties.containsKey("max")) {
376 max = parseInteger(properties.get("max").toString());
378 final ApplyType minApply = generateMinCheck(min);
379 final ApplyType maxApply = generateMaxCheck(max);
381 // Make sure we have at least something to check here,
382 // otherwise there really is no point to this policy.
384 if (timeRange == null && minApply == null && maxApply == null) {
390 RuleType permit = new RuleType();
391 permit.setDescription("Default is to PERMIT if the policy matches.");
392 permit.setRuleId(policyName + ":rule");
393 permit.setEffect(EffectType.PERMIT);
394 permit.setTarget(new TargetType());
396 // Create our condition
398 final ConditionType condition = new ConditionType();
400 // Check if we have all the fields (this can be a little
401 // ugly) but the ultimate goal is to simplify the policy
402 // condition to only check for necessary attributes.
404 ObjectFactory factory = new ObjectFactory();
405 if (timeRange != null && minApply != null && maxApply != null) {
409 ApplyType applyAnd = new ApplyType();
410 applyAnd.setDescription("return true if all the apply's are true.");
411 applyAnd.setFunctionId(XACML3.ID_FUNCTION_AND.stringValue());
412 applyAnd.getExpression().add(factory.createApply(timeRange));
413 applyAnd.getExpression().add(factory.createApply(minApply));
414 applyAnd.getExpression().add(factory.createApply(maxApply));
416 // Add into the condition
418 condition.setExpression(factory.createApply(applyAnd));
421 // At least one of these applies is null. We need at least
422 // two to require the And apply. Otherwise there is no need
423 // for an outer And apply as the single condition can work
426 if (timeRange != null && minApply == null && maxApply == null) {
428 // Only the time range check is necessary
430 condition.setExpression(factory.createApply(timeRange));
431 } else if (timeRange == null && minApply != null && maxApply == null) {
433 // Only the min check is necessary
435 condition.setExpression(factory.createApply(minApply));
436 } else if (timeRange == null && minApply == null) {
438 // Only the max check is necessary
440 condition.setExpression(factory.createApply(maxApply));
443 // Ok we will need an outer And and have at least the
444 // time range and either min or max check
446 ApplyType applyAnd = new ApplyType();
447 applyAnd.setDescription("return true if all the apply's are true.");
448 applyAnd.setFunctionId(XACML3.ID_FUNCTION_AND.stringValue());
449 if (timeRange != null) {
450 applyAnd.getExpression().add(factory.createApply(timeRange));
452 if (minApply != null) {
453 applyAnd.getExpression().add(factory.createApply(minApply));
455 if (maxApply != null) {
456 applyAnd.getExpression().add(factory.createApply(maxApply));
459 // Add into the condition
461 condition.setExpression(factory.createApply(applyAnd));
467 permit.setCondition(condition);
469 // TODO Add the advice - Is the request id needed to be returned?
471 // permit.setAdviceExpressions(adviceExpressions);
478 private static ApplyType generateTimeInRange(String start, String end) {
479 if (start == null || end == null) {
480 LOGGER.warn("Missing time range start {} end {}", start, end);
483 if (start.isEmpty() || end.isEmpty()) {
484 LOGGER.warn("Empty time range start {} end {}", start, end);
488 AttributeDesignatorType designator = new AttributeDesignatorType();
489 designator.setAttributeId(XACML3.ID_ENVIRONMENT_CURRENT_TIME.stringValue());
490 designator.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_ENVIRONMENT.stringValue());
491 designator.setDataType(XACML3.ID_DATATYPE_TIME.stringValue());
493 AttributeValueType valueStart = new AttributeValueType();
494 valueStart.setDataType(XACML3.ID_DATATYPE_TIME.stringValue());
495 valueStart.getContent().add(start);
497 AttributeValueType valueEnd = new AttributeValueType();
498 valueEnd.setDataType(XACML3.ID_DATATYPE_TIME.stringValue());
499 valueEnd.getContent().add(end);
501 ObjectFactory factory = new ObjectFactory();
503 ApplyType applyOneAndOnly = new ApplyType();
504 applyOneAndOnly.setDescription("Unbag the current time");
505 applyOneAndOnly.setFunctionId(XACML3.ID_FUNCTION_TIME_ONE_AND_ONLY.stringValue());
506 applyOneAndOnly.getExpression().add(factory.createAttributeDesignator(designator));
508 ApplyType applyTimeInRange = new ApplyType();
509 applyTimeInRange.setDescription("return true if current time is in range.");
510 applyTimeInRange.setFunctionId(XACML3.ID_FUNCTION_TIME_IN_RANGE.stringValue());
511 applyTimeInRange.getExpression().add(factory.createApply(applyOneAndOnly));
512 applyTimeInRange.getExpression().add(factory.createAttributeValue(valueStart));
513 applyTimeInRange.getExpression().add(factory.createAttributeValue(valueEnd));
515 return applyTimeInRange;
518 private static ApplyType generateCountCheck(Integer limit, String timeWindow, String timeUnits) {
519 AttributeDesignatorType designator = new AttributeDesignatorType();
520 designator.setAttributeId(ToscaDictionary.ID_RESOURCE_GUARD_OPERATIONCOUNT.stringValue());
521 designator.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE.stringValue());
522 designator.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
524 // TODO Add this back in when the operational database PIP is configured.
525 // The issuer indicates that the PIP will be providing this attribute during
526 // the decision making.
528 // Right now I am faking the count value by re-using the request-id field
530 String issuer = ToscaDictionary.GUARD_ISSUER + ":tw:" + timeWindow + ":" + timeUnits;
531 designator.setIssuer(issuer);
533 AttributeValueType valueLimit = new AttributeValueType();
534 valueLimit.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
536 // Yes really use toString(), the marshaller will
537 // throw an exception if this is an integer object
540 valueLimit.getContent().add(limit.toString());
542 ObjectFactory factory = new ObjectFactory();
544 ApplyType applyOneAndOnly = new ApplyType();
545 applyOneAndOnly.setDescription("Unbag the limit");
546 applyOneAndOnly.setFunctionId(XACML3.ID_FUNCTION_INTEGER_ONE_AND_ONLY.stringValue());
547 applyOneAndOnly.getExpression().add(factory.createAttributeDesignator(designator));
549 ApplyType applyLessThan = new ApplyType();
550 applyLessThan.setDescription("return true if current count is less than.");
551 applyLessThan.setFunctionId(XACML3.ID_FUNCTION_INTEGER_LESS_THAN.stringValue());
552 applyLessThan.getExpression().add(factory.createApply(applyOneAndOnly));
553 applyLessThan.getExpression().add(factory.createAttributeValue(valueLimit));
555 return applyLessThan;
558 private static ApplyType generateMinCheck(Integer min) {
562 AttributeDesignatorType designator = new AttributeDesignatorType();
563 designator.setAttributeId(ToscaDictionary.ID_RESOURCE_GUARD_VFCOUNT.stringValue());
564 designator.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE.stringValue());
565 designator.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
569 AttributeValueType valueLimit = new AttributeValueType();
570 valueLimit.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
572 // Yes really use toString(), the marshaller will
573 // throw an exception if this is an integer object
576 valueLimit.getContent().add(min.toString());
577 ObjectFactory factory = new ObjectFactory();
579 ApplyType applyOneAndOnly = new ApplyType();
580 applyOneAndOnly.setDescription("Unbag the min");
581 applyOneAndOnly.setFunctionId(XACML3.ID_FUNCTION_INTEGER_ONE_AND_ONLY.stringValue());
582 applyOneAndOnly.getExpression().add(factory.createAttributeDesignator(designator));
584 ApplyType applyGreaterThanEqual = new ApplyType();
585 applyGreaterThanEqual.setDescription("return true if current count is greater than or equal.");
586 applyGreaterThanEqual.setFunctionId(XACML3.ID_FUNCTION_INTEGER_GREATER_THAN_OR_EQUAL.stringValue());
587 applyGreaterThanEqual.getExpression().add(factory.createApply(applyOneAndOnly));
588 applyGreaterThanEqual.getExpression().add(factory.createAttributeValue(valueLimit));
590 return applyGreaterThanEqual;
593 private static ApplyType generateMaxCheck(Integer max) {
597 AttributeDesignatorType designator = new AttributeDesignatorType();
598 designator.setAttributeId(ToscaDictionary.ID_RESOURCE_GUARD_VFCOUNT.stringValue());
599 designator.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE.stringValue());
600 designator.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
604 AttributeValueType valueLimit = new AttributeValueType();
605 valueLimit.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
607 // Yes really use toString(), the marshaller will
608 // throw an exception if this is an integer object
611 valueLimit.getContent().add(max.toString());
612 ObjectFactory factory = new ObjectFactory();
614 ApplyType applyOneAndOnly = new ApplyType();
615 applyOneAndOnly.setDescription("Unbag the min");
616 applyOneAndOnly.setFunctionId(XACML3.ID_FUNCTION_INTEGER_ONE_AND_ONLY.stringValue());
617 applyOneAndOnly.getExpression().add(factory.createAttributeDesignator(designator));
619 ApplyType applyLessThanEqual = new ApplyType();
620 applyLessThanEqual.setDescription("return true if current count is less than or equal.");
621 applyLessThanEqual.setFunctionId(XACML3.ID_FUNCTION_INTEGER_LESS_THAN_OR_EQUAL.stringValue());
622 applyLessThanEqual.getExpression().add(factory.createApply(applyOneAndOnly));
623 applyLessThanEqual.getExpression().add(factory.createAttributeValue(valueLimit));
625 return applyLessThanEqual;
628 private static Integer parseInteger(String strInteger) {
629 Integer theInt = null;
631 theInt = Integer.parseInt(strInteger);
632 } catch (NumberFormatException e) {
633 LOGGER.warn("Expecting an integer", e);
635 Double dblLimit = Double.parseDouble(strInteger);
636 theInt = dblLimit.intValue();
637 } catch (NumberFormatException e1) {
638 LOGGER.error("Failed to parse expected integer as a double", e);
645 private static AdviceExpressionsType generateRequestIdAdvice() {
646 AdviceExpressionType adviceExpression = new AdviceExpressionType();
647 adviceExpression.setAppliesTo(EffectType.PERMIT);
648 adviceExpression.setAdviceId(ToscaDictionary.ID_ADVICE_GUARD.stringValue());
650 AttributeDesignatorType designator = new AttributeDesignatorType();
651 designator.setAttributeId(ToscaDictionary.ID_SUBJECT_GUARD_REQUESTID.stringValue());
652 designator.setCategory(XACML3.ID_SUBJECT_CATEGORY_ACCESS_SUBJECT.stringValue());
653 designator.setDataType(XACML3.ID_DATATYPE_STRING.stringValue());
655 AttributeAssignmentExpressionType assignment = new AttributeAssignmentExpressionType();
656 assignment.setAttributeId(ToscaDictionary.ID_ADVICE_GUARD_REQUESTID.stringValue());
657 assignment.setCategory(XACML3.ID_SUBJECT_CATEGORY_ACCESS_SUBJECT.stringValue());
658 assignment.setExpression(new ObjectFactory().createAttributeDesignator(designator));
660 adviceExpression.getAttributeAssignmentExpression().add(assignment);
662 AdviceExpressionsType adviceExpressions = new AdviceExpressionsType();
663 adviceExpressions.getAdviceExpression().add(adviceExpression);
665 return adviceExpressions;