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