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() {
100 public Object convertPolicy(ToscaPolicy toscaPolicy) throws ToscaPolicyConversionException {
102 // Policy name should be at the root
104 String policyName = toscaPolicy.getMetadata().get("policy-id");
106 // Set it as the policy ID
108 PolicyType newPolicyType = new PolicyType();
109 newPolicyType.setPolicyId(policyName);
111 // Optional description
113 newPolicyType.setDescription(toscaPolicy.getDescription());
115 // There should be a metadata section
117 this.fillMetadataSection(newPolicyType, toscaPolicy.getMetadata());
119 // Generate the TargetType - add true if not blacklist
121 newPolicyType.setTarget(this.generateTargetType(toscaPolicy.getProperties(),
122 ! POLICYTYPE_BLACKLIST.equals(toscaPolicy.getType())));
124 // Add specific's per guard policy type
126 if (POLICYTYPE_FREQUENCY.equals(toscaPolicy.getType())) {
127 newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_DENY_UNLESS_PERMIT.stringValue());
128 generateFrequencyRules(toscaPolicy, policyName, newPolicyType);
129 } else if (POLICYTYPE_MINMAX.equals(toscaPolicy.getType())) {
130 newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_DENY_UNLESS_PERMIT.stringValue());
131 generateMinMaxRules(toscaPolicy, policyName, newPolicyType);
132 } else if (POLICYTYPE_BLACKLIST.equals(toscaPolicy.getType())) {
133 newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_PERMIT_UNLESS_DENY.stringValue());
134 generateBlacklistRules(toscaPolicy, policyName, newPolicyType);
136 throw new ToscaPolicyConversionException("Unknown guard policy type " + toscaPolicy.getType());
138 return newPolicyType;
145 public Request convertRequest(DecisionRequest request) throws ToscaPolicyConversionException {
146 LOGGER.info("Converting Request {}", request);
148 return RequestParser.parseRequest(GuardPolicyRequest.createInstance(request));
149 } catch (IllegalArgumentException | IllegalAccessException | DataTypeException e) {
150 throw new ToscaPolicyConversionException("Failed to convert DecisionRequest", e);
158 public DecisionResponse convertResponse(Response xacmlResponse) {
159 LOGGER.info("Converting Response {}", xacmlResponse);
160 DecisionResponse decisionResponse = new DecisionResponse();
162 // Iterate through all the results
164 for (Result xacmlResult : xacmlResponse.getResults()) {
168 if (xacmlResult.getDecision() == Decision.PERMIT) {
170 // Just simply return a Permit response
172 decisionResponse.setStatus(Decision.PERMIT.toString());
173 } else if (xacmlResult.getDecision() == Decision.DENY) {
175 // Just simply return a Deny response
177 decisionResponse.setStatus(Decision.DENY.toString());
180 // There is no guard policy, so we return a permit
182 decisionResponse.setStatus(Decision.PERMIT.toString());
186 return decisionResponse;
190 * From the TOSCA metadata section, pull in values that are needed into the XACML policy.
192 * @param policy Policy Object to store the metadata
193 * @param map The Metadata TOSCA Map
194 * @return Same Policy Object
196 protected PolicyType fillMetadataSection(PolicyType policy, Map<String, String> map) {
198 // NOTE: The models code ensures the metadata section ALWAYS exists
201 // Add in the Policy Version
203 policy.setVersion(map.get("policy-version"));
208 * Generate the targettype for the policy. Optional to add MatchType for the target. eg. the
209 * blacklist policy type uses the target in a different manner.
211 * @param properties TOSCA properties object
212 * @param addTargets true to go ahead and add target to the match list.
213 * @return TargetType object
214 * @throws ToscaPolicyConversionException if there is a missing property
216 protected TargetType generateTargetType(Map<String, Object> properties, boolean addTargets)
217 throws ToscaPolicyConversionException {
219 // Go through potential properties
221 AllOfType allOf = new AllOfType();
222 if (properties.containsKey(FIELD_ACTOR)) {
223 addMatch(allOf, properties.get(FIELD_ACTOR), ToscaDictionary.ID_RESOURCE_GUARD_ACTOR);
225 if (properties.containsKey(FIELD_OPERATION)) {
226 addMatch(allOf, properties.get(FIELD_OPERATION), ToscaDictionary.ID_RESOURCE_GUARD_RECIPE);
228 if (addTargets && properties.containsKey(FIELD_TARGET)) {
229 addMatch(allOf, properties.get(FIELD_TARGET), ToscaDictionary.ID_RESOURCE_GUARD_TARGETID);
231 if (properties.containsKey(FIELD_CONTROLLOOP)) {
232 addMatch(allOf, properties.get(FIELD_CONTROLLOOP), ToscaDictionary.ID_RESOURCE_GUARD_CLNAME);
234 if (properties.containsKey(FIELD_TIMERANGE)) {
235 addTimeRangeMatch(allOf, properties.get(FIELD_TIMERANGE));
240 TargetType target = new TargetType();
241 AnyOfType anyOf = new AnyOfType();
242 anyOf.getAllOf().add(allOf);
243 target.getAnyOf().add(anyOf);
247 @SuppressWarnings("unchecked")
248 protected AllOfType addMatch(AllOfType allOf, Object value, Identifier attributeId) {
249 if (value instanceof String) {
250 if (".*".equals(value.toString())) {
252 // There's no point to even have a match
259 MatchType match = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
260 XACML3.ID_FUNCTION_STRING_EQUAL,
262 XACML3.ID_DATATYPE_STRING,
264 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
266 allOf.getMatch().add(match);
270 if (value instanceof Collection) {
271 ((Collection<String>) value).forEach(val -> {
272 MatchType match = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
273 XACML3.ID_FUNCTION_STRING_EQUAL,
275 XACML3.ID_DATATYPE_STRING,
277 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
279 allOf.getMatch().add(match);
285 @SuppressWarnings("rawtypes")
286 protected void addTimeRangeMatch(AllOfType allOf, Object timeRange)
287 throws ToscaPolicyConversionException {
288 if (! (timeRange instanceof Map)) {
289 throw new ToscaPolicyConversionException("timeRange is not a map object " + timeRange.getClass());
292 MatchType matchStart = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
293 XACML3.ID_FUNCTION_TIME_GREATER_THAN_OR_EQUAL,
294 ((Map) timeRange).get("start_time").toString(),
295 XACML3.ID_DATATYPE_TIME,
296 XACML3.ID_ENVIRONMENT_CURRENT_TIME,
297 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
299 allOf.getMatch().add(matchStart);
301 MatchType matchEnd = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
302 XACML3.ID_FUNCTION_TIME_LESS_THAN_OR_EQUAL,
303 ((Map) timeRange).get("end_time").toString(),
304 XACML3.ID_DATATYPE_TIME,
305 XACML3.ID_ENVIRONMENT_CURRENT_TIME,
306 XACML3.ID_ATTRIBUTE_CATEGORY_ENVIRONMENT);
308 allOf.getMatch().add(matchEnd);
311 protected void generateFrequencyRules(ToscaPolicy toscaPolicy, String policyName, PolicyType newPolicyType)
312 throws ToscaPolicyConversionException {
314 // We must have the limit
316 if (! toscaPolicy.getProperties().containsKey(FIELD_LIMIT)) {
317 throw new ToscaPolicyConversionException("Missing property limit");
320 // See if its possible to generate a count
322 Integer limit = ToscaPolicyTranslatorUtils.parseInteger(
323 toscaPolicy.getProperties().get(FIELD_LIMIT).toString());
325 throw new ToscaPolicyConversionException("Missing limit value");
327 String timeWindow = null;
328 if (toscaPolicy.getProperties().containsKey(FIELD_TIMEWINDOW)) {
329 Integer intTimeWindow = ToscaPolicyTranslatorUtils.parseInteger(
330 toscaPolicy.getProperties().get(FIELD_TIMEWINDOW).toString());
331 if (intTimeWindow == null) {
332 throw new ToscaPolicyConversionException("timeWindow is not an integer");
334 timeWindow = intTimeWindow.toString();
336 String timeUnits = null;
337 if (toscaPolicy.getProperties().containsKey(FIELD_TIMEUNITS)) {
338 timeUnits = toscaPolicy.getProperties().get(FIELD_TIMEUNITS).toString();
343 final ApplyType countCheck = generateCountCheck(limit, timeWindow, timeUnits);
345 // Create our condition
347 final ConditionType condition = new ConditionType();
348 condition.setExpression(new ObjectFactory().createApply(countCheck));
351 // Now we can create our rule
353 RuleType frequencyRule = new RuleType();
354 frequencyRule.setDescription("Frequency limit permit rule");
355 frequencyRule.setRuleId(policyName + ":frequency");
356 frequencyRule.setEffect(EffectType.PERMIT);
357 frequencyRule.setTarget(new TargetType());
361 frequencyRule.setCondition(condition);
363 // Add the rule to the policy
365 newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(frequencyRule);
368 protected ApplyType generateCountCheck(Integer limit, String timeWindow, String timeUnits) {
369 AttributeDesignatorType designator = new AttributeDesignatorType();
370 designator.setAttributeId(ToscaDictionary.ID_RESOURCE_GUARD_OPERATIONCOUNT.stringValue());
371 designator.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE.stringValue());
372 designator.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
374 // Setup issuer - used by the operations PIP to determine
375 // how to do the database query.
377 String issuer = ToscaDictionary.GUARD_ISSUER_PREFIX
378 + CountRecentOperationsPip.ISSUER_NAME
379 + ":tw:" + timeWindow + ":" + timeUnits;
380 designator.setIssuer(issuer);
382 AttributeValueType valueLimit = new AttributeValueType();
383 valueLimit.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
385 // Yes really use toString(), the marshaller will
386 // throw an exception if this is an integer object
389 valueLimit.getContent().add(limit.toString());
391 ObjectFactory factory = new ObjectFactory();
393 ApplyType applyOneAndOnly = new ApplyType();
394 applyOneAndOnly.setDescription("Unbag the limit");
395 applyOneAndOnly.setFunctionId(XACML3.ID_FUNCTION_INTEGER_ONE_AND_ONLY.stringValue());
396 applyOneAndOnly.getExpression().add(factory.createAttributeDesignator(designator));
398 ApplyType applyLessThan = new ApplyType();
399 applyLessThan.setDescription("return true if current count is less than.");
400 applyLessThan.setFunctionId(XACML3.ID_FUNCTION_INTEGER_LESS_THAN.stringValue());
401 applyLessThan.getExpression().add(factory.createApply(applyOneAndOnly));
402 applyLessThan.getExpression().add(factory.createAttributeValue(valueLimit));
404 return applyLessThan;
407 protected void generateMinMaxRules(ToscaPolicy toscaPolicy, String policyName, PolicyType newPolicyType)
408 throws ToscaPolicyConversionException {
412 if (! toscaPolicy.getProperties().containsKey(FIELD_TARGET)) {
413 throw new ToscaPolicyConversionException("Missing target field in minmax policy");
415 MatchType matchTarget = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
416 XACML3.ID_FUNCTION_STRING_EQUAL,
417 toscaPolicy.getProperties().get(FIELD_TARGET).toString(),
418 XACML3.ID_DATATYPE_STRING,
419 ToscaDictionary.ID_RESOURCE_GUARD_TARGETID,
420 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
422 // For the min, if the # of instances is less than the minimum
423 // then allow the scale.
426 if (toscaPolicy.getProperties().containsKey(FIELD_MIN)) {
427 min = ToscaPolicyTranslatorUtils.parseInteger(toscaPolicy.getProperties().get(FIELD_MIN).toString());
428 MatchType matchMin = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
429 XACML3.ID_FUNCTION_INTEGER_GREATER_THAN,
431 XACML3.ID_DATATYPE_INTEGER,
432 ToscaDictionary.ID_RESOURCE_GUARD_VFCOUNT,
433 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
435 newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(
436 generateMinMaxRule(matchTarget, matchMin, policyName + ":minrule", "check minimum"));
439 if (toscaPolicy.getProperties().containsKey(FIELD_MAX)) {
440 max = ToscaPolicyTranslatorUtils.parseInteger(toscaPolicy.getProperties().get(FIELD_MAX).toString());
441 MatchType matchMax = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
442 XACML3.ID_FUNCTION_INTEGER_GREATER_THAN,
444 XACML3.ID_DATATYPE_INTEGER,
445 ToscaDictionary.ID_RESOURCE_GUARD_VFCOUNT,
446 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
448 newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(
449 generateMinMaxRule(matchTarget, matchMax, policyName + ":maxrule", "check maximum"));
452 // Do we have at least a min or max?
454 if (min == null && max == null) {
455 throw new ToscaPolicyConversionException("Missing min or max field in minmax policy");
459 protected RuleType generateMinMaxRule(MatchType matchTarget, MatchType matchMinOrMax, String ruleId, String desc) {
460 AllOfType allOf = new AllOfType();
461 allOf.getMatch().add(matchTarget);
462 allOf.getMatch().add(matchMinOrMax);
463 AnyOfType anyOf = new AnyOfType();
464 anyOf.getAllOf().add(allOf);
465 TargetType target = new TargetType();
466 target.getAnyOf().add(anyOf);
467 RuleType minMaxRule = new RuleType();
468 minMaxRule.setEffect(EffectType.PERMIT);
469 minMaxRule.setDescription(desc);
470 minMaxRule.setRuleId(ruleId);
471 minMaxRule.setTarget(target);
475 protected void generateBlacklistRules(ToscaPolicy toscaPolicy, String policyName, PolicyType newPolicyType)
476 throws ToscaPolicyConversionException {
478 // Validate the blacklist exists
480 if (! toscaPolicy.getProperties().containsKey(FIELD_BLACKLIST)) {
481 throw new ToscaPolicyConversionException("Missing blacklist field");
484 // Get the blacklist, which should be an array or collection.
486 Object arrayBlacklisted = toscaPolicy.getProperties().get(FIELD_BLACKLIST);
487 if (!(arrayBlacklisted instanceof Collection)) {
488 throw new ToscaPolicyConversionException("Blacklist is not a collection");
491 // Iterate the entries and create individual AnyOf so each entry is
494 TargetType target = new TargetType();
495 for (Object blacklisted : ((Collection<?>) arrayBlacklisted)) {
496 AllOfType allOf = new AllOfType();
497 this.addMatch(allOf, blacklisted, ToscaDictionary.ID_RESOURCE_GUARD_TARGETID);
498 AnyOfType anyOf = new AnyOfType();
499 anyOf.getAllOf().add(allOf);
500 target.getAnyOf().add(anyOf);
503 // Create our rule and add the target
505 RuleType blacklistRule = new RuleType();
506 blacklistRule.setEffect(EffectType.DENY);
507 blacklistRule.setDescription("blacklist the entities");
508 blacklistRule.setRuleId(policyName + ":blacklist");
509 blacklistRule.setTarget(target);
511 // Add the rule to the policy
513 newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(blacklistRule);