d606cc2e92cb10f3216256ec4bb72d5ee34ceffa
[policy/xacml-pdp.git] / applications / guard / src / main / java / org / onap / policy / xacml / pdp / application / guard / GuardTranslator.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2020-2021 AT&T Intellectual Property. All rights reserved.
6  * Modifications Copyright (C) 2020 Nordix Foundation.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  * SPDX-License-Identifier: Apache-2.0
21  * ============LICENSE_END=========================================================
22  */
23
24 package org.onap.policy.xacml.pdp.application.guard;
25
26 import com.att.research.xacml.api.DataTypeException;
27 import com.att.research.xacml.api.Decision;
28 import com.att.research.xacml.api.Identifier;
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.IdentifierImpl;
34 import com.att.research.xacml.std.annotations.RequestParser;
35 import com.google.gson.annotations.SerializedName;
36 import java.time.OffsetDateTime;
37 import java.time.OffsetTime;
38 import java.util.Collection;
39 import java.util.List;
40 import java.util.Map;
41 import lombok.Getter;
42 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AllOfType;
43 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType;
44 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ApplyType;
45 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType;
46 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType;
47 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ConditionType;
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.PolicyType;
52 import oasis.names.tc.xacml._3_0.core.schema.wd_17.RuleType;
53 import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType;
54 import oasis.names.tc.xacml._3_0.core.schema.wd_17.VariableDefinitionType;
55 import oasis.names.tc.xacml._3_0.core.schema.wd_17.VariableReferenceType;
56 import org.onap.policy.common.parameters.annotations.NotBlank;
57 import org.onap.policy.common.parameters.annotations.NotNull;
58 import org.onap.policy.common.parameters.annotations.Valid;
59 import org.onap.policy.models.decisions.concepts.DecisionRequest;
60 import org.onap.policy.models.decisions.concepts.DecisionResponse;
61 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
62 import org.onap.policy.pdp.xacml.application.common.ToscaDictionary;
63 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException;
64 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslator;
65 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslatorUtils;
66 import org.onap.policy.pdp.xacml.application.common.operationshistory.CountRecentOperationsPip;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
69
70 public class GuardTranslator implements ToscaPolicyTranslator {
71     private static final Logger LOGGER = LoggerFactory.getLogger(GuardTranslator.class);
72
73     //
74     // common guard property fields
75     //
76     public static final String FIELD_ACTOR = "actor";
77     public static final String FIELD_OPERATION = "operation";
78     public static final String FIELD_CONTROLLOOP = "id";
79     public static final String FIELD_TIMERANGE = "timeRange";
80
81     //
82     // frequency property fields
83     //
84     public static final String FIELD_TIMEWINDOW = "timeWindow";
85     public static final String FIELD_TIMEUNITS = "timeUnits";
86     public static final String FIELD_LIMIT = "limit";
87
88     //
89     // minmax property fields
90     //
91     public static final String FIELD_TARGET = "target";
92     public static final String FIELD_MIN = "min";
93     public static final String FIELD_MAX = "max";
94
95     //
96     // blacklist property fields
97     //
98     public static final String FIELD_BLACKLIST = "blacklist";
99
100     //
101     // filter property fields
102     //
103     public static final String FIELD_FILTER_WHITELIST = "whitelist";
104     public static final String FIELD_FILTER_ALGORITHM = "algorithm";
105     public static final String FIELD_FILTER_FILTERS = "filters";
106     public static final String FIELD_FILTER_FIELD = "field";
107     public static final String FIELD_FILTER_FUNCTION = "function";
108     public static final String FIELD_FILTER_FILTER = "filter";
109     public static final String FIELD_FILTER_BLACKLIST = "blacklist";
110
111     public static final String POLICYTYPE_FREQUENCY = "onap.policies.controlloop.guard.common.FrequencyLimiter";
112     public static final String POLICYTYPE_MINMAX = "onap.policies.controlloop.guard.common.MinMax";
113     public static final String POLICYTYPE_BLACKLIST = "onap.policies.controlloop.guard.common.Blacklist";
114     public static final String POLICYTYPE_FILTER = "onap.policies.controlloop.guard.common.Filter";
115
116     //
117     // Variable definitions
118     //
119     private static final String VARIABLE_TIMEINRANGE = "timeInRange";
120
121     public GuardTranslator() {
122         super();
123     }
124
125     /**
126      * Convert the policy.
127      */
128     @Override
129     public Object convertPolicy(ToscaPolicy toscaPolicy) throws ToscaPolicyConversionException {
130         //
131         // Policy name should be at the root
132         //
133         String policyName = toscaPolicy.getMetadata().get("policy-id");
134         //
135         // Set it as the policy ID
136         //
137         var newPolicyType = new PolicyType();
138         newPolicyType.setPolicyId(policyName);
139         //
140         // Optional description
141         //
142         newPolicyType.setDescription(toscaPolicy.getDescription());
143         //
144         // There should be a metadata section
145         //
146         this.fillMetadataSection(newPolicyType, toscaPolicy.getMetadata());
147         //
148         // There should be properties metadata section
149         //
150         if (toscaPolicy.getProperties() == null) {
151             throw new ToscaPolicyConversionException("no properties specified on guard policy: " + policyName);
152         }
153         //
154         // Generate the TargetType - add true if not blacklist
155         //
156         newPolicyType.setTarget(this.generateTargetType(toscaPolicy.getProperties(),
157                 ! POLICYTYPE_BLACKLIST.equals(toscaPolicy.getType())));
158         //
159         // Add specific's per guard policy type
160         //
161         if (POLICYTYPE_FREQUENCY.equals(toscaPolicy.getType())) {
162             newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_DENY_UNLESS_PERMIT.stringValue());
163             generateFrequencyRules(toscaPolicy, policyName, newPolicyType);
164         } else if (POLICYTYPE_MINMAX.equals(toscaPolicy.getType())) {
165             newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_DENY_UNLESS_PERMIT.stringValue());
166             generateMinMaxRules(toscaPolicy, policyName, newPolicyType);
167         } else if (POLICYTYPE_BLACKLIST.equals(toscaPolicy.getType())) {
168             newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_PERMIT_UNLESS_DENY.stringValue());
169             generateBlacklistRules(toscaPolicy, policyName, newPolicyType);
170         } else if (POLICYTYPE_FILTER.equals(toscaPolicy.getType())) {
171             newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_PERMIT_UNLESS_DENY.stringValue());
172             generateFilterRules(toscaPolicy, policyName, newPolicyType);
173         } else {
174             throw new ToscaPolicyConversionException("Unknown guard policy type " + toscaPolicy.getType());
175         }
176         //
177         // Add in our variable definition
178         //
179         VariableReferenceType variable = this.createTimeRangeVariable(toscaPolicy.getProperties(), newPolicyType);
180         if (variable != null) {
181             //
182             // Update all the rules to have conditions for this variable
183             //
184             this.addVariableToConditionTypes(variable, newPolicyType);
185         }
186         return newPolicyType;
187     }
188
189     /**
190      * This method iterates through all the existing rules, adding in a conditionType that will test
191      * whether the Variable is true or false. Any existing ConditionType will be updated to AND with the
192      * Variable.
193      *
194      * @param variable VariableDefinitionType to add
195      * @param newPolicyType PolicyType that will be updated
196      */
197     private void addVariableToConditionTypes(VariableReferenceType variable,
198             PolicyType newPolicyType) {
199         //
200         // Iterate through the rules
201         //
202         for (Object objectType : newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition()) {
203             if (objectType instanceof RuleType) {
204                 RuleType rule = (RuleType) objectType;
205                 if (rule.getCondition() == null) {
206                     //
207                     // No condition already, just create and add a new one
208                     //
209                     var condition = new ConditionType();
210                     condition.setExpression(new ObjectFactory().createVariableReference(variable));
211                     rule.setCondition(condition);
212                 } else {
213                     //
214                     // Need to create a new ConditionType that treats all the expressions as an AND
215                     // with the Variable.
216                     //
217                     rule.setCondition(ToscaPolicyTranslatorUtils.addVariableToCondition(rule.getCondition(), variable,
218                             XACML3.ID_FUNCTION_AND));
219                 }
220             }
221         }
222     }
223
224     /**
225      * Convert Request.
226      */
227     @Override
228     public Request convertRequest(DecisionRequest request) throws ToscaPolicyConversionException {
229         LOGGER.info("Converting Request {}", request);
230         try {
231             return RequestParser.parseRequest(GuardPolicyRequest.createInstance(request));
232         } catch (IllegalArgumentException | IllegalAccessException | DataTypeException e) {
233             throw new ToscaPolicyConversionException("Failed to convert DecisionRequest", e);
234         }
235     }
236
237     /**
238      * Convert response.
239      */
240     @Override
241     public DecisionResponse convertResponse(Response xacmlResponse) {
242         LOGGER.info("Converting Response {}", xacmlResponse);
243         var decisionResponse = new DecisionResponse();
244         //
245         // Iterate through all the results
246         //
247         for (Result xacmlResult : xacmlResponse.getResults()) {
248             //
249             // Check the result
250             //
251             if (xacmlResult.getDecision() == Decision.PERMIT) {
252                 //
253                 // Just simply return a Permit response
254                 //
255                 decisionResponse.setStatus(Decision.PERMIT.toString());
256             } else if (xacmlResult.getDecision() == Decision.DENY) {
257                 //
258                 // Just simply return a Deny response
259                 //
260                 decisionResponse.setStatus(Decision.DENY.toString());
261             } else {
262                 //
263                 // There is no guard policy, so we return a permit
264                 //
265                 decisionResponse.setStatus(Decision.PERMIT.toString());
266             }
267         }
268
269         return decisionResponse;
270     }
271
272     /**
273      * From the TOSCA metadata section, pull in values that are needed into the XACML policy.
274      *
275      * @param policy Policy Object to store the metadata
276      * @param map The Metadata TOSCA Map
277      * @return Same Policy Object
278      */
279     protected PolicyType fillMetadataSection(PolicyType policy, Map<String, String> map) {
280         //
281         // NOTE: The models code ensures the metadata section ALWAYS exists
282         //
283         //
284         // Add in the Policy Version
285         //
286         policy.setVersion(map.get("policy-version"));
287         return policy;
288     }
289
290     /**
291      * Generate the targettype for the policy. Optional to add MatchType for the target. eg. the
292      * blacklist policy type uses the target in a different manner.
293      *
294      * @param properties TOSCA properties object
295      * @param addTargets true to go ahead and add target to the match list.
296      * @return TargetType object
297      * @throws ToscaPolicyConversionException if there is a missing property
298      */
299     protected TargetType generateTargetType(Map<String, Object> properties, boolean addTargets)
300             throws ToscaPolicyConversionException {
301         //
302         // Decode the definition from the policy's properties
303         //
304         TargetTypeDefinition targetTypeDef =
305                         ToscaPolicyTranslatorUtils.decodeProperties(properties, TargetTypeDefinition.class);
306         //
307         // Go through potential properties
308         //
309         var allOf = new AllOfType();
310         if (targetTypeDef.getActor() != null) {
311             addMatch(allOf, targetTypeDef.getActor(), ToscaDictionary.ID_RESOURCE_GUARD_ACTOR);
312         }
313         if (targetTypeDef.getOperation() != null) {
314             addMatch(allOf, targetTypeDef.getOperation(), ToscaDictionary.ID_RESOURCE_GUARD_RECIPE);
315         }
316         if (addTargets && targetTypeDef.getTarget() != null) {
317             addMatch(allOf, targetTypeDef.getTarget(), ToscaDictionary.ID_RESOURCE_GUARD_TARGETID);
318         }
319         if (targetTypeDef.getId() != null) {
320             addMatch(allOf, targetTypeDef.getId(), ToscaDictionary.ID_RESOURCE_GUARD_CLNAME);
321         }
322         //
323         // Create target
324         //
325         var target = new TargetType();
326         var anyOf = new AnyOfType();
327         anyOf.getAllOf().add(allOf);
328         target.getAnyOf().add(anyOf);
329         return target;
330     }
331
332     @SuppressWarnings("unchecked")
333     protected AllOfType addMatch(AllOfType allOf, Object value, Identifier attributeId) {
334         if (value instanceof String) {
335             if (".*".equals(value.toString())) {
336                 //
337                 // There's no point to even have a match
338                 //
339                 return allOf;
340             } else {
341                 //
342                 // Exact match
343                 //
344                 var match = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
345                     XACML3.ID_FUNCTION_STRING_EQUAL,
346                     value,
347                     XACML3.ID_DATATYPE_STRING,
348                     attributeId,
349                     XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
350
351                 allOf.getMatch().add(match);
352             }
353             return allOf;
354         }
355         if (value instanceof Collection) {
356             ((Collection<String>) value).forEach(val -> {
357                 var match = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
358                         XACML3.ID_FUNCTION_STRING_EQUAL,
359                         val,
360                         XACML3.ID_DATATYPE_STRING,
361                         attributeId,
362                         XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
363
364                 allOf.getMatch().add(match);
365             });
366         }
367         return allOf;
368     }
369
370     protected void addTimeRangeMatch(AllOfType allOf, TimeRange timeRange)
371             throws ToscaPolicyConversionException {
372
373         var matchStart = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
374                 XACML3.ID_FUNCTION_TIME_GREATER_THAN_OR_EQUAL,
375                 timeRange.getStartTime(),
376                 XACML3.ID_DATATYPE_TIME,
377                 XACML3.ID_ENVIRONMENT_CURRENT_TIME,
378                 XACML3.ID_ATTRIBUTE_CATEGORY_ENVIRONMENT);
379
380         allOf.getMatch().add(matchStart);
381
382         var matchEnd = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
383                 XACML3.ID_FUNCTION_TIME_LESS_THAN_OR_EQUAL,
384                 timeRange.getEndTime(),
385                 XACML3.ID_DATATYPE_TIME,
386                 XACML3.ID_ENVIRONMENT_CURRENT_TIME,
387                 XACML3.ID_ATTRIBUTE_CATEGORY_ENVIRONMENT);
388
389         allOf.getMatch().add(matchEnd);
390     }
391
392     protected VariableReferenceType createTimeRangeVariable(Map<String, Object> properties, PolicyType newPolicyType)
393             throws ToscaPolicyConversionException {
394         //
395         // Decode the definition from the policy's properties
396         //
397         TimeRangeDefinition timeRangeDef =
398                         ToscaPolicyTranslatorUtils.decodeProperties(properties, TimeRangeDefinition.class);
399         TimeRange timeRange = timeRangeDef.getTimeRange();
400         if (timeRange == null) {
401             return null;
402         }
403         //
404         // Should also be parseable as an ISO8601 timestamp
405         //
406         var startTimeObject = parseTimestamp(timeRange.getStartTime());
407         var endTimeObject = parseTimestamp(timeRange.getEndTime());
408         //
409         // They should be the same object types. We cannot establish a range
410         // between an OffsetDateTime and an OffsetTime
411         //
412         if (! startTimeObject.getClass().equals(endTimeObject.getClass())) {
413             throw new ToscaPolicyConversionException("start_time and end_time class types do not match");
414         }
415         //
416         // Create the inner timeInRange ApplyType
417         //
418         ApplyType timeInRange = ToscaPolicyTranslatorUtils.generateTimeInRange(timeRange.getStartTime(),
419                         timeRange.getEndTime(), true);
420         var variable = new VariableDefinitionType();
421         variable.setVariableId(VARIABLE_TIMEINRANGE);
422         variable.setExpression(new ObjectFactory().createApply(timeInRange));
423         //
424         // Add it to the policy
425         //
426         newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(variable);
427         //
428         // Create and return the reference to the variable
429         //
430         var reference = new VariableReferenceType();
431         reference.setVariableId(variable.getVariableId());
432         return reference;
433     }
434
435     private Object parseTimestamp(String string) throws ToscaPolicyConversionException {
436         //
437         // First see if it is a full datetime object
438         //
439         try {
440             return OffsetDateTime.parse(string);
441         } catch (Exception e) {
442             LOGGER.warn("timestamp {} could not be parsed. This may not be an error.", string, e);
443         }
444         //
445         // May only be a time object
446         //
447         try {
448             return OffsetTime.parse(string);
449         } catch (Exception e) {
450             throw new ToscaPolicyConversionException("timestamp " + string + " could not be parsed ", e);
451         }
452     }
453
454     protected void generateFrequencyRules(ToscaPolicy toscaPolicy, String policyName, PolicyType newPolicyType)
455             throws ToscaPolicyConversionException {
456         //
457         // Decode the definition from the policy's properties
458         //
459         FrequencyDefinition frequencyDef = ToscaPolicyTranslatorUtils.decodeProperties(toscaPolicy.getProperties(),
460                         FrequencyDefinition.class);
461         //
462         // See if its possible to generate a count
463         //
464         String timeWindow = null;
465         if (frequencyDef.getTimeWindow() != null) {
466             timeWindow = frequencyDef.getTimeWindow().toString();
467         }
468         //
469         // Generate a count
470         //
471         final ApplyType countCheck =
472                         generateCountCheck(frequencyDef.getLimit(), timeWindow, frequencyDef.getTimeUnits());
473         //
474         // Create our condition
475         //
476         final var condition = new ConditionType();
477         condition.setExpression(new ObjectFactory().createApply(countCheck));
478
479         //
480         // Now we can create our rule
481         //
482         var frequencyRule = new RuleType();
483         frequencyRule.setDescription("Frequency limit permit rule");
484         frequencyRule.setRuleId(policyName + ":frequency");
485         frequencyRule.setEffect(EffectType.PERMIT);
486         frequencyRule.setTarget(new TargetType());
487         //
488         // Add the condition
489         //
490         frequencyRule.setCondition(condition);
491         //
492         // Add the rule to the policy
493         //
494         newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(frequencyRule);
495     }
496
497     protected ApplyType generateCountCheck(Integer limit, String timeWindow, String timeUnits) {
498         var designator = new AttributeDesignatorType();
499         designator.setAttributeId(ToscaDictionary.ID_RESOURCE_GUARD_OPERATIONCOUNT.stringValue());
500         designator.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE.stringValue());
501         designator.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
502         //
503         // Setup issuer - used by the operations PIP to determine
504         // how to do the database query.
505         //
506         String issuer = ToscaDictionary.GUARD_ISSUER_PREFIX
507             + CountRecentOperationsPip.ISSUER_NAME
508             + ":tw:" + timeWindow + ":" + timeUnits;
509         designator.setIssuer(issuer);
510
511         var valueLimit = new AttributeValueType();
512         valueLimit.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
513         //
514         // Yes really use toString(), the marshaller will
515         // throw an exception if this is an integer object
516         // and not a string.
517         //
518         valueLimit.getContent().add(limit.toString());
519
520         var factory = new ObjectFactory();
521
522         var applyOneAndOnly = new ApplyType();
523         applyOneAndOnly.setDescription("Unbag the limit");
524         applyOneAndOnly.setFunctionId(XACML3.ID_FUNCTION_INTEGER_ONE_AND_ONLY.stringValue());
525         applyOneAndOnly.getExpression().add(factory.createAttributeDesignator(designator));
526
527         var applyLessThan = new ApplyType();
528         applyLessThan.setDescription("return true if current count is less than.");
529         applyLessThan.setFunctionId(XACML3.ID_FUNCTION_INTEGER_LESS_THAN.stringValue());
530         applyLessThan.getExpression().add(factory.createApply(applyOneAndOnly));
531         applyLessThan.getExpression().add(factory.createAttributeValue(valueLimit));
532
533         return applyLessThan;
534     }
535
536     protected void generateMinMaxRules(ToscaPolicy toscaPolicy, String policyName, PolicyType newPolicyType)
537             throws ToscaPolicyConversionException {
538         //
539         // Decode the definition from the policy's properties
540         //
541         MinMaxDefinition minMaxDef = ToscaPolicyTranslatorUtils.decodeProperties(toscaPolicy.getProperties(),
542                         MinMaxDefinition.class);
543         //
544         // Add the target
545         //
546         var matchTarget = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
547                 XACML3.ID_FUNCTION_STRING_EQUAL,
548                 minMaxDef.getTarget(),
549                 XACML3.ID_DATATYPE_STRING,
550                 ToscaDictionary.ID_RESOURCE_GUARD_TARGETID,
551                 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
552         //
553         // For the min, if the # of instances is less than the minimum
554         // then allow the scale.
555         //
556         if (minMaxDef.getMin() != null) {
557             var matchMin = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
558                     XACML3.ID_FUNCTION_INTEGER_GREATER_THAN,
559                     minMaxDef.getMin().toString(),
560                     XACML3.ID_DATATYPE_INTEGER,
561                     ToscaDictionary.ID_RESOURCE_GUARD_VFCOUNT,
562                     XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
563
564             newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(
565                     generateMinMaxRule(matchTarget, matchMin, policyName + ":minrule", "check minimum"));
566         }
567         if (minMaxDef.getMax() != null) {
568             var matchMax = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
569                     XACML3.ID_FUNCTION_INTEGER_GREATER_THAN,
570                     minMaxDef.getMax().toString(),
571                     XACML3.ID_DATATYPE_INTEGER,
572                     ToscaDictionary.ID_RESOURCE_GUARD_VFCOUNT,
573                     XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
574
575             newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(
576                     generateMinMaxRule(matchTarget, matchMax, policyName + ":maxrule", "check maximum"));
577         }
578         //
579         // Do we have at least a min or max?
580         //
581         if (minMaxDef.getMin() == null && minMaxDef.getMax() == null) {
582             throw new ToscaPolicyConversionException("Missing min or max field in minmax policy");
583         }
584     }
585
586     protected RuleType generateMinMaxRule(MatchType matchTarget, MatchType matchMinOrMax, String ruleId, String desc) {
587         var allOf = new AllOfType();
588         allOf.getMatch().add(matchTarget);
589         allOf.getMatch().add(matchMinOrMax);
590         var anyOf = new AnyOfType();
591         anyOf.getAllOf().add(allOf);
592         var target = new TargetType();
593         target.getAnyOf().add(anyOf);
594         var minMaxRule = new RuleType();
595         minMaxRule.setEffect(EffectType.PERMIT);
596         minMaxRule.setDescription(desc);
597         minMaxRule.setRuleId(ruleId);
598         minMaxRule.setTarget(target);
599         return minMaxRule;
600     }
601
602     protected void generateBlacklistRules(ToscaPolicy toscaPolicy, String policyName, PolicyType newPolicyType)
603             throws ToscaPolicyConversionException {
604         //
605         // Decode the definition from the policy's properties
606         //
607         BlacklistDefinition blacklistDef = ToscaPolicyTranslatorUtils.decodeProperties(toscaPolicy.getProperties(),
608                         BlacklistDefinition.class);
609         //
610         // Iterate the entries and create individual AnyOf so each entry is
611         // treated as an OR.
612         //
613         var target = new TargetType();
614         var anyOf = new AnyOfType();
615         for (Object blacklisted : blacklistDef.blacklist) {
616             var allOf = new AllOfType();
617             this.addMatch(allOf, blacklisted, ToscaDictionary.ID_RESOURCE_GUARD_TARGETID);
618             anyOf.getAllOf().add(allOf);
619         }
620         target.getAnyOf().add(anyOf);
621         //
622         // Create our rule and add the target
623         //
624         var blacklistRule = new RuleType();
625         blacklistRule.setEffect(EffectType.DENY);
626         blacklistRule.setDescription("blacklist the entities");
627         blacklistRule.setRuleId(policyName + ":blacklist");
628         blacklistRule.setTarget(target);
629         //
630         // Add the rule to the policy
631         //
632         newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(blacklistRule);
633     }
634
635     protected void generateFilterRules(ToscaPolicy toscaPolicy, String policyName, PolicyType newPolicyType)
636             throws ToscaPolicyConversionException {
637         //
638         // Decode the definition from the policy's properties
639         //
640         FilterDefinition filterDef = ToscaPolicyTranslatorUtils.decodeProperties(toscaPolicy.getProperties(),
641                         FilterDefinition.class);
642         //
643         // Set the combining algorithm
644         //
645         switch (filterDef.getAlgorithm()) {
646             case "whitelist-overrides":
647                 newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_PERMIT_OVERRIDES.stringValue());
648                 break;
649             case "blacklist-overrides":
650                 newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_DENY_OVERRIDES.stringValue());
651                 break;
652             default:
653                 throw new ToscaPolicyConversionException(
654                                 "Unexpected value for algorithm, should be whitelist-overrides or blacklist-overrides");
655         }
656         //
657         // Iterate the filters
658         //
659         var ruleId = 1;
660         for (FilterAttribute filterAttributes : filterDef.filters) {
661             //
662             // Check fields requiring extra validation
663             //
664             String field = validateFilterPropertyField(filterAttributes.getField());
665             Identifier function = validateFilterPropertyFunction(filterAttributes.getFunction());
666             //
667             // Create our filter rule
668             //
669             RuleType filterRule = createFilterRule(policyName + ":rule" + ruleId++, field, filterAttributes.getFilter(),
670                             function, filterAttributes.getBlacklist());
671             //
672             // Add the rule to the policy
673             //
674             newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(filterRule);
675         }
676     }
677
678     private String validateFilterPropertyField(String field)
679             throws ToscaPolicyConversionException {
680         String fieldLowerCase = field.toLowerCase();
681         switch (fieldLowerCase) {
682             case "generic-vnf.vnf-name":
683             case "generic-vnf.vnf-id":
684             case "generic-vnf.vnf-type":
685             case "generic-vnf.nf-naming-code":
686             case "vserver.vserver-id":
687             case "cloud-region.cloud-region-id":
688                 return fieldLowerCase;
689             default:
690                 throw new ToscaPolicyConversionException("Unexpected value for field in filter");
691         }
692     }
693
694     private Identifier validateFilterPropertyFunction(String function)
695             throws ToscaPolicyConversionException {
696         switch (function.toLowerCase()) {
697             case "string-equal":
698                 return XACML3.ID_FUNCTION_STRING_EQUAL;
699             case "string-equal-ignore-case":
700                 return XACML3.ID_FUNCTION_STRING_EQUAL_IGNORE_CASE;
701             case "string-regexp-match":
702                 return XACML3.ID_FUNCTION_STRING_REGEXP_MATCH;
703             case "string-contains":
704                 return XACML3.ID_FUNCTION_STRING_CONTAINS;
705             case "string-greater-than":
706                 return XACML3.ID_FUNCTION_STRING_GREATER_THAN;
707             case "string-greater-than-or-equal":
708                 return XACML3.ID_FUNCTION_STRING_GREATER_THAN_OR_EQUAL;
709             case "string-less-than":
710                 return XACML3.ID_FUNCTION_STRING_LESS_THAN;
711             case "string-less-than-or-equal":
712                 return XACML3.ID_FUNCTION_STRING_LESS_THAN_OR_EQUAL;
713             case "string-starts-with":
714                 return XACML3.ID_FUNCTION_STRING_STARTS_WITH;
715             case "string-ends-with":
716                 return XACML3.ID_FUNCTION_STRING_ENDS_WITH;
717             default:
718                 throw new ToscaPolicyConversionException("Unexpected value for function in filter");
719         }
720     }
721
722     private RuleType createFilterRule(String ruleId, String field, String filter, Identifier function,
723             boolean isBlacklisted) {
724         var rule = new RuleType();
725         rule.setRuleId(ruleId);
726
727         //
728         // Create the Match
729         //
730         var matchFilter = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
731                 function,
732                 filter,
733                 XACML3.ID_DATATYPE_STRING,
734                 new IdentifierImpl(GuardPolicyRequest.PREFIX_RESOURCE_ATTRIBUTE_ID + field),
735                 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE
736                 );
737         var allOf = new AllOfType();
738         allOf.getMatch().add(matchFilter);
739         var anyOf = new AnyOfType();
740         anyOf.getAllOf().add(allOf);
741         var target = new TargetType();
742         target.getAnyOf().add(anyOf);
743
744         rule.setTarget(target);
745
746         if (isBlacklisted) {
747             rule.setEffect(EffectType.DENY);
748         } else {
749             rule.setEffect(EffectType.PERMIT);
750         }
751         return rule;
752     }
753
754     @Getter
755     public static class TimeRangeDefinition {
756         private @Valid TimeRange timeRange;
757     }
758
759     @Getter
760     public static class TargetTypeDefinition {
761         private String actor;
762         private String operation;
763         private String target;
764         private String id;
765     }
766
767     @Getter
768     @NotNull
769     @NotBlank
770     public static class TimeRange {
771         @SerializedName("start_time")
772         private String startTime;
773
774         @SerializedName("end_time")
775         private String endTime;
776     }
777
778     @Getter
779     public static class FrequencyDefinition {
780         @NotNull
781         private Integer limit;
782         private Integer timeWindow;
783         private String timeUnits;
784     }
785
786     @Getter
787     public static class MinMaxDefinition {
788         @NotNull
789         private String target;
790         private Integer min;
791         private Integer max;
792     }
793
794     @Getter
795     @NotNull
796     public static class BlacklistDefinition {
797         private List<@NotNull Object> blacklist;
798     }
799
800     @Getter
801     @NotNull
802     public static class FilterDefinition {
803         private String algorithm;
804         private List<@NotNull @Valid FilterAttribute> filters;
805     }
806
807     @Getter
808     @NotNull
809     public static class FilterAttribute {
810         private String field;
811         private String filter;
812         private String function;
813         private Boolean blacklist;
814     }
815 }