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.onap.policy.pdp.xacml.application.common.operationshistory.CountRecentOperationsPip;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
64 public class LegacyGuardTranslator implements ToscaPolicyTranslator {
66 private static final Logger LOGGER = LoggerFactory.getLogger(LegacyGuardTranslator.class);
68 private static final String FIELD_GUARD_ACTIVE_START = "guardActiveStart";
69 private static final String FIELD_GUARD_ACTIVE_END = "guardActiveEnd";
71 public LegacyGuardTranslator() {
76 public PolicyType convertPolicy(ToscaPolicy toscaPolicy) throws ToscaPolicyConversionException {
78 // Policy name should be at the root
80 String policyName = toscaPolicy.getMetadata().get("policy-id");
82 // Set it as the policy ID
84 PolicyType newPolicyType = new PolicyType();
85 newPolicyType.setPolicyId(policyName);
87 // Optional description
89 newPolicyType.setDescription(toscaPolicy.getDescription());
91 // There should be a metadata section
93 this.fillMetadataSection(newPolicyType, toscaPolicy.getMetadata());
95 // Set the combining rule
97 newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_DENY_UNLESS_PERMIT.stringValue());
99 // Generate the TargetType
101 newPolicyType.setTarget(this.generateTargetType(toscaPolicy.getProperties()));
103 // Now create the Permit Rule
105 RuleType rule = generatePermitRule(policyName, toscaPolicy.getType(), toscaPolicy.getProperties());
107 // Check if we were able to create the rule
110 LOGGER.warn("Failed to create rule");
114 // Add the rule to the policy
116 newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(rule);
118 // Return our new policy
120 return newPolicyType;
124 public Request convertRequest(DecisionRequest request) {
125 LOGGER.debug("Converting Request {}", request);
127 return RequestParser.parseRequest(LegacyGuardPolicyRequest.createInstance(request));
128 } catch (IllegalArgumentException | IllegalAccessException | DataTypeException e) {
129 LOGGER.error("Failed to convert DecisionRequest: {}", e);
132 // TODO throw exception
138 public DecisionResponse convertResponse(Response xacmlResponse) {
139 LOGGER.debug("Converting Response {}", xacmlResponse);
140 DecisionResponse decisionResponse = new DecisionResponse();
142 // Iterate through all the results
144 for (Result xacmlResult : xacmlResponse.getResults()) {
148 if (xacmlResult.getDecision() == Decision.PERMIT) {
150 // Just simply return a Permit response
152 decisionResponse.setStatus(Decision.PERMIT.toString());
154 if (xacmlResult.getDecision() == Decision.DENY) {
156 // Just simply return a Deny response
158 decisionResponse.setStatus(Decision.DENY.toString());
160 if (xacmlResult.getDecision() == Decision.NOTAPPLICABLE) {
162 // There is no guard policy, so we return a permit
164 decisionResponse.setStatus(Decision.PERMIT.toString());
168 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_PREFIX
531 + CountRecentOperationsPip.ISSUER_NAME
532 + ":tw:" + timeWindow + ":" + timeUnits;
533 designator.setIssuer(issuer);
535 AttributeValueType valueLimit = new AttributeValueType();
536 valueLimit.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
538 // Yes really use toString(), the marshaller will
539 // throw an exception if this is an integer object
542 valueLimit.getContent().add(limit.toString());
544 ObjectFactory factory = new ObjectFactory();
546 ApplyType applyOneAndOnly = new ApplyType();
547 applyOneAndOnly.setDescription("Unbag the limit");
548 applyOneAndOnly.setFunctionId(XACML3.ID_FUNCTION_INTEGER_ONE_AND_ONLY.stringValue());
549 applyOneAndOnly.getExpression().add(factory.createAttributeDesignator(designator));
551 ApplyType applyLessThan = new ApplyType();
552 applyLessThan.setDescription("return true if current count is less than.");
553 applyLessThan.setFunctionId(XACML3.ID_FUNCTION_INTEGER_LESS_THAN.stringValue());
554 applyLessThan.getExpression().add(factory.createApply(applyOneAndOnly));
555 applyLessThan.getExpression().add(factory.createAttributeValue(valueLimit));
557 return applyLessThan;
560 private static ApplyType generateMinCheck(Integer min) {
564 AttributeDesignatorType designator = new AttributeDesignatorType();
565 designator.setAttributeId(ToscaDictionary.ID_RESOURCE_GUARD_VFCOUNT.stringValue());
566 designator.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE.stringValue());
567 designator.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
571 AttributeValueType valueLimit = new AttributeValueType();
572 valueLimit.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
574 // Yes really use toString(), the marshaller will
575 // throw an exception if this is an integer object
578 valueLimit.getContent().add(min.toString());
579 ObjectFactory factory = new ObjectFactory();
581 ApplyType applyOneAndOnly = new ApplyType();
582 applyOneAndOnly.setDescription("Unbag the min");
583 applyOneAndOnly.setFunctionId(XACML3.ID_FUNCTION_INTEGER_ONE_AND_ONLY.stringValue());
584 applyOneAndOnly.getExpression().add(factory.createAttributeDesignator(designator));
586 ApplyType applyGreaterThanEqual = new ApplyType();
587 applyGreaterThanEqual.setDescription("return true if current count is greater than or equal.");
588 applyGreaterThanEqual.setFunctionId(XACML3.ID_FUNCTION_INTEGER_GREATER_THAN_OR_EQUAL.stringValue());
589 applyGreaterThanEqual.getExpression().add(factory.createApply(applyOneAndOnly));
590 applyGreaterThanEqual.getExpression().add(factory.createAttributeValue(valueLimit));
592 return applyGreaterThanEqual;
595 private static ApplyType generateMaxCheck(Integer max) {
599 AttributeDesignatorType designator = new AttributeDesignatorType();
600 designator.setAttributeId(ToscaDictionary.ID_RESOURCE_GUARD_VFCOUNT.stringValue());
601 designator.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE.stringValue());
602 designator.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
606 AttributeValueType valueLimit = new AttributeValueType();
607 valueLimit.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
609 // Yes really use toString(), the marshaller will
610 // throw an exception if this is an integer object
613 valueLimit.getContent().add(max.toString());
614 ObjectFactory factory = new ObjectFactory();
616 ApplyType applyOneAndOnly = new ApplyType();
617 applyOneAndOnly.setDescription("Unbag the min");
618 applyOneAndOnly.setFunctionId(XACML3.ID_FUNCTION_INTEGER_ONE_AND_ONLY.stringValue());
619 applyOneAndOnly.getExpression().add(factory.createAttributeDesignator(designator));
621 ApplyType applyLessThanEqual = new ApplyType();
622 applyLessThanEqual.setDescription("return true if current count is less than or equal.");
623 applyLessThanEqual.setFunctionId(XACML3.ID_FUNCTION_INTEGER_LESS_THAN_OR_EQUAL.stringValue());
624 applyLessThanEqual.getExpression().add(factory.createApply(applyOneAndOnly));
625 applyLessThanEqual.getExpression().add(factory.createAttributeValue(valueLimit));
627 return applyLessThanEqual;
630 private static Integer parseInteger(String strInteger) {
631 Integer theInt = null;
633 theInt = Integer.parseInt(strInteger);
634 } catch (NumberFormatException e) {
635 LOGGER.warn("Expecting an integer", e);
637 Double dblLimit = Double.parseDouble(strInteger);
638 theInt = dblLimit.intValue();
639 } catch (NumberFormatException e1) {
640 LOGGER.error("Failed to parse expected integer as a double", e);
647 private static AdviceExpressionsType generateRequestIdAdvice() {
648 AdviceExpressionType adviceExpression = new AdviceExpressionType();
649 adviceExpression.setAppliesTo(EffectType.PERMIT);
650 adviceExpression.setAdviceId(ToscaDictionary.ID_ADVICE_GUARD.stringValue());
652 AttributeDesignatorType designator = new AttributeDesignatorType();
653 designator.setAttributeId(ToscaDictionary.ID_SUBJECT_GUARD_REQUESTID.stringValue());
654 designator.setCategory(XACML3.ID_SUBJECT_CATEGORY_ACCESS_SUBJECT.stringValue());
655 designator.setDataType(XACML3.ID_DATATYPE_STRING.stringValue());
657 AttributeAssignmentExpressionType assignment = new AttributeAssignmentExpressionType();
658 assignment.setAttributeId(ToscaDictionary.ID_ADVICE_GUARD_REQUESTID.stringValue());
659 assignment.setCategory(XACML3.ID_SUBJECT_CATEGORY_ACCESS_SUBJECT.stringValue());
660 assignment.setExpression(new ObjectFactory().createAttributeDesignator(designator));
662 adviceExpression.getAttributeAssignmentExpression().add(assignment);
664 AdviceExpressionsType adviceExpressions = new AdviceExpressionsType();
665 adviceExpressions.getAdviceExpression().add(adviceExpression);
667 return adviceExpressions;