Upgrade to Tosca derivedFrom fix
[policy/xacml-pdp.git] / applications / guard / src / main / java / org / onap / policy / xacml / pdp / application / guard / LegacyGuardTranslator.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  * SPDX-License-Identifier: Apache-2.0
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.policy.xacml.pdp.application.guard;
24
25 import com.att.research.xacml.api.DataTypeException;
26 import com.att.research.xacml.api.Decision;
27 import com.att.research.xacml.api.Identifier;
28 import com.att.research.xacml.api.Request;
29 import com.att.research.xacml.api.Response;
30 import com.att.research.xacml.api.Result;
31 import com.att.research.xacml.api.XACML3;
32 import com.att.research.xacml.std.annotations.RequestParser;
33
34 import java.util.Collection;
35 import java.util.Map;
36
37 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AdviceExpressionType;
38 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AdviceExpressionsType;
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.AttributeAssignmentExpressionType;
43 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType;
44 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType;
45 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ConditionType;
46 import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType;
47 import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType;
48 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObjectFactory;
49 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType;
50 import oasis.names.tc.xacml._3_0.core.schema.wd_17.RuleType;
51 import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType;
52
53 import org.onap.policy.models.decisions.concepts.DecisionRequest;
54 import org.onap.policy.models.decisions.concepts.DecisionResponse;
55 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
56 import org.onap.policy.pdp.xacml.application.common.ToscaDictionary;
57 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException;
58 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslator;
59 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslatorUtils;
60 import org.onap.policy.pdp.xacml.application.common.operationshistory.CountRecentOperationsPip;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63
64 public class LegacyGuardTranslator implements ToscaPolicyTranslator {
65
66     private static final Logger LOGGER = LoggerFactory.getLogger(LegacyGuardTranslator.class);
67
68     private static final String FIELD_GUARD_ACTIVE_START = "guardActiveStart";
69     private static final String FIELD_GUARD_ACTIVE_END = "guardActiveEnd";
70     private static final String FIELD_TARGET = "targets";
71     private static final String DESC_DEFAULT = "Default is to PERMIT if the policy matches.";
72     private static final String ID_RULE = ":rule";
73
74     public LegacyGuardTranslator() {
75         super();
76     }
77
78     @Override
79     public PolicyType convertPolicy(ToscaPolicy toscaPolicy) throws ToscaPolicyConversionException {
80         //
81         // Policy name should be at the root
82         //
83         String policyName = toscaPolicy.getMetadata().get("policy-id");
84         //
85         // Set it as the policy ID
86         //
87         PolicyType newPolicyType = new PolicyType();
88         newPolicyType.setPolicyId(policyName);
89         //
90         // Optional description
91         //
92         newPolicyType.setDescription(toscaPolicy.getDescription());
93         //
94         // There should be a metadata section
95         //
96         this.fillMetadataSection(newPolicyType, toscaPolicy.getMetadata());
97         //
98         // Set the combining rule
99         //
100         newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_DENY_UNLESS_PERMIT.stringValue());
101         //
102         // Generate the TargetType - add true if not blacklist
103         //
104         newPolicyType.setTarget(this.generateTargetType(toscaPolicy.getProperties(),
105                 ! "onap.policies.controlloop.guard.Blacklist".equals(toscaPolicy.getType())));
106         //
107         // Now create the Permit Rule
108         //
109         RuleType rule = generatePermitRule(policyName, toscaPolicy.getType(), toscaPolicy.getProperties());
110         //
111         // Check if we were able to create the rule
112         //
113         if (rule == null) {
114             LOGGER.error("Failed to create rule");
115             return null;
116         }
117         //
118         // Add the rule to the policy
119         //
120         newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(rule);
121         //
122         // Return our new policy
123         //
124         return newPolicyType;
125     }
126
127     @Override
128     public Request convertRequest(DecisionRequest request) {
129         LOGGER.info("Converting Request {}", request);
130         try {
131             return RequestParser.parseRequest(LegacyGuardPolicyRequest.createInstance(request));
132         } catch (IllegalArgumentException | IllegalAccessException | DataTypeException e) {
133             LOGGER.error("Failed to convert DecisionRequest: {}", e);
134         }
135         //
136         // TODO throw exception
137         //
138         return null;
139     }
140
141     @Override
142     public DecisionResponse convertResponse(Response xacmlResponse) {
143         LOGGER.info("Converting Response {}", xacmlResponse);
144         DecisionResponse decisionResponse = new DecisionResponse();
145         //
146         // Iterate through all the results
147         //
148         for (Result xacmlResult : xacmlResponse.getResults()) {
149             //
150             // Check the result
151             //
152             if (xacmlResult.getDecision() == Decision.PERMIT) {
153                 //
154                 // Just simply return a Permit response
155                 //
156                 decisionResponse.setStatus(Decision.PERMIT.toString());
157             }
158             if (xacmlResult.getDecision() == Decision.DENY) {
159                 //
160                 // Just simply return a Deny response
161                 //
162                 decisionResponse.setStatus(Decision.DENY.toString());
163             }
164             if (xacmlResult.getDecision() == Decision.NOTAPPLICABLE) {
165                 //
166                 // There is no guard policy, so we return a permit
167                 //
168                 decisionResponse.setStatus(Decision.PERMIT.toString());
169             }
170         }
171
172         return decisionResponse;
173     }
174
175     /**
176      * From the TOSCA metadata section, pull in values that are needed into the XACML policy.
177      *
178      * @param policy Policy Object to store the metadata
179      * @param map The Metadata TOSCA Map
180      * @return Same Policy Object
181      * @throws ToscaPolicyConversionException If there is something missing from the metadata
182      */
183     protected PolicyType fillMetadataSection(PolicyType policy,
184             Map<String, String> map) throws ToscaPolicyConversionException {
185         if (! map.containsKey("policy-id")) {
186             throw new ToscaPolicyConversionException(policy.getPolicyId() + " missing metadata policy-id");
187         } else {
188             //
189             // Do nothing here - the XACML PolicyId is used from TOSCA Policy Name field
190             //
191         }
192         if (! map.containsKey("policy-version")) {
193             throw new ToscaPolicyConversionException(policy.getPolicyId() + " missing metadata policy-version");
194         } else {
195             //
196             // Add in the Policy Version
197             //
198             policy.setVersion(map.get("policy-version"));
199         }
200         return policy;
201     }
202
203     protected TargetType generateTargetType(Map<String, Object> properties, boolean addTargets) {
204         //
205         // Go through potential properties
206         //
207         AllOfType allOf = new AllOfType();
208         if (properties.containsKey("actor")) {
209             addMatch(allOf, properties.get("actor"), ToscaDictionary.ID_RESOURCE_GUARD_ACTOR);
210         }
211         if (properties.containsKey("recipe")) {
212             addMatch(allOf, properties.get("recipe"), ToscaDictionary.ID_RESOURCE_GUARD_RECIPE);
213         }
214         if (addTargets && properties.containsKey(FIELD_TARGET)) {
215             addMatch(allOf, properties.get(FIELD_TARGET), ToscaDictionary.ID_RESOURCE_GUARD_TARGETID);
216         }
217         if (properties.containsKey("clname")) {
218             addMatch(allOf, properties.get("clname"), ToscaDictionary.ID_RESOURCE_GUARD_CLNAME);
219         }
220         //
221         // Create target
222         //
223         TargetType target = new TargetType();
224         AnyOfType anyOf = new AnyOfType();
225         anyOf.getAllOf().add(allOf);
226         target.getAnyOf().add(anyOf);
227         return target;
228     }
229
230     private static AllOfType addMatch(AllOfType allOf, Object value, Identifier attributeId) {
231         if (value instanceof String) {
232             if (".*".equals(value.toString())) {
233                 //
234                 // There's no point to even have a match
235                 //
236                 return allOf;
237             } else {
238                 //
239                 // Exact match
240                 //
241                 MatchType match = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
242                     XACML3.ID_FUNCTION_STRING_EQUAL,
243                     value,
244                     XACML3.ID_DATATYPE_STRING,
245                     attributeId,
246                     XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
247
248                 allOf.getMatch().add(match);
249             }
250             return allOf;
251         }
252         if (value instanceof Collection) {
253             //
254             // TODO support a collection of that attribute
255             //
256         }
257         return allOf;
258     }
259
260     private static RuleType generatePermitRule(String policyName, String policyType, Map<String, Object> properties)
261             throws ToscaPolicyConversionException {
262         //
263         // Now determine which policy type we are generating
264         //
265         if ("onap.policies.controlloop.guard.FrequencyLimiter".equals(policyType)) {
266             return generateFrequencyPermit(policyName, properties);
267         } else if ("onap.policies.controlloop.guard.MinMax".equals(policyType)) {
268             return generateMinMaxPermit(policyName, properties);
269         } else if ("onap.policies.controlloop.guard.Blacklist".equals(policyType)) {
270             return generateBlacklistPermit(policyName, properties);
271         }
272         LOGGER.error("Missing policy type in the policy");
273         return null;
274     }
275
276     private static RuleType generateFrequencyPermit(String policyName, Map<String, Object> properties)
277             throws ToscaPolicyConversionException {
278         //
279         // See if its possible to generate a count
280         //
281         Integer limit = parseInteger(properties.get("limit").toString());
282         if (limit == null) {
283             LOGGER.error("Must have a limit value for frequency guard policy to be created");
284             return null;
285         }
286         //
287         // Get the properties that are common among guards
288         //
289         String timeWindow = null;
290         if (properties.containsKey("timeWindow")) {
291             Integer intTimeWindow = parseInteger(properties.get("timeWindow").toString());
292             if (intTimeWindow == null) {
293                 throw new ToscaPolicyConversionException("timeWindow is not an integer");
294             }
295             timeWindow = intTimeWindow.toString();
296         }
297         String timeUnits = null;
298         if (properties.containsKey("timeUnits")) {
299             timeUnits = properties.get("timeUnits").toString();
300         }
301         String guardActiveStart = null;
302         if (properties.containsKey(FIELD_GUARD_ACTIVE_START)) {
303             guardActiveStart = properties.get(FIELD_GUARD_ACTIVE_START).toString();
304         }
305         String guardActiveEnd = null;
306         if (properties.containsKey(FIELD_GUARD_ACTIVE_END)) {
307             guardActiveEnd = properties.get(FIELD_GUARD_ACTIVE_END).toString();
308         }
309         //
310         // Generate the time in range
311         //
312         final ApplyType timeRange = generateTimeInRange(guardActiveStart, guardActiveEnd);
313         //
314         // Generate a count
315         //
316         final ApplyType countCheck = generateCountCheck(limit, timeWindow, timeUnits);
317         //
318         // Now combine into an And
319         //
320         ApplyType applyAnd = new ApplyType();
321         applyAnd.setDescription("return true if time range and count checks are true.");
322         applyAnd.setFunctionId(XACML3.ID_FUNCTION_AND.stringValue());
323         applyAnd.getExpression().add(new ObjectFactory().createApply(timeRange));
324         applyAnd.getExpression().add(new ObjectFactory().createApply(countCheck));
325
326         //
327         // Create our condition
328         //
329         final ConditionType condition = new ConditionType();
330         condition.setExpression(new ObjectFactory().createApply(applyAnd));
331
332         //
333         // Now we can create our rule
334         //
335         RuleType permit = new RuleType();
336         permit.setDescription(DESC_DEFAULT);
337         permit.setRuleId(policyName + ID_RULE);
338         permit.setEffect(EffectType.PERMIT);
339         permit.setTarget(new TargetType());
340         //
341         // Add the condition
342         //
343         permit.setCondition(condition);
344         //
345         // TODO Add the advice - Is the request id needed to be returned?
346         //
347         // permit . setAdviceExpressions (adviceExpressions)
348         //
349         // Done
350         //
351         return permit;
352     }
353
354     private static RuleType generateMinMaxPermit(String policyName, Map<String, Object> properties) {
355         //
356         // Get the properties that are common among guards
357         //
358         String guardActiveStart = null;
359         if (properties.containsKey(FIELD_GUARD_ACTIVE_START)) {
360             guardActiveStart = properties.get(FIELD_GUARD_ACTIVE_START).toString();
361         }
362         String guardActiveEnd = null;
363         if (properties.containsKey(FIELD_GUARD_ACTIVE_END)) {
364             guardActiveEnd = properties.get(FIELD_GUARD_ACTIVE_END).toString();
365         }
366         //
367         // Generate the time in range
368         //
369         final ApplyType timeRange = generateTimeInRange(guardActiveStart, guardActiveEnd);
370         //
371         // See if its possible to generate a count
372         //
373         Integer min = null;
374         if (properties.containsKey("min")) {
375             min = parseInteger(properties.get("min").toString());
376         }
377         Integer max = null;
378         if (properties.containsKey("max")) {
379             max = parseInteger(properties.get("max").toString());
380         }
381         final ApplyType minApply = generateMinCheck(min);
382         final ApplyType maxApply = generateMaxCheck(max);
383         //
384         // Make sure we have at least something to check here,
385         // otherwise there really is no point to this policy.
386         //
387         if (timeRange == null && minApply == null && maxApply == null) {
388             return null;
389         }
390         //
391         // Create our rule
392         //
393         RuleType permit = new RuleType();
394         permit.setDescription(DESC_DEFAULT);
395         permit.setRuleId(policyName + ID_RULE);
396         permit.setEffect(EffectType.PERMIT);
397         permit.setTarget(new TargetType());
398         //
399         // Create the condition
400         //
401         permit.setCondition(createCondition(timeRange, minApply, maxApply));
402         //
403         // TODO Add the advice - Is the request id needed to be returned?
404         //
405         // permit . setAdviceExpressions (adviceExpressions)
406         //
407         // Done
408         //
409         return permit;
410     }
411
412     private static ConditionType createCondition(ApplyType timeRange, ApplyType minApply, ApplyType maxApply) {
413         final ConditionType condition = new ConditionType();
414         //
415         // Check if we have all the fields (this can be a little
416         // ugly) but the ultimate goal is to simplify the policy
417         // condition to only check for necessary attributes.
418         //
419         ObjectFactory factory = new ObjectFactory();
420         if (timeRange != null && minApply != null && maxApply != null) {
421             //
422             // All 3 must apply
423             //
424             ApplyType applyAnd = new ApplyType();
425             applyAnd.setDescription("return true if all the apply's are true.");
426             applyAnd.setFunctionId(XACML3.ID_FUNCTION_AND.stringValue());
427             applyAnd.getExpression().add(factory.createApply(timeRange));
428             applyAnd.getExpression().add(factory.createApply(minApply));
429             applyAnd.getExpression().add(factory.createApply(maxApply));
430             //
431             // Add into the condition
432             //
433             condition.setExpression(factory.createApply(applyAnd));
434
435             return condition;
436         }
437         //
438         // At least one of these applies is null. We need at least
439         // two to require the And apply. Otherwise there is no need
440         // for an outer And apply as the single condition can work
441         // on its own.
442         //
443         if (timeRange != null && minApply == null && maxApply == null) {
444             //
445             // Only the time range check is necessary
446             //
447             condition.setExpression(factory.createApply(timeRange));
448         } else if (timeRange == null && minApply != null && maxApply == null) {
449             //
450             // Only the min check is necessary
451             //
452             condition.setExpression(factory.createApply(minApply));
453         } else if (timeRange == null && minApply == null) {
454             //
455             // Only the max check is necessary
456             //
457             condition.setExpression(factory.createApply(maxApply));
458         } else {
459             //
460             // Ok we will need an outer And and have at least the
461             // time range and either min or max check
462             //
463             ApplyType applyAnd = new ApplyType();
464             applyAnd.setDescription("return true if all the apply's are true.");
465             applyAnd.setFunctionId(XACML3.ID_FUNCTION_AND.stringValue());
466             if (timeRange != null) {
467                 applyAnd.getExpression().add(factory.createApply(timeRange));
468             }
469             if (minApply != null) {
470                 applyAnd.getExpression().add(factory.createApply(minApply));
471             }
472             if (maxApply != null) {
473                 applyAnd.getExpression().add(factory.createApply(maxApply));
474             }
475             //
476             // Add into the condition
477             //
478             condition.setExpression(factory.createApply(applyAnd));
479         }
480         return condition;
481     }
482
483     private static RuleType generateBlacklistPermit(String policyName, Map<String, Object> properties) {
484         //
485         // Generate target
486         //
487         if (! properties.containsKey(FIELD_TARGET)) {
488             LOGGER.error("Missing target for blacklist policy");
489             return null;
490         }
491         final ApplyType targetApply = generateTargetApply(properties.get(FIELD_TARGET));
492         //
493         // Get the properties that are common among guards
494         //
495         String guardActiveStart = null;
496         if (properties.containsKey(FIELD_GUARD_ACTIVE_START)) {
497             guardActiveStart = properties.get(FIELD_GUARD_ACTIVE_START).toString();
498         }
499         String guardActiveEnd = null;
500         if (properties.containsKey(FIELD_GUARD_ACTIVE_END)) {
501             guardActiveEnd = properties.get(FIELD_GUARD_ACTIVE_END).toString();
502         }
503         //
504         // Generate the time in range
505         //
506         final ApplyType timeRange = generateTimeInRange(guardActiveStart, guardActiveEnd);
507         //
508         // Create our rule
509         //
510         RuleType permit = new RuleType();
511         permit.setDescription(DESC_DEFAULT);
512         permit.setRuleId(policyName + ID_RULE);
513         permit.setEffect(EffectType.PERMIT);
514         permit.setTarget(new TargetType());
515         //
516         // Create our condition
517         //
518         ObjectFactory factory = new ObjectFactory();
519         ApplyType innerApply;
520         if (timeRange != null) {
521             ApplyType applyAnd = new ApplyType();
522             applyAnd.setDescription("Combine the timeRange with target to create AND");
523             applyAnd.setFunctionId(XACML3.ID_FUNCTION_AND.stringValue());
524             applyAnd.getExpression().add(factory.createApply(timeRange));
525             applyAnd.getExpression().add(factory.createApply(targetApply));
526             //
527             // Now we need to NOT this so the permit happens
528             //
529             ApplyType applyNot = new ApplyType();
530             applyNot.setDescription("This should be false for a  permit.");
531             applyNot.setFunctionId(XACML3.ID_FUNCTION_NOT.stringValue());
532             applyNot.getExpression().add(factory.createApply(applyAnd));
533             innerApply = applyNot;
534         } else {
535             //
536             // Just the target is needed
537             //
538             ApplyType applyNot = new ApplyType();
539             applyNot.setDescription("This should be false for a  permit.");
540             applyNot.setFunctionId(XACML3.ID_FUNCTION_NOT.stringValue());
541             applyNot.getExpression().add(factory.createApply(targetApply));
542             innerApply = applyNot;
543         }
544         //
545         // Create our condition
546         //
547         final ConditionType condition = new ConditionType();
548         //
549         // Add into the condition
550         //
551         condition.setExpression(factory.createApply(innerApply));
552         //
553         // Add the condition
554         //
555         permit.setCondition(condition);
556         return permit;
557     }
558
559     private static ApplyType generateTimeInRange(String start, String end) {
560         if (start == null || end == null) {
561             LOGGER.warn("Missing time range start {} end {}", start, end);
562             return null;
563         }
564         if (start.isEmpty() || end.isEmpty()) {
565             LOGGER.warn("Empty time range start {} end {}", start, end);
566             return null;
567         }
568
569         AttributeDesignatorType designator = new AttributeDesignatorType();
570         designator.setAttributeId(XACML3.ID_ENVIRONMENT_CURRENT_TIME.stringValue());
571         designator.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_ENVIRONMENT.stringValue());
572         designator.setDataType(XACML3.ID_DATATYPE_TIME.stringValue());
573
574         AttributeValueType valueStart = new AttributeValueType();
575         valueStart.setDataType(XACML3.ID_DATATYPE_TIME.stringValue());
576         valueStart.getContent().add(start);
577
578         AttributeValueType valueEnd = new AttributeValueType();
579         valueEnd.setDataType(XACML3.ID_DATATYPE_TIME.stringValue());
580         valueEnd.getContent().add(end);
581
582         ObjectFactory factory = new ObjectFactory();
583
584         ApplyType applyOneAndOnly = new ApplyType();
585         applyOneAndOnly.setDescription("Unbag the current time");
586         applyOneAndOnly.setFunctionId(XACML3.ID_FUNCTION_TIME_ONE_AND_ONLY.stringValue());
587         applyOneAndOnly.getExpression().add(factory.createAttributeDesignator(designator));
588
589         ApplyType applyTimeInRange = new ApplyType();
590         applyTimeInRange.setDescription("return true if current time is in range.");
591         applyTimeInRange.setFunctionId(XACML3.ID_FUNCTION_TIME_IN_RANGE.stringValue());
592         applyTimeInRange.getExpression().add(factory.createApply(applyOneAndOnly));
593         applyTimeInRange.getExpression().add(factory.createAttributeValue(valueStart));
594         applyTimeInRange.getExpression().add(factory.createAttributeValue(valueEnd));
595
596         return applyTimeInRange;
597     }
598
599     private static ApplyType generateCountCheck(Integer limit, String timeWindow, String timeUnits) {
600         AttributeDesignatorType designator = new AttributeDesignatorType();
601         designator.setAttributeId(ToscaDictionary.ID_RESOURCE_GUARD_OPERATIONCOUNT.stringValue());
602         designator.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE.stringValue());
603         designator.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
604         //
605         // Setup issuer
606         //
607         String issuer = ToscaDictionary.GUARD_ISSUER_PREFIX
608             + CountRecentOperationsPip.ISSUER_NAME
609             + ":tw:" + timeWindow + ":" + timeUnits;
610         designator.setIssuer(issuer);
611
612         AttributeValueType valueLimit = new AttributeValueType();
613         valueLimit.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
614         //
615         // Yes really use toString(), the marshaller will
616         // throw an exception if this is an integer object
617         // and not a string.
618         //
619         valueLimit.getContent().add(limit.toString());
620
621         ObjectFactory factory = new ObjectFactory();
622
623         ApplyType applyOneAndOnly = new ApplyType();
624         applyOneAndOnly.setDescription("Unbag the limit");
625         applyOneAndOnly.setFunctionId(XACML3.ID_FUNCTION_INTEGER_ONE_AND_ONLY.stringValue());
626         applyOneAndOnly.getExpression().add(factory.createAttributeDesignator(designator));
627
628         ApplyType applyLessThan = new ApplyType();
629         applyLessThan.setDescription("return true if current count is less than.");
630         applyLessThan.setFunctionId(XACML3.ID_FUNCTION_INTEGER_LESS_THAN.stringValue());
631         applyLessThan.getExpression().add(factory.createApply(applyOneAndOnly));
632         applyLessThan.getExpression().add(factory.createAttributeValue(valueLimit));
633
634         return applyLessThan;
635     }
636
637     private static ApplyType generateMinCheck(Integer min) {
638         if (min == null) {
639             return null;
640         }
641         AttributeDesignatorType designator = new AttributeDesignatorType();
642         designator.setAttributeId(ToscaDictionary.ID_RESOURCE_GUARD_VFCOUNT.stringValue());
643         designator.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE.stringValue());
644         designator.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
645         //
646         //
647         //
648         AttributeValueType valueLimit = new AttributeValueType();
649         valueLimit.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
650         //
651         // Yes really use toString(), the marshaller will
652         // throw an exception if this is an integer object
653         // and not a string.
654         //
655         valueLimit.getContent().add(min.toString());
656         ObjectFactory factory = new ObjectFactory();
657
658         ApplyType applyOneAndOnly = new ApplyType();
659         applyOneAndOnly.setDescription("Unbag the min");
660         applyOneAndOnly.setFunctionId(XACML3.ID_FUNCTION_INTEGER_ONE_AND_ONLY.stringValue());
661         applyOneAndOnly.getExpression().add(factory.createAttributeDesignator(designator));
662
663         ApplyType applyGreaterThanEqual = new ApplyType();
664         applyGreaterThanEqual.setDescription("return true if current count is greater than or equal.");
665         applyGreaterThanEqual.setFunctionId(XACML3.ID_FUNCTION_INTEGER_GREATER_THAN_OR_EQUAL.stringValue());
666         applyGreaterThanEqual.getExpression().add(factory.createApply(applyOneAndOnly));
667         applyGreaterThanEqual.getExpression().add(factory.createAttributeValue(valueLimit));
668
669         return applyGreaterThanEqual;
670     }
671
672     private static ApplyType generateMaxCheck(Integer max) {
673         if (max == null) {
674             return null;
675         }
676         AttributeDesignatorType designator = new AttributeDesignatorType();
677         designator.setAttributeId(ToscaDictionary.ID_RESOURCE_GUARD_VFCOUNT.stringValue());
678         designator.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE.stringValue());
679         designator.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
680         //
681         //
682         //
683         AttributeValueType valueLimit = new AttributeValueType();
684         valueLimit.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
685         //
686         // Yes really use toString(), the marshaller will
687         // throw an exception if this is an integer object
688         // and not a string.
689         //
690         valueLimit.getContent().add(max.toString());
691         ObjectFactory factory = new ObjectFactory();
692
693         ApplyType applyOneAndOnly = new ApplyType();
694         applyOneAndOnly.setDescription("Unbag the min");
695         applyOneAndOnly.setFunctionId(XACML3.ID_FUNCTION_INTEGER_ONE_AND_ONLY.stringValue());
696         applyOneAndOnly.getExpression().add(factory.createAttributeDesignator(designator));
697
698         ApplyType applyLessThanEqual = new ApplyType();
699         applyLessThanEqual.setDescription("return true if current count is less than or equal.");
700         applyLessThanEqual.setFunctionId(XACML3.ID_FUNCTION_INTEGER_LESS_THAN_OR_EQUAL.stringValue());
701         applyLessThanEqual.getExpression().add(factory.createApply(applyOneAndOnly));
702         applyLessThanEqual.getExpression().add(factory.createAttributeValue(valueLimit));
703
704         return applyLessThanEqual;
705     }
706
707     @SuppressWarnings("unchecked")
708     private static ApplyType generateTargetApply(Object targetObject) {
709         ObjectFactory factory = new ObjectFactory();
710         //
711         // Create a bag of values
712         //
713         ApplyType applyStringBag = new ApplyType();
714         applyStringBag.setDescription("Bag the target values");
715         applyStringBag.setFunctionId(XACML3.ID_FUNCTION_STRING_BAG.stringValue());
716         if (targetObject instanceof Collection) {
717             for (Object target : ((Collection<Object>) targetObject)) {
718                 if (! (target instanceof String)) {
719                     LOGGER.error("Collection of unsupported objects {}", target.getClass());
720                     return null;
721                 }
722                 AttributeValueType value = new AttributeValueType();
723                 value.setDataType(XACML3.ID_DATATYPE_STRING.stringValue());
724                 value.getContent().add(target.toString());
725                 applyStringBag.getExpression().add(factory.createAttributeValue(value));
726             }
727         } else if (targetObject instanceof String) {
728             AttributeValueType value = new AttributeValueType();
729             value.setDataType(XACML3.ID_DATATYPE_STRING.stringValue());
730             value.getContent().add(targetObject.toString());
731             applyStringBag.getExpression().add(factory.createAttributeValue(value));
732         } else {
733             LOGGER.warn("Unsupported object for target {}", targetObject.getClass());
734             return null;
735         }
736         //
737         // Create our designator
738         //
739         AttributeDesignatorType designator = new AttributeDesignatorType();
740         designator.setAttributeId(ToscaDictionary.ID_RESOURCE_GUARD_TARGETID.stringValue());
741         designator.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE.stringValue());
742         designator.setDataType(XACML3.ID_DATATYPE_STRING.stringValue());
743         //
744         // Create apply for our AnyOf
745         //
746         ApplyType applyAnyOf = new ApplyType();
747         applyAnyOf.setDescription("Find designator as anyof the possible values");
748         applyAnyOf.setFunctionId(XACML3.ID_FUNCTION_ANY_OF.stringValue());
749         applyAnyOf.getExpression().add(factory.createAttributeDesignator(designator));
750         applyAnyOf.getExpression().add(factory.createApply(applyStringBag));
751         return applyAnyOf;
752     }
753
754     private static Integer parseInteger(String strInteger) {
755         Integer theInt = null;
756         try {
757             theInt = Integer.parseInt(strInteger);
758         } catch (NumberFormatException e) {
759             LOGGER.warn("Expecting an integer", e);
760             try {
761                 Double dblLimit = Double.parseDouble(strInteger);
762                 theInt = dblLimit.intValue();
763             } catch (NumberFormatException e1) {
764                 LOGGER.error("Failed to parse expected integer as a double", e);
765                 return null;
766             }
767         }
768         return theInt;
769     }
770
771     @SuppressWarnings("unused")
772     private static AdviceExpressionsType generateRequestIdAdvice() {
773         AdviceExpressionType adviceExpression = new AdviceExpressionType();
774         adviceExpression.setAppliesTo(EffectType.PERMIT);
775         adviceExpression.setAdviceId(ToscaDictionary.ID_ADVICE_GUARD.stringValue());
776
777         AttributeDesignatorType designator = new AttributeDesignatorType();
778         designator.setAttributeId(ToscaDictionary.ID_SUBJECT_GUARD_REQUESTID.stringValue());
779         designator.setCategory(XACML3.ID_SUBJECT_CATEGORY_ACCESS_SUBJECT.stringValue());
780         designator.setDataType(XACML3.ID_DATATYPE_STRING.stringValue());
781
782         AttributeAssignmentExpressionType assignment = new AttributeAssignmentExpressionType();
783         assignment.setAttributeId(ToscaDictionary.ID_ADVICE_GUARD_REQUESTID.stringValue());
784         assignment.setCategory(XACML3.ID_SUBJECT_CATEGORY_ACCESS_SUBJECT.stringValue());
785         assignment.setExpression(new ObjectFactory().createAttributeDesignator(designator));
786
787         adviceExpression.getAttributeAssignmentExpression().add(assignment);
788
789         AdviceExpressionsType adviceExpressions = new AdviceExpressionsType();
790         adviceExpressions.getAdviceExpression().add(adviceExpression);
791
792         return adviceExpressions;
793     }
794 }