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