2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2020 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;
33 import java.util.Collection;
35 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AllOfType;
36 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType;
37 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ApplyType;
38 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType;
39 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType;
40 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ConditionType;
41 import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType;
42 import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType;
43 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObjectFactory;
44 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType;
45 import oasis.names.tc.xacml._3_0.core.schema.wd_17.RuleType;
46 import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType;
47 import org.onap.policy.models.decisions.concepts.DecisionRequest;
48 import org.onap.policy.models.decisions.concepts.DecisionResponse;
49 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
50 import org.onap.policy.pdp.xacml.application.common.ToscaDictionary;
51 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException;
52 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslator;
53 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslatorUtils;
54 import org.onap.policy.pdp.xacml.application.common.operationshistory.CountRecentOperationsPip;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
58 public class GuardTranslator implements ToscaPolicyTranslator {
59 private static final Logger LOGGER = LoggerFactory.getLogger(GuardTranslator.class);
62 // common guard property fields
64 public static final String FIELD_ACTOR = "actor";
65 public static final String FIELD_OPERATION = "operation";
66 public static final String FIELD_CONTROLLOOP = "id";
67 public static final String FIELD_TIMERANGE = "timeRange";
70 // frequency property fields
72 public static final String FIELD_TIMEWINDOW = "timeWindow";
73 public static final String FIELD_TIMEUNITS = "timeUnits";
74 public static final String FIELD_LIMIT = "limit";
77 // minmax property fields
79 public static final String FIELD_TARGET = "target";
80 public static final String FIELD_MIN = "min";
81 public static final String FIELD_MAX = "max";
84 // blacklist property fields
86 public static final String FIELD_BLACKLIST = "blacklist";
88 public static final String POLICYTYPE_FREQUENCY = "onap.policies.controlloop.guard.common.FrequencyLimiter";
89 public static final String POLICYTYPE_MINMAX = "onap.policies.controlloop.guard.common.MinMax";
90 public static final String POLICYTYPE_BLACKLIST = "onap.policies.controlloop.guard.common.Blacklist";
92 public GuardTranslator() {
99 public PolicyType convertPolicy(ToscaPolicy toscaPolicy) throws ToscaPolicyConversionException {
101 // Policy name should be at the root
103 String policyName = toscaPolicy.getMetadata().get("policy-id");
105 // Set it as the policy ID
107 PolicyType newPolicyType = new PolicyType();
108 newPolicyType.setPolicyId(policyName);
110 // Optional description
112 newPolicyType.setDescription(toscaPolicy.getDescription());
114 // There should be a metadata section
116 this.fillMetadataSection(newPolicyType, toscaPolicy.getMetadata());
118 // Generate the TargetType - add true if not blacklist
120 newPolicyType.setTarget(this.generateTargetType(toscaPolicy.getProperties(),
121 ! POLICYTYPE_BLACKLIST.equals(toscaPolicy.getType())));
123 // Add specific's per guard policy type
125 if (POLICYTYPE_FREQUENCY.equals(toscaPolicy.getType())) {
126 newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_DENY_UNLESS_PERMIT.stringValue());
127 generateFrequencyRules(toscaPolicy, policyName, newPolicyType);
128 } else if (POLICYTYPE_MINMAX.equals(toscaPolicy.getType())) {
129 newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_DENY_UNLESS_PERMIT.stringValue());
130 generateMinMaxRules(toscaPolicy, policyName, newPolicyType);
131 } else if (POLICYTYPE_BLACKLIST.equals(toscaPolicy.getType())) {
132 newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_PERMIT_UNLESS_DENY.stringValue());
133 generateBlacklistRules(toscaPolicy, policyName, newPolicyType);
135 throw new ToscaPolicyConversionException("Unknown guard policy type " + toscaPolicy.getType());
137 return newPolicyType;
143 public Request convertRequest(DecisionRequest request) {
144 LOGGER.info("Converting Request {}", request);
146 return RequestParser.parseRequest(GuardPolicyRequest.createInstance(request));
147 } catch (IllegalArgumentException | IllegalAccessException | DataTypeException e) {
148 LOGGER.error("Failed to convert DecisionRequest", e);
151 // TODO throw exception
159 public DecisionResponse convertResponse(Response xacmlResponse) {
160 LOGGER.info("Converting Response {}", xacmlResponse);
161 DecisionResponse decisionResponse = new DecisionResponse();
163 // Iterate through all the results
165 for (Result xacmlResult : xacmlResponse.getResults()) {
169 if (xacmlResult.getDecision() == Decision.PERMIT) {
171 // Just simply return a Permit response
173 decisionResponse.setStatus(Decision.PERMIT.toString());
174 } else if (xacmlResult.getDecision() == Decision.DENY) {
176 // Just simply return a Deny response
178 decisionResponse.setStatus(Decision.DENY.toString());
181 // There is no guard policy, so we return a permit
183 decisionResponse.setStatus(Decision.PERMIT.toString());
187 return decisionResponse;
191 * From the TOSCA metadata section, pull in values that are needed into the XACML policy.
193 * @param policy Policy Object to store the metadata
194 * @param map The Metadata TOSCA Map
195 * @return Same Policy Object
197 protected PolicyType fillMetadataSection(PolicyType policy, Map<String, String> map) {
199 // NOTE: The models code ensures the metadata section ALWAYS exists
202 // Add in the Policy Version
204 policy.setVersion(map.get("policy-version"));
209 * Generate the targettype for the policy. Optional to add MatchType for the target. eg. the
210 * blacklist policy type uses the target in a different manner.
212 * @param properties TOSCA properties object
213 * @param addTargets true to go ahead and add target to the match list.
214 * @return TargetType object
215 * @throws ToscaPolicyConversionException if there is a missing property
217 protected TargetType generateTargetType(Map<String, Object> properties, boolean addTargets)
218 throws ToscaPolicyConversionException {
220 // Go through potential properties
222 AllOfType allOf = new AllOfType();
223 if (properties.containsKey(FIELD_ACTOR)) {
224 addMatch(allOf, properties.get(FIELD_ACTOR), ToscaDictionary.ID_RESOURCE_GUARD_ACTOR);
226 if (properties.containsKey(FIELD_OPERATION)) {
227 addMatch(allOf, properties.get(FIELD_OPERATION), ToscaDictionary.ID_RESOURCE_GUARD_RECIPE);
229 if (addTargets && properties.containsKey(FIELD_TARGET)) {
230 addMatch(allOf, properties.get(FIELD_TARGET), ToscaDictionary.ID_RESOURCE_GUARD_TARGETID);
232 if (properties.containsKey(FIELD_CONTROLLOOP)) {
233 addMatch(allOf, properties.get(FIELD_CONTROLLOOP), ToscaDictionary.ID_RESOURCE_GUARD_CLNAME);
235 if (properties.containsKey(FIELD_TIMERANGE)) {
236 addTimeRangeMatch(allOf, properties.get(FIELD_TIMERANGE));
241 TargetType target = new TargetType();
242 AnyOfType anyOf = new AnyOfType();
243 anyOf.getAllOf().add(allOf);
244 target.getAnyOf().add(anyOf);
248 @SuppressWarnings("unchecked")
249 protected AllOfType addMatch(AllOfType allOf, Object value, Identifier attributeId) {
250 if (value instanceof String) {
251 if (".*".equals(value.toString())) {
253 // There's no point to even have a match
260 MatchType match = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
261 XACML3.ID_FUNCTION_STRING_EQUAL,
263 XACML3.ID_DATATYPE_STRING,
265 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
267 allOf.getMatch().add(match);
271 if (value instanceof Collection) {
272 ((Collection<String>) value).forEach(val -> {
273 MatchType match = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
274 XACML3.ID_FUNCTION_STRING_EQUAL,
276 XACML3.ID_DATATYPE_STRING,
278 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
280 allOf.getMatch().add(match);
286 @SuppressWarnings("rawtypes")
287 protected void addTimeRangeMatch(AllOfType allOf, Object timeRange)
288 throws ToscaPolicyConversionException {
289 if (! (timeRange instanceof Map)) {
290 throw new ToscaPolicyConversionException("timeRange is not a map object " + timeRange.getClass());
293 MatchType matchStart = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
294 XACML3.ID_FUNCTION_TIME_GREATER_THAN_OR_EQUAL,
295 ((Map) timeRange).get("start_time").toString(),
296 XACML3.ID_DATATYPE_TIME,
297 XACML3.ID_ENVIRONMENT_CURRENT_TIME,
298 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
300 allOf.getMatch().add(matchStart);
302 MatchType matchEnd = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
303 XACML3.ID_FUNCTION_TIME_LESS_THAN_OR_EQUAL,
304 ((Map) timeRange).get("end_time").toString(),
305 XACML3.ID_DATATYPE_TIME,
306 XACML3.ID_ENVIRONMENT_CURRENT_TIME,
307 XACML3.ID_ATTRIBUTE_CATEGORY_ENVIRONMENT);
309 allOf.getMatch().add(matchEnd);
312 protected void generateFrequencyRules(ToscaPolicy toscaPolicy, String policyName, PolicyType newPolicyType)
313 throws ToscaPolicyConversionException {
315 // We must have the limit
317 if (! toscaPolicy.getProperties().containsKey(FIELD_LIMIT)) {
318 throw new ToscaPolicyConversionException("Missing property limit");
321 // See if its possible to generate a count
323 Integer limit = ToscaPolicyTranslatorUtils.parseInteger(
324 toscaPolicy.getProperties().get(FIELD_LIMIT).toString());
326 throw new ToscaPolicyConversionException("Missing limit value");
328 String timeWindow = null;
329 if (toscaPolicy.getProperties().containsKey(FIELD_TIMEWINDOW)) {
330 Integer intTimeWindow = ToscaPolicyTranslatorUtils.parseInteger(
331 toscaPolicy.getProperties().get(FIELD_TIMEWINDOW).toString());
332 if (intTimeWindow == null) {
333 throw new ToscaPolicyConversionException("timeWindow is not an integer");
335 timeWindow = intTimeWindow.toString();
337 String timeUnits = null;
338 if (toscaPolicy.getProperties().containsKey(FIELD_TIMEUNITS)) {
339 timeUnits = toscaPolicy.getProperties().get(FIELD_TIMEUNITS).toString();
344 final ApplyType countCheck = generateCountCheck(limit, timeWindow, timeUnits);
346 // Create our condition
348 final ConditionType condition = new ConditionType();
349 condition.setExpression(new ObjectFactory().createApply(countCheck));
352 // Now we can create our rule
354 RuleType frequencyRule = new RuleType();
355 frequencyRule.setDescription("Frequency limit permit rule");
356 frequencyRule.setRuleId(policyName + ":frequency");
357 frequencyRule.setEffect(EffectType.PERMIT);
358 frequencyRule.setTarget(new TargetType());
362 frequencyRule.setCondition(condition);
364 // Add the rule to the policy
366 newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(frequencyRule);
369 protected ApplyType generateCountCheck(Integer limit, String timeWindow, String timeUnits) {
370 AttributeDesignatorType designator = new AttributeDesignatorType();
371 designator.setAttributeId(ToscaDictionary.ID_RESOURCE_GUARD_OPERATIONCOUNT.stringValue());
372 designator.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE.stringValue());
373 designator.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
375 // Setup issuer - used by the operations PIP to determine
376 // how to do the database query.
378 String issuer = ToscaDictionary.GUARD_ISSUER_PREFIX
379 + CountRecentOperationsPip.ISSUER_NAME
380 + ":tw:" + timeWindow + ":" + timeUnits;
381 designator.setIssuer(issuer);
383 AttributeValueType valueLimit = new AttributeValueType();
384 valueLimit.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
386 // Yes really use toString(), the marshaller will
387 // throw an exception if this is an integer object
390 valueLimit.getContent().add(limit.toString());
392 ObjectFactory factory = new ObjectFactory();
394 ApplyType applyOneAndOnly = new ApplyType();
395 applyOneAndOnly.setDescription("Unbag the limit");
396 applyOneAndOnly.setFunctionId(XACML3.ID_FUNCTION_INTEGER_ONE_AND_ONLY.stringValue());
397 applyOneAndOnly.getExpression().add(factory.createAttributeDesignator(designator));
399 ApplyType applyLessThan = new ApplyType();
400 applyLessThan.setDescription("return true if current count is less than.");
401 applyLessThan.setFunctionId(XACML3.ID_FUNCTION_INTEGER_LESS_THAN.stringValue());
402 applyLessThan.getExpression().add(factory.createApply(applyOneAndOnly));
403 applyLessThan.getExpression().add(factory.createAttributeValue(valueLimit));
405 return applyLessThan;
408 protected void generateMinMaxRules(ToscaPolicy toscaPolicy, String policyName, PolicyType newPolicyType)
409 throws ToscaPolicyConversionException {
413 if (! toscaPolicy.getProperties().containsKey(FIELD_TARGET)) {
414 throw new ToscaPolicyConversionException("Missing target field in minmax policy");
416 MatchType matchTarget = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
417 XACML3.ID_FUNCTION_STRING_EQUAL,
418 toscaPolicy.getProperties().get(FIELD_TARGET).toString(),
419 XACML3.ID_DATATYPE_STRING,
420 ToscaDictionary.ID_RESOURCE_GUARD_TARGETID,
421 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
423 // For the min, if the # of instances is less than the minimum
424 // then allow the scale.
427 if (toscaPolicy.getProperties().containsKey(FIELD_MIN)) {
428 min = ToscaPolicyTranslatorUtils.parseInteger(toscaPolicy.getProperties().get(FIELD_MIN).toString());
429 MatchType matchMin = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
430 XACML3.ID_FUNCTION_INTEGER_GREATER_THAN,
432 XACML3.ID_DATATYPE_INTEGER,
433 ToscaDictionary.ID_RESOURCE_GUARD_VFCOUNT,
434 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
436 newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(
437 generateMinMaxRule(matchTarget, matchMin, policyName + ":minrule", "check minimum"));
440 if (toscaPolicy.getProperties().containsKey(FIELD_MAX)) {
441 max = ToscaPolicyTranslatorUtils.parseInteger(toscaPolicy.getProperties().get(FIELD_MAX).toString());
442 MatchType matchMax = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
443 XACML3.ID_FUNCTION_INTEGER_GREATER_THAN,
445 XACML3.ID_DATATYPE_INTEGER,
446 ToscaDictionary.ID_RESOURCE_GUARD_VFCOUNT,
447 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
449 newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(
450 generateMinMaxRule(matchTarget, matchMax, policyName + ":maxrule", "check maximum"));
453 // Do we have at least a min or max?
455 if (min == null && max == null) {
456 throw new ToscaPolicyConversionException("Missing min or max field in minmax policy");
460 protected RuleType generateMinMaxRule(MatchType matchTarget, MatchType matchMinOrMax, String ruleId, String desc) {
461 AllOfType allOf = new AllOfType();
462 allOf.getMatch().add(matchTarget);
463 allOf.getMatch().add(matchMinOrMax);
464 AnyOfType anyOf = new AnyOfType();
465 anyOf.getAllOf().add(allOf);
466 TargetType target = new TargetType();
467 target.getAnyOf().add(anyOf);
468 RuleType minMaxRule = new RuleType();
469 minMaxRule.setEffect(EffectType.PERMIT);
470 minMaxRule.setDescription(desc);
471 minMaxRule.setRuleId(ruleId);
472 minMaxRule.setTarget(target);
476 protected void generateBlacklistRules(ToscaPolicy toscaPolicy, String policyName, PolicyType newPolicyType)
477 throws ToscaPolicyConversionException {
479 // Validate the blacklist exists
481 if (! toscaPolicy.getProperties().containsKey(FIELD_BLACKLIST)) {
482 throw new ToscaPolicyConversionException("Missing blacklist field");
484 final AllOfType allOf = new AllOfType();
485 this.addMatch(allOf, toscaPolicy.getProperties().get(FIELD_BLACKLIST),
486 ToscaDictionary.ID_RESOURCE_GUARD_TARGETID);
488 // Create our rule and add the target
490 RuleType blacklistRule = new RuleType();
491 blacklistRule.setEffect(EffectType.DENY);
492 blacklistRule.setDescription("blacklist the entities");
493 blacklistRule.setRuleId(policyName + ":blacklist");
494 TargetType target = new TargetType();
495 AnyOfType anyOf = new AnyOfType();
496 anyOf.getAllOf().add(allOf);
497 target.getAnyOf().add(anyOf);
498 blacklistRule.setTarget(target);
500 // Add the rule to the policy
502 newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(blacklistRule);