From 3b755ec2e3d9776980236db6ba754ae6b7cc2402 Mon Sep 17 00:00:00 2001 From: Pamela Dragosh Date: Wed, 18 Mar 2020 07:50:22 -0400 Subject: Re-factor matchable to reduce complexity This solution is much cleaner than what is in StdMatchableTranslator. Over 90% code coverage on it - utilizes a callback to retrieve DataType and PolicyTypes. Support for missing timestamp TOSCA type. Also can do a better job differentiating between a property contained in the policy vs a schema. Changed StdMatchableTranslator to utilize these classes. And removed the old spaghetti. Added some JUnit coverage for ToscaPolicyTranslatorUtils. Removed duplicate code in the XACML Native Exception classes. Issue-ID: POLICY-2242 Change-Id: I18f898d9e65f6da28e3b27517d40f8d389de18a0 Signed-off-by: Pamela Dragosh --- .../common/ToscaPolicyTranslatorUtils.java | 48 ++ .../common/matchable/MatchableCallback.java | 34 ++ .../common/matchable/MatchablePolicyType.java | 338 ++++++++++++ .../common/matchable/MatchableProperty.java | 36 ++ .../common/matchable/MatchablePropertyType.java | 34 ++ .../matchable/MatchablePropertyTypeBase.java | 42 ++ .../matchable/MatchablePropertyTypeBoolean.java | 61 +++ .../matchable/MatchablePropertyTypeFloat.java | 68 +++ .../matchable/MatchablePropertyTypeInteger.java | 64 +++ .../matchable/MatchablePropertyTypeList.java | 73 +++ .../common/matchable/MatchablePropertyTypeMap.java | 80 +++ .../matchable/MatchablePropertyTypeString.java | 60 +++ .../matchable/MatchablePropertyTypeTimestamp.java | 64 +++ .../common/std/StdMatchableTranslator.java | 571 ++++----------------- .../common/ToscaPolicyTranslatorUtilsTest.java | 25 +- .../common/matchable/MatchablePolicyTypeTest.java | 300 +++++++++++ .../common/std/StdMatchableTranslatorTest.java | 2 +- .../matchable/onap.policies.Test-1.0.0.yaml | 96 +++- .../matchable/test.policies.input.tosca.yaml | 38 +- 19 files changed, 1550 insertions(+), 484 deletions(-) create mode 100644 applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchableCallback.java create mode 100644 applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePolicyType.java create mode 100644 applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchableProperty.java create mode 100644 applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyType.java create mode 100644 applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeBase.java create mode 100644 applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeBoolean.java create mode 100644 applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeFloat.java create mode 100644 applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeInteger.java create mode 100644 applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeList.java create mode 100644 applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeMap.java create mode 100644 applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeString.java create mode 100644 applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeTimestamp.java create mode 100644 applications/common/src/test/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePolicyTypeTest.java (limited to 'applications/common') diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/ToscaPolicyTranslatorUtils.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/ToscaPolicyTranslatorUtils.java index f1d3d5e3..796499d4 100644 --- a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/ToscaPolicyTranslatorUtils.java +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/ToscaPolicyTranslatorUtils.java @@ -25,11 +25,13 @@ package org.onap.policy.pdp.xacml.application.common; import com.att.research.xacml.api.Identifier; import com.att.research.xacml.api.XACML3; import oasis.names.tc.xacml._3_0.core.schema.wd_17.AllOfType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.ApplyType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObjectFactory; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType; import org.apache.commons.lang3.StringUtils; /** @@ -166,4 +168,50 @@ public final class ToscaPolicyTranslatorUtils { } return theInt; } + + /** + * For a given MatchType or AnyOfType, builds it and appends it into the + * AnyOfType. + * + * @param anyOf AnyOfType - will create if null + * @param type MatchType or AnyOfType + * @return returns the given anyOf or new AnyTypeOf if null + */ + public static AnyOfType buildAndAppendAllof(AnyOfType anyOf, Object type) { + if (type instanceof MatchType) { + AllOfType allOf = new AllOfType(); + allOf.getMatch().add((MatchType) type); + if (anyOf == null) { + anyOf = new AnyOfType(); + } + anyOf.getAllOf().add(allOf); + } else if (type instanceof AllOfType) { + if (anyOf == null) { + anyOf = new AnyOfType(); + } + anyOf.getAllOf().add((AllOfType) type); + } + + return anyOf; + } + + /** + * buildAndAppendTarget - adds in the potential object into TargetType. + * + * @param target TargetType - must exist + * @param object AnyOfType or MatchType + * @return TargetType + */ + public static TargetType buildAndAppendTarget(TargetType target, Object object) { + if (object instanceof AnyOfType) { + target.getAnyOf().add((AnyOfType) object); + } else if (object instanceof MatchType) { + AllOfType allOf = new AllOfType(); + allOf.getMatch().add((MatchType) object); + AnyOfType anyOf = new AnyOfType(); + anyOf.getAllOf().add(allOf); + target.getAnyOf().add(anyOf); + } + return target; + } } diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchableCallback.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchableCallback.java new file mode 100644 index 00000000..a076929d --- /dev/null +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchableCallback.java @@ -0,0 +1,34 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.pdp.xacml.application.common.matchable; + +import org.onap.policy.models.tosca.authorative.concepts.ToscaDataType; +import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyType; + +public interface MatchableCallback { + + ToscaPolicyType retrievePolicyType(String derivedFrom); + + ToscaDataType retrieveDataType(String datatype); + +} diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePolicyType.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePolicyType.java new file mode 100644 index 00000000..3bbc6ea5 --- /dev/null +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePolicyType.java @@ -0,0 +1,338 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.pdp.xacml.application.common.matchable; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.function.Function; +import lombok.Getter; +import lombok.NonNull; +import org.onap.policy.models.tosca.authorative.concepts.ToscaDataType; +import org.onap.policy.models.tosca.authorative.concepts.ToscaEntrySchema; +import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifier; +import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyType; +import org.onap.policy.models.tosca.authorative.concepts.ToscaProperty; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Getter +public class MatchablePolicyType { + private static final Logger LOGGER = LoggerFactory.getLogger(MatchablePolicyType.class); + + public static final String TOSCA_PRIMITIVE_STRING = "string"; + public static final String TOSCA_PRIMITIVE_INTEGER = "integer"; + public static final String TOSCA_PRIMITIVE_FLOAT = "float"; + public static final String TOSCA_PRIMITIVE_BOOLEAN = "boolean"; + public static final String TOSCA_PRIMITIVE_TIMESTAMP = "timestamp"; + public static final String TOSCA_TYPE_LIST = "list"; + public static final String TOSCA_TYPE_MAP = "map"; + + private static final Map>> + mapPrimitivesProperty = Map.of( + TOSCA_PRIMITIVE_STRING, MatchablePropertyTypeString::new, + TOSCA_PRIMITIVE_INTEGER, MatchablePropertyTypeInteger::new, + TOSCA_PRIMITIVE_FLOAT, MatchablePropertyTypeFloat::new, + TOSCA_PRIMITIVE_BOOLEAN, MatchablePropertyTypeBoolean::new, + TOSCA_PRIMITIVE_TIMESTAMP, MatchablePropertyTypeTimestamp::new + ); + + private static final Map>> + mapPrimitivesSchema = Map.of( + TOSCA_PRIMITIVE_STRING, MatchablePropertyTypeString::new, + TOSCA_PRIMITIVE_INTEGER, MatchablePropertyTypeInteger::new, + TOSCA_PRIMITIVE_FLOAT, MatchablePropertyTypeFloat::new, + TOSCA_PRIMITIVE_BOOLEAN, MatchablePropertyTypeBoolean::new, + TOSCA_PRIMITIVE_TIMESTAMP, MatchablePropertyTypeTimestamp::new + ); + + ToscaPolicyIdentifier policyId; + Map matchables = new HashMap<>(); + + public MatchablePolicyType(@NonNull ToscaPolicyType policyType, @NonNull MatchableCallback callback) { + this.policyId = new ToscaPolicyIdentifier(policyType.getName(), policyType.getVersion()); + scanPolicyType(policyType, callback); + } + + public MatchableProperty get(@NonNull String property) { + return this.matchables.get(property); + } + + protected void scanPolicyType(@NonNull ToscaPolicyType inPolicyType, @NonNull MatchableCallback callback) { + ToscaPolicyType policyType = inPolicyType; + while (policyType != null) { + LOGGER.info("Scanning PolicyType {}:{}", policyType.getName(), policyType.getVersion()); + // + // Scan for all the matchable properties + // + scanProperties(policyType.getProperties(), matchables, callback); + // + // Does this PolicyType derive from another Policy Type? + // + if ("tosca.policies.Root".equals(policyType.getDerivedFrom())) { + // + // No we are finished + // + LOGGER.info("Found root - done scanning"); + break; + } + // + // Move to the parent policy and scan it for matchables. + // + policyType = callback.retrievePolicyType(policyType.getDerivedFrom()); + } + } + + /** + * Scans properties for matchables. + * + * @param properties Map of ToscaProperties to scan + * @param matchables Found matchables will be put into this list + * @param callback Callback routine for finding Policy Types and Data Types + */ + public static void scanProperties(Map properties, Map matchables, + MatchableCallback callback) { + for (Entry entrySet : properties.entrySet()) { + final String property = entrySet.getKey(); + final ToscaProperty toscaProperty = entrySet.getValue(); + // + // Most likely case is its a primitive + // + if (isPrimitive(toscaProperty.getType())) { + MatchableProperty primitiveProperty = handlePrimitive(property, toscaProperty); + if (primitiveProperty != null) { + matchables.put(property, primitiveProperty); + } + } else if (TOSCA_TYPE_LIST.equals(toscaProperty.getType())) { + MatchableProperty listProperty = handleList(property, toscaProperty, matchables, callback); + if (listProperty != null) { + matchables.put(property, listProperty); + } + } else if (TOSCA_TYPE_MAP.equals(toscaProperty.getType())) { + MatchableProperty mapProperty = handleMap(property, toscaProperty, matchables, callback); + if (mapProperty != null) { + matchables.put(property, mapProperty); + } + } else { + scanDatatype(toscaProperty, matchables, callback); + } + } + } + + /** + * handlePrimitive - handles a primitive type only if its matchable. + * + * @param property String containing property name + * @param toscaProperty ToscaProperty object + * @return MatchableProperty object + */ + public static MatchableProperty handlePrimitive(String property, ToscaProperty toscaProperty) { + if (!isMatchable(toscaProperty)) { + return null; + } + Function> function = + mapPrimitivesProperty.get(toscaProperty.getType()); + if (function != null) { + return new MatchableProperty(property, function.apply(toscaProperty)); + } + throw new IllegalArgumentException("Not a primitive " + toscaProperty.getType()); + } + + /** + * handlePrimitive from a schema. Note that a ToscaEntrySchema does NOT have a metadata section + * so you cannot check if its matchable. + * + * @param property String containing property name + * @param toscaSchema ToscaSchema + * @return MatchableProperty object + */ + public static MatchableProperty handlePrimitive(String property, ToscaEntrySchema toscaSchema) { + Function> function = + mapPrimitivesSchema.get(toscaSchema.getType()); + if (function != null) { + return new MatchableProperty(property, function.apply(toscaSchema)); // compilation err wants ToscaProperty + } + throw new IllegalArgumentException("Not a primitive " + toscaSchema.getType()); + } + + /** + * handleList - iterates a list looking for matchables. + * + * @param property String containing property name + * @param toscaProperty ToscaProperty object + * @param matchables list of matchables to add to + * @param callback MatchableCallback to retrieve Data Types + * @return MatchableProperty object + */ + public static MatchableProperty handleList(@NonNull String property, @NonNull ToscaProperty toscaProperty, + Map matchables, @NonNull MatchableCallback callback) { + if (! TOSCA_TYPE_LIST.equals(toscaProperty.getType())) { + throw new IllegalArgumentException("must be a list"); + } + // + // Is it a simple or complex list? + // + if (isPrimitive(toscaProperty.getEntrySchema().getType())) { + // + // Only if the list is matchable + // + if (!isMatchable(toscaProperty)) { + return null; + } + // + // Simple list of primitives + // + return new MatchableProperty(property, new MatchablePropertyTypeList(toscaProperty)); + } + // + // Scan the datatype for matchables + // + scanDatatype(toscaProperty.getEntrySchema(), matchables, callback); + // + // Return nothing - scanning the datatype should find the matchables + // + return null; + } + + /** + * handleMap - iterates a map looking for matchables. + * + * @param property String containing property name + * @param toscaProperty ToscaProperty object + * @param matchables list of matchables to add to + * @param callback MatchableCallback to retrieve Data Types + * @return MatchableProperty object + */ + public static MatchableProperty handleMap(@NonNull String property, @NonNull ToscaProperty toscaProperty, + Map matchables, @NonNull MatchableCallback callback) { + if (! TOSCA_TYPE_MAP.equals(toscaProperty.getType())) { + throw new IllegalArgumentException("must be a map"); + } + // + // Is it a simple or complex map? + // + if (isPrimitive(toscaProperty.getEntrySchema().getType())) { + // + // Only if the map is matchable + // + if (!isMatchable(toscaProperty)) { + return null; + } + // + // Simple map of primitives + // + return new MatchableProperty(property, new MatchablePropertyTypeMap(toscaProperty)); + } + // + // Scan the datatype for matchables + // + scanDatatype(toscaProperty.getEntrySchema(), matchables, callback); + // + // Return nothing - scanning the datatype should find the matchables + // + return null; + } + + /** + * scanDatatype - scans a datatypes schema properties for matchables. + * + * @param toscaProperty ToscaProperty object + * @param matchables list of matchables to add to + * @param callback MatchableCallback to retrieve Data Types + */ + public static void scanDatatype(@NonNull ToscaProperty toscaProperty, Map matchables, + @NonNull MatchableCallback callback) { + // + // Don't check for matchable property, as that does not make sense to support right now. But we need to + // scan the datatype for matchable properties. + // + LOGGER.info("Retrieving datatype {}", toscaProperty.getType()); + // + // Try to retrieve the datatype + // + ToscaDataType dataType = callback.retrieveDataType(toscaProperty.getType()); + if (dataType == null) { + LOGGER.error("Failed to retrieve datatype {}", toscaProperty.getType()); + return; + } + // + // Now scan the properties in the datatype + // + scanProperties(dataType.getProperties(), matchables, callback); + } + + /** + * scanDatatypes - scans a datatype schema for matchables. + * + * @param toscaSchema ToscaEntrySchema + * @param matchables list of matchables to add to + * @param callback MatchableCallback object for retrieving other data types + */ + public static void scanDatatype(@NonNull ToscaEntrySchema toscaSchema, Map matchables, + @NonNull MatchableCallback callback) { + // + // Don't check for matchable property, as that does not make sense to support right now. But we need to + // scan the datatype for matchable properties. + // + LOGGER.info("Retrieving datatype {}", toscaSchema.getType()); + // + // Try to retrieve the datatype + // + ToscaDataType dataType = callback.retrieveDataType(toscaSchema.getType()); + if (dataType == null) { + LOGGER.error("Failed to retrieve datatype {}", toscaSchema.getType()); + return; + } + // + // Now scan the properties in the datatype + // + scanProperties(dataType.getProperties(), matchables, callback); + } + + /** + * isPrimitive. + * + * @param type String containing type + * @return true if it is a primitive + */ + public static boolean isPrimitive(@NonNull String type) { + return mapPrimitivesProperty.containsKey(type); + } + + /** + * isMatchable - scans metadata for matchable field set to true. + * + * @param property ToscaProperty object + * @return true if matchable + */ + public static boolean isMatchable(@NonNull ToscaProperty property) { + if (property.getMetadata() == null) { + return false; + } + String isMatchable = property.getMetadata().get("matchable"); + if (isMatchable == null) { + return false; + } + return "true".equalsIgnoreCase(isMatchable); + } +} diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchableProperty.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchableProperty.java new file mode 100644 index 00000000..9b5de5c8 --- /dev/null +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchableProperty.java @@ -0,0 +1,36 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.pdp.xacml.application.common.matchable; + +import lombok.Data; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +@Data +@RequiredArgsConstructor +public class MatchableProperty { + @NonNull + String property; + @NonNull + MatchablePropertyType type; +} diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyType.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyType.java new file mode 100644 index 00000000..8f910c4e --- /dev/null +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyType.java @@ -0,0 +1,34 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.pdp.xacml.application.common.matchable; + +import com.att.research.xacml.api.Identifier; +import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException; + +public interface MatchablePropertyType { + + public T validate(Object value) throws ToscaPolicyConversionException; + + public Object generate(Object value, Identifier attributeId) throws ToscaPolicyConversionException; + +} diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeBase.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeBase.java new file mode 100644 index 00000000..f93f8a3d --- /dev/null +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeBase.java @@ -0,0 +1,42 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.pdp.xacml.application.common.matchable; + +import lombok.Getter; +import org.onap.policy.models.tosca.authorative.concepts.ToscaEntrySchema; +import org.onap.policy.models.tosca.authorative.concepts.ToscaProperty; + +@Getter +public abstract class MatchablePropertyTypeBase implements MatchablePropertyType { + private ToscaProperty toscaProperty; + private ToscaEntrySchema toscaSchema; + + public MatchablePropertyTypeBase(ToscaProperty inProperty) { + this.toscaProperty = inProperty; + } + + public MatchablePropertyTypeBase(ToscaEntrySchema inSchema) { + this.toscaSchema = inSchema; + } + +} diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeBoolean.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeBoolean.java new file mode 100644 index 00000000..d3b16c21 --- /dev/null +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeBoolean.java @@ -0,0 +1,61 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.pdp.xacml.application.common.matchable; + +import com.att.research.xacml.api.Identifier; +import com.att.research.xacml.api.XACML3; +import org.onap.policy.models.tosca.authorative.concepts.ToscaEntrySchema; +import org.onap.policy.models.tosca.authorative.concepts.ToscaProperty; +import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException; +import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslatorUtils; + +public class MatchablePropertyTypeBoolean extends MatchablePropertyTypeBase { + + public MatchablePropertyTypeBoolean(ToscaProperty inProperty) { + super(inProperty); + } + + public MatchablePropertyTypeBoolean(ToscaEntrySchema toscaSchema) { + super(toscaSchema); + } + + @Override + public Boolean validate(Object value) throws ToscaPolicyConversionException { + if (value instanceof Boolean) { + return (Boolean) value; + } + return Boolean.parseBoolean(value.toString()); + } + + @Override + public Object generate(Object value, Identifier attributeId) + throws ToscaPolicyConversionException { + return ToscaPolicyTranslatorUtils.buildMatchTypeDesignator( + XACML3.ID_FUNCTION_BOOLEAN_EQUAL, + validate(value).toString(), + XACML3.ID_DATATYPE_BOOLEAN, + attributeId, + XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE); + } + +} diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeFloat.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeFloat.java new file mode 100644 index 00000000..5231ca62 --- /dev/null +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeFloat.java @@ -0,0 +1,68 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.pdp.xacml.application.common.matchable; + +import com.att.research.xacml.api.Identifier; +import com.att.research.xacml.api.XACML3; +import org.onap.policy.models.tosca.authorative.concepts.ToscaEntrySchema; +import org.onap.policy.models.tosca.authorative.concepts.ToscaProperty; +import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException; +import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslatorUtils; + +public class MatchablePropertyTypeFloat extends MatchablePropertyTypeBase { + + public MatchablePropertyTypeFloat(ToscaProperty inProperty) { + super(inProperty); + } + + public MatchablePropertyTypeFloat(ToscaEntrySchema toscaSchema) { + super(toscaSchema); + } + + @Override + public Float validate(Object value) throws ToscaPolicyConversionException { + // + // Most likely it isn't because Gson does not recognize floats + // + if (value instanceof Float) { + return (Float) value; + } + try { + return Float.parseFloat(value.toString()); + } catch (NumberFormatException e) { + throw new ToscaPolicyConversionException("Bad float value" + value.toString(), e); + } + } + + @Override + public Object generate(Object value, Identifier attributeId) + throws ToscaPolicyConversionException { + return ToscaPolicyTranslatorUtils.buildMatchTypeDesignator( + XACML3.ID_FUNCTION_INTEGER_EQUAL, + validate(value).toString(), + XACML3.ID_DATATYPE_INTEGER, + attributeId, + XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE); + } + +} diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeInteger.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeInteger.java new file mode 100644 index 00000000..ea798a5a --- /dev/null +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeInteger.java @@ -0,0 +1,64 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.pdp.xacml.application.common.matchable; + +import com.att.research.xacml.api.Identifier; +import com.att.research.xacml.api.XACML3; +import org.onap.policy.models.tosca.authorative.concepts.ToscaEntrySchema; +import org.onap.policy.models.tosca.authorative.concepts.ToscaProperty; +import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException; +import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslatorUtils; + +public class MatchablePropertyTypeInteger extends MatchablePropertyTypeBase { + + public MatchablePropertyTypeInteger(ToscaProperty inProperty) { + super(inProperty); + } + + public MatchablePropertyTypeInteger(ToscaEntrySchema toscaSchema) { + super(toscaSchema); + } + + @Override + public Integer validate(Object value) throws ToscaPolicyConversionException { + if (value instanceof Integer) { + return (Integer) value; + } + try { + return Integer.valueOf(value.toString()); + } catch (NumberFormatException e) { + throw new ToscaPolicyConversionException("Bad integer value" + value.toString(), e); + } + } + + @Override + public Object generate(Object value, Identifier attributeId) throws ToscaPolicyConversionException { + return ToscaPolicyTranslatorUtils.buildMatchTypeDesignator( + XACML3.ID_FUNCTION_INTEGER_EQUAL, + validate(value).toString(), + XACML3.ID_DATATYPE_INTEGER, + attributeId, + XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE); + } + +} diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeList.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeList.java new file mode 100644 index 00000000..55fea540 --- /dev/null +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeList.java @@ -0,0 +1,73 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.pdp.xacml.application.common.matchable; + +import com.att.research.xacml.api.Identifier; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType; +import org.onap.policy.models.tosca.authorative.concepts.ToscaEntrySchema; +import org.onap.policy.models.tosca.authorative.concepts.ToscaProperty; +import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException; +import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslatorUtils; + +public class MatchablePropertyTypeList extends MatchablePropertyTypeBase>> { + MatchableProperty primitiveProperty; + + /** + * constructor. + * + * @param toscaProperty ToscaProperty object + */ + public MatchablePropertyTypeList(ToscaProperty toscaProperty) { + super(toscaProperty); + + ToscaEntrySchema schema = toscaProperty.getEntrySchema(); + this.primitiveProperty = MatchablePolicyType.handlePrimitive(schema.getType(), schema); + } + + @SuppressWarnings("unchecked") + @Override + public List> validate(Object value) throws ToscaPolicyConversionException { + if (value instanceof Collection) { + return (List>) value; + } + return Collections.emptyList(); + } + + @Override + public Object generate(Object value, Identifier attributeId) + throws ToscaPolicyConversionException { + AnyOfType anyOf = null; + for (Object val : this.validate(value)) { + // + // Build the AnyOfType + // + anyOf = ToscaPolicyTranslatorUtils.buildAndAppendAllof(anyOf, + primitiveProperty.getType().generate(val, attributeId)); + } + return anyOf; + } + +} diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeMap.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeMap.java new file mode 100644 index 00000000..d5083e1b --- /dev/null +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeMap.java @@ -0,0 +1,80 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.pdp.xacml.application.common.matchable; + +import com.att.research.xacml.api.Identifier; +import com.att.research.xacml.std.IdentifierImpl; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import lombok.NonNull; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType; +import org.onap.policy.models.tosca.authorative.concepts.ToscaEntrySchema; +import org.onap.policy.models.tosca.authorative.concepts.ToscaProperty; +import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException; +import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslatorUtils; + +public class MatchablePropertyTypeMap extends MatchablePropertyTypeBase>> { + MatchableProperty primitiveProperty; + + /** + * constructor. + * + * @param toscaProperty ToscaProperty object + */ + public MatchablePropertyTypeMap(@NonNull ToscaProperty toscaProperty) { + super(toscaProperty); + + ToscaEntrySchema schema = toscaProperty.getEntrySchema(); + this.primitiveProperty = MatchablePolicyType.handlePrimitive(schema.getType(), schema); + } + + @SuppressWarnings("unchecked") + @Override + public Map> validate(Object value) throws ToscaPolicyConversionException { + if (value instanceof Map) { + return (Map>) value; + } + return Collections.emptyMap(); + } + + @Override + public Object generate(Object value, Identifier attributeId) throws ToscaPolicyConversionException { + AnyOfType anyOf = new AnyOfType(); + for (Entry> entrySet : this.validate(value).entrySet()) { + final String id = entrySet.getKey(); + final Object val = entrySet.getValue(); + // + // For map we use the LHS as part of the attribute id + // + Identifier newId = new IdentifierImpl(attributeId + ":" + id); + // + // Build the AnyOfType + // + anyOf = ToscaPolicyTranslatorUtils.buildAndAppendAllof(anyOf, + primitiveProperty.getType().generate(val, newId)); + } + return anyOf; + } + +} diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeString.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeString.java new file mode 100644 index 00000000..4605b845 --- /dev/null +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeString.java @@ -0,0 +1,60 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.pdp.xacml.application.common.matchable; + +import com.att.research.xacml.api.Identifier; +import com.att.research.xacml.api.XACML3; +import org.onap.policy.models.tosca.authorative.concepts.ToscaEntrySchema; +import org.onap.policy.models.tosca.authorative.concepts.ToscaProperty; +import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslatorUtils; + +public class MatchablePropertyTypeString extends MatchablePropertyTypeBase { + + public MatchablePropertyTypeString(ToscaProperty toscaProperty) { + super(toscaProperty); + } + + public MatchablePropertyTypeString(ToscaEntrySchema toscaSchema) { + super(toscaSchema); + } + + @Override + public String validate(Object value) { + // + // Future we could do constraint validation + // + return value.toString(); + } + + @Override + public Object generate(Object value, Identifier attributeId) { + return ToscaPolicyTranslatorUtils.buildMatchTypeDesignator( + XACML3.ID_FUNCTION_STRING_EQUAL, + validate(value), + XACML3.ID_DATATYPE_STRING, + attributeId, + XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE); + } + + +} diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeTimestamp.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeTimestamp.java new file mode 100644 index 00000000..d24147cd --- /dev/null +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePropertyTypeTimestamp.java @@ -0,0 +1,64 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.pdp.xacml.application.common.matchable; + +import com.att.research.xacml.api.Identifier; +import com.att.research.xacml.api.XACML3; +import com.att.research.xacml.std.datatypes.ISO8601DateTime; +import java.text.ParseException; +import org.onap.policy.models.tosca.authorative.concepts.ToscaEntrySchema; +import org.onap.policy.models.tosca.authorative.concepts.ToscaProperty; +import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException; +import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslatorUtils; + +public class MatchablePropertyTypeTimestamp extends MatchablePropertyTypeBase { + + public MatchablePropertyTypeTimestamp(ToscaProperty inProperty) { + super(inProperty); + } + + public MatchablePropertyTypeTimestamp(ToscaEntrySchema toscaSchema) { + super(toscaSchema); + } + + @Override + public ISO8601DateTime validate(Object value) throws ToscaPolicyConversionException { + try { + return ISO8601DateTime.fromISO8601DateTimeString(value.toString()); + } catch (ParseException e) { + throw new ToscaPolicyConversionException("bad ISO8601 timevalue " + value.toString(), e); + } + } + + @Override + public Object generate(Object value, Identifier attributeId) + throws ToscaPolicyConversionException { + return ToscaPolicyTranslatorUtils.buildMatchTypeDesignator( + XACML3.ID_FUNCTION_DATETIME_EQUAL, + validate(value).toString(), + XACML3.ID_DATATYPE_DATETIME, + attributeId, + XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE); + } + +} diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchableTranslator.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchableTranslator.java index 690b710f..11a45e0d 100644 --- a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchableTranslator.java +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchableTranslator.java @@ -35,7 +35,6 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; @@ -44,9 +43,9 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import lombok.Setter; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AllOfType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.RuleType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType; @@ -61,8 +60,8 @@ import org.onap.policy.models.tosca.authorative.concepts.ToscaDataType; import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy; import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyType; import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier; -import org.onap.policy.models.tosca.authorative.concepts.ToscaProperty; import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; +import org.onap.policy.models.tosca.simple.concepts.JpaToscaServiceTemplate; import org.onap.policy.pdp.xacml.application.common.OnapObligation; import org.onap.policy.pdp.xacml.application.common.PolicyApiCaller; import org.onap.policy.pdp.xacml.application.common.PolicyApiException; @@ -70,6 +69,9 @@ import org.onap.policy.pdp.xacml.application.common.ToscaDictionary; import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException; import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslatorUtils; import org.onap.policy.pdp.xacml.application.common.XacmlApplicationException; +import org.onap.policy.pdp.xacml.application.common.matchable.MatchableCallback; +import org.onap.policy.pdp.xacml.application.common.matchable.MatchablePolicyType; +import org.onap.policy.pdp.xacml.application.common.matchable.MatchableProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -80,16 +82,14 @@ import org.slf4j.LoggerFactory; * @author pameladragosh * */ -public class StdMatchableTranslator extends StdBaseTranslator { +public class StdMatchableTranslator extends StdBaseTranslator implements MatchableCallback { private static final Logger LOGGER = LoggerFactory.getLogger(StdMatchableTranslator.class); private static final StandardYamlCoder standardYamlCoder = new StandardYamlCoder(); - private static final String MSG_WEIGHT = "Weight is {}"; - private static final String MSG_WEIGHT_LIST = "Weight list is {}"; - private static final String MSG_WEIGHT_MAP = "Weight map is {}"; - private final Map matchablePolicyTypes = new HashMap<>(); + private final Map matchableCache = new HashMap<>(); + @Setter private RestServerParameters apiRestParameters; @Setter @@ -322,6 +322,50 @@ public class StdMatchableTranslator extends StdBaseTranslator { return newPolicyType; } + @Override + public ToscaPolicyType retrievePolicyType(String derivedFrom) { + ToscaServiceTemplate template = this.findPolicyType(new ToscaPolicyTypeIdentifier(derivedFrom, "1.0.0")); + if (template == null) { + LOGGER.error("Could not retrieve Policy Type {}", derivedFrom); + return null; + } + return template.getPolicyTypes().get(derivedFrom); + } + + @Override + public ToscaDataType retrieveDataType(String datatype) { + // + // Our outer class is not storing the current template being scanned + // + LOGGER.error("this retrieveDataType should not be called."); + return null; + } + + private class MyMatchableCallback implements MatchableCallback { + private StdMatchableTranslator translator; + private ToscaServiceTemplate template; + + public MyMatchableCallback(StdMatchableTranslator translator, ToscaServiceTemplate template) { + this.translator = translator; + this.template = template; + } + + @Override + public ToscaPolicyType retrievePolicyType(String derivedFrom) { + ToscaPolicyType policyType = this.template.getPolicyTypes().get(derivedFrom); + if (policyType != null) { + return policyType; + } + return translator.retrievePolicyType(derivedFrom); + } + + @Override + public ToscaDataType retrieveDataType(String datatype) { + return this.template.getDataTypes().get(datatype); + } + + } + /** * For generating target type, we scan for matchable properties * and use those to build the policy. @@ -330,496 +374,100 @@ public class StdMatchableTranslator extends StdBaseTranslator { * @param policyTypes Collection of policy Type to find matchable metadata * @return {@code Pair} Returns a TargetType and a Total Weight of matchables. */ - protected Pair generateTargetType(ToscaPolicy policyType, - ToscaServiceTemplate policyTemplate) { + protected Pair generateTargetType(ToscaPolicy policy, ToscaServiceTemplate template) { // // Our return object // - TargetType targetType = new TargetType(); - // - // Top-level list of properties - // - Map properties = policyType.getProperties(); + TargetType target = new TargetType(); // - // To start, we know these properties are for this specific Policy Type ID/Version + // See if we have a matchable in the cache already // - ToscaPolicyTypeIdentifier propertiesPolicyId = policyType.getTypeIdentifier(); + MatchablePolicyType matchablePolicyType = matchableCache.get(policy.getTypeIdentifier()); // - // Scan the property map for matchables + // If not found, create one // - int totalWeight = findMatchablesInProperties(properties, propertiesPolicyId, policyTemplate, targetType); - LOGGER.info("Total weight is {}", totalWeight); - return Pair.of(targetType, totalWeight); - } - - protected int findMatchablesInProperties(Map properties, - ToscaPolicyTypeIdentifier propertiesPolicyId, - ToscaServiceTemplate policyTemplate, - TargetType targetType) { - LOGGER.info("findMatchablesInProperties from policy Type {} {}", propertiesPolicyId, properties); - // - // We better have the policy type definition available from the template - // - ToscaPolicyType policyType = getToscaPolicyTypeFromTemplate(propertiesPolicyId, policyTemplate); - if (policyType == null) { - LOGGER.error("Failed to find policy type in template {}", propertiesPolicyId); - return 0; - } - // - // Our total weight to return - // - int totalWeight = 0; - for (Entry entrySet : properties.entrySet()) { + if (matchablePolicyType == null) { // - // Find the property details + // Our callback // - Pair property = findProperty(entrySet.getKey(), - policyType, propertiesPolicyId, policyTemplate); - if (property == null) { - continue; - } - ToscaProperty toscaProperty = property.getLeft(); - LOGGER.info("Found property {} with type {} schema {}", entrySet.getKey(), toscaProperty.getType(), - (toscaProperty.getEntrySchema() == null ? "null" : toscaProperty.getEntrySchema().getType())); + MyMatchableCallback myCallback = new MyMatchableCallback(this, template); // - // Is it matchable? + // Create the matchable // - if (checkIsMatchableProperty(toscaProperty)) { - // - // This will generate the matchables for the property - // - int weight = generateMatchable(targetType, entrySet.getKey(), entrySet.getValue(), - property.getLeft(), property.getRight()); - LOGGER.info(MSG_WEIGHT, weight); - totalWeight += weight; - } else { - // - // Not matchable, but we need to check if this contains list or map of datatypes. - // Those will need to be searched for matchables. - // - if ("list".equals(toscaProperty.getType())) { - int weight = findMatchablesInList(entrySet.getKey(), entrySet.getValue(), toscaProperty, - policyTemplate, targetType); - LOGGER.info(MSG_WEIGHT_LIST, weight); - totalWeight += weight; - } else if ("map".equals(toscaProperty.getType())) { - int weight = findMatchablesInMap(entrySet.getKey(), entrySet.getValue(), toscaProperty, - policyTemplate, targetType); - LOGGER.info(MSG_WEIGHT_MAP, weight); - totalWeight += weight; - } - } - } - return totalWeight; - } - - @SuppressWarnings("unchecked") - protected int findMatchablesInList(String listPropertyName, Object listValue, ToscaProperty listProperty, - ToscaServiceTemplate listTemplate, TargetType targetType) { - // - // Don't bother if there is no schema (which should be a problem) or - // its a list of primitives - // - if (listProperty.getEntrySchema() == null) { - LOGGER.error("No entry schema for list property {}", listPropertyName); - return 0; - } - // - // If they are primitives, then no need to go through them. ?? - // - if (isYamlType(listProperty.getEntrySchema().getType())) { - LOGGER.info("list of primitives"); - return 0; - } - // - // Find the datatype - // - ToscaDataType listDataType = listTemplate.getDataTypes().get(listProperty.getEntrySchema().getType()); - if (listDataType == null) { - LOGGER.error("Unable to find datatype {}", listProperty.getEntrySchema().getType()); - return 0; - } - - int totalWeight = 0; - for (Object datatypeValue : ((Collection)listValue)) { + matchablePolicyType = new MatchablePolicyType( + template.getPolicyTypes().get(policy.getType()), myCallback); // - // This should be a map - because this is a list of datatypes. + // Cache it // - if (! (datatypeValue instanceof Map)) { - LOGGER.error("datatype {} value is not a map {}", listDataType.getName(), datatypeValue.getClass()); - continue; - } - for (Entry entrySet : ((Map)datatypeValue).entrySet()) { - ToscaProperty toscaProperty = listDataType.getProperties().get(entrySet.getKey()); - if (toscaProperty == null) { - LOGGER.error("Failed to find datatype {} property {}", listDataType.getName(), entrySet.getKey()); - continue; - } - LOGGER.info("Found list property {} with type {} schema {}", entrySet.getKey(), toscaProperty.getType(), - (toscaProperty.getEntrySchema() == null ? "null" : toscaProperty.getEntrySchema().getType())); - // - // Is it matchable? - // - if (checkIsMatchableProperty(toscaProperty)) { - // - // This will generate the matchables for the property - // - int weight = generateMatchable(targetType, entrySet.getKey(), entrySet.getValue(), - toscaProperty, listTemplate); - LOGGER.info(MSG_WEIGHT, weight); - totalWeight += weight; - } else { - // - // Not matchable, but we need to check if this contains list or map of datatypes. - // Those will need to be searched for matchables. - // - if ("list".equals(toscaProperty.getType())) { - int weight = findMatchablesInList(entrySet.getKey(), entrySet.getValue(), toscaProperty, - listTemplate, targetType); - LOGGER.info(MSG_WEIGHT_LIST, weight); - totalWeight += weight; - } else if ("map".equals(toscaProperty.getType())) { - int weight = findMatchablesInMap(entrySet.getKey(), entrySet.getValue(), toscaProperty, - listTemplate, targetType); - LOGGER.info(MSG_WEIGHT_MAP, weight); - totalWeight += weight; - } - } - } + matchableCache.put(policy.getTypeIdentifier(), matchablePolicyType); } - - return totalWeight; - } - - @SuppressWarnings("unchecked") - protected int findMatchablesInMap(String mapPropertyName, Object mapValue, ToscaProperty mapProperty, - ToscaServiceTemplate mapTemplate, TargetType targetType) { // - // There needs to be a schema. + // Fill in the target type with potential matchables // - if (mapProperty.getEntrySchema() == null) { - LOGGER.error("No entry schema for map property {}", mapPropertyName); - return 0; + try { + fillTargetTypeWithMatchables(target, matchablePolicyType, policy.getProperties()); + } catch (ToscaPolicyConversionException e) { + LOGGER.error("Could not generate target type", e); } // - // If they are primitives, then no need to go through them. ?? + // There may be a case for default policies there is no weight - need to clean + // up the target then else PDP will report bad policy missing AnyOf // - if (isYamlType(mapProperty.getEntrySchema().getType())) { - LOGGER.debug("map property {} is primitives", mapPropertyName); - return 0; - } + int weight = calculateWeight(target); + LOGGER.debug("Weight is {} for policy {}", weight, policy.getName()); // - // Find the datatype + // Assume the number of AllOf's is the weight for now // - ToscaDataType mapDataType = mapTemplate.getDataTypes().get(mapProperty.getEntrySchema().getType()); - if (mapDataType == null) { - LOGGER.error("Unable to find datatype {}", mapProperty.getEntrySchema().getType()); - return 0; - } - - int totalWeight = 0; - for (Entry entrySet : ((Map)mapValue).entrySet()) { - ToscaProperty toscaProperty = mapDataType.getProperties().get(entrySet.getKey()); - if (toscaProperty == null) { - LOGGER.error("Failed to find datatype {} property {}", mapDataType.getName(), entrySet.getKey()); - continue; - } - LOGGER.info("Found map property {} with type {} schema {}", entrySet.getKey(), toscaProperty.getType(), - (toscaProperty.getEntrySchema() == null ? "null" : toscaProperty.getEntrySchema().getType())); - // - // Is it matchable? - // - if (checkIsMatchableProperty(toscaProperty)) { - // - // This will generate the matchables for the property - // - int weight = generateMatchable(targetType, entrySet.getKey(), entrySet.getValue(), - toscaProperty, mapTemplate); - LOGGER.info(MSG_WEIGHT, weight); - totalWeight += weight; - } else { - // - // Not matchable, but we need to check if this contains list or map of datatypes. - // Those will need to be searched for matchables. - // - if ("list".equals(toscaProperty.getType())) { - int weight = findMatchablesInList(entrySet.getKey(), entrySet.getValue(), toscaProperty, - mapTemplate, targetType); - LOGGER.info(MSG_WEIGHT_LIST, weight); - totalWeight += weight; - } else if ("map".equals(toscaProperty.getType())) { - int weight = findMatchablesInMap(entrySet.getKey(), entrySet.getValue(), toscaProperty, - mapTemplate, targetType); - LOGGER.info(MSG_WEIGHT_MAP, weight); - totalWeight += weight; - } - } - } - - return totalWeight; + return Pair.of(target, weight); } - /** - * findMatchableProperty - Iterates through available TOSCA Policy Types and return the - * ToscaProperty and template for the property. - * - * @param propertyName Name of property - * @param policyTypes Collection of TOSCA Policy Types to scan - * @return ToscaProperty and ToscaServiceTemplate if matchable - */ - protected Pair findProperty(String propertyName, - ToscaPolicyType policyType, ToscaPolicyTypeIdentifier propertiesPolicyId, - ToscaServiceTemplate policyTemplate) { - // - // See if the property is defined by the policy template - // - ToscaProperty toscaProperty = policyType.getProperties().get(propertyName); - if (toscaProperty != null) { - // - // Does it contain the matchable property and if so its set to true? - // - return Pair.of(toscaProperty, policyTemplate); - } - LOGGER.debug("property {} is not in policy type {}", propertyName, propertiesPolicyId); - // - // Check its parent policy types - // - ToscaPolicyTypeIdentifier parentId = getParentDerivedFrom(propertiesPolicyId, policyTemplate); - while (parentId != null) { - LOGGER.debug("searching parent policy type {}", parentId); - // - // Search the existing template (should be there during runtime) - // - ToscaPolicyType parentPolicyType = getParentPolicyType(parentId, policyTemplate); - if (parentPolicyType != null) { - toscaProperty = parentPolicyType.getProperties().get(propertyName); - if (toscaProperty != null) { - return Pair.of(toscaProperty, policyTemplate); - } + @SuppressWarnings("unchecked") + protected void fillTargetTypeWithMatchables(TargetType target, MatchablePolicyType matchablePolicyType, + Map properties) throws ToscaPolicyConversionException { + for (Entry entrySet : properties.entrySet()) { + String propertyName = entrySet.getKey(); + Object propertyValue = entrySet.getValue(); + MatchableProperty matchable = matchablePolicyType.get(propertyName); + if (matchable != null) { // - // Move to the next parent + // Construct attribute id // - parentId = getParentDerivedFrom(parentId, policyTemplate); - } else { - LOGGER.warn("Parent policy type is not found {}", parentId); + Identifier id = new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + propertyName); // - // Find the parent policy type. During JUnit this may be in a separate - // file. We hope that during runtime the template is complete. + // Depending on what type it is, add it into the target // - ToscaServiceTemplate parentTemplate = findPolicyType(parentId); - if (parentTemplate != null) { - parentPolicyType = getParentPolicyType(parentId, parentTemplate); - if (parentPolicyType != null) { - toscaProperty = parentPolicyType.getProperties().get(propertyName); - if (toscaProperty != null) { - return Pair.of(toscaProperty, parentTemplate); - } - } - // - // Move to the next parent - // - parentId = getParentDerivedFrom(parentId, parentTemplate); - } else { - LOGGER.error("Unable to find/pull parent policy type {}", parentId); - parentId = null; - } - } - } - LOGGER.warn("Property {} is NOT found in any template", propertyName); - return null; - } - - private ToscaPolicyType getToscaPolicyTypeFromTemplate(ToscaPolicyTypeIdentifier propertiesPolicyId, - ToscaServiceTemplate policyTemplate) { - for (Entry entry : policyTemplate.getPolicyTypes().entrySet()) { - if (propertiesPolicyId.getName().equals(entry.getKey()) - && propertiesPolicyId.getVersion().equals(entry.getValue().getVersion())) { - return entry.getValue(); - } - } - return null; - } + ToscaPolicyTranslatorUtils.buildAndAppendTarget(target, + matchable.getType().generate(propertyValue, id)); - private boolean isYamlType(String type) { - return "string".equalsIgnoreCase(type) || "integer".equalsIgnoreCase(type) || "float".equalsIgnoreCase(type) - || "boolean".equalsIgnoreCase(type) || "timestamp".equalsIgnoreCase(type); - } - - /** - * checkIsMatchableProperty - checks the property metadata to see if matchable exists. - * - * @param toscaProperty ToscaProperty - * @return true if matchable - */ - protected boolean checkIsMatchableProperty(ToscaProperty toscaProperty) { - if (toscaProperty.getMetadata() == null) { - return false; - } - for (Entry entrySet : toscaProperty.getMetadata().entrySet()) { - if ("matchable".equals(entrySet.getKey()) && "true".equals(entrySet.getValue())) { - LOGGER.debug("found matchable of type {}", toscaProperty.getType()); - return true; + continue; } - } - return false; - } - - /** - * generateMatchable - Given the object, generates list of MatchType objects and add them - * to the TargetType object. Returns a weight which is the number of AnyOf's generated. The - * weight can be used to further filter the results for "closest match". - * - * @param targetType TargetType object to add matches to - * @param key Property key - * @param value Object is the value - which can be a Collection or single Object - * @param toscaProperty The property that was found - * @param toscaServiceTemplate The template from which the property was found - * @return int Weight of the match. - */ - protected int generateMatchable(TargetType targetType, String key, Object value, ToscaProperty toscaProperty, - ToscaServiceTemplate toscaServiceTemplate) { - int weight = 0; - if (value instanceof Collection) { // - // Further determine how we treat this collection. We will need the schema - // if it is not available then we have to bail. + // Here is the special case where we look for a Collection of values that may + // contain potential matchables // - if (toscaProperty.getEntrySchema() == null) { - LOGGER.error("No schema for property {} of type {}", key, toscaProperty.getType()); - } - if ("list".equals(toscaProperty.getType())) { - return generateMatchableList(targetType, key, value, toscaProperty, toscaServiceTemplate); - } - if ("map".equals(toscaProperty.getType())) { - return generateMatchableMap(targetType, key, value, toscaProperty, toscaServiceTemplate); - } - } else { - AnyOfType anyOf = generateMatches(Arrays.asList(value), - new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + key)); - if (! anyOf.getAllOf().isEmpty()) { - targetType.getAnyOf().add(anyOf); - weight = 1; + if (propertyValue instanceof List) { + for (Object listValue : ((List)propertyValue)) { + if (listValue instanceof Map) { + fillTargetTypeWithMatchables(target, matchablePolicyType, (Map) listValue); + } + } + } else if (propertyValue instanceof Map) { + fillTargetTypeWithMatchables(target, matchablePolicyType, (Map) propertyValue); } } - return weight; } - @SuppressWarnings("unchecked") - protected int generateMatchableList(TargetType targetType, String key, Object value, ToscaProperty toscaProperty, - ToscaServiceTemplate toscaServiceTemplate) { + protected int calculateWeight(TargetType target) { int weight = 0; - if (isYamlType(toscaProperty.getEntrySchema().getType())) { - AnyOfType anyOf = generateMatches((Collection) value, - new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + key)); - if (! anyOf.getAllOf().isEmpty()) { - targetType.getAnyOf().add(anyOf); - weight = 1; + for (AnyOfType anyOf : target.getAnyOf()) { + for (AllOfType allOf : anyOf.getAllOf()) { + weight += allOf.getMatch().size(); } - } else { - LOGGER.debug("PLD use datatype for list?"); } - return weight; - } - @SuppressWarnings("unchecked") - protected int generateMatchableMap(TargetType targetType, String key, Object value, ToscaProperty toscaProperty, - ToscaServiceTemplate toscaServiceTemplate) { - int weight = 0; - if (isYamlType(toscaProperty.getEntrySchema().getType())) { - // - // PLD TODO - this won't work. Right now there are no maps being used to match. - // need to investigate whether we really can support that situation. - // - AnyOfType anyOf = generateMatches((Collection) value, - new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + key)); - if (! anyOf.getAllOf().isEmpty()) { - targetType.getAnyOf().add(anyOf); - weight = 1; - } - } else { - LOGGER.debug("PLD use datatype for map?"); - } return weight; } - /** - * generateMatches - Goes through the collection of objects, creates a MatchType object - * for each object and associates it with the given attribute Id. Returns the AnyOfType - * object that contains all the generated MatchType objects. - * - * @param matchables Collection of object to generate MatchType from - * @param attributeId Given attribute Id for each MatchType - * @return AnyOfType object - */ - protected AnyOfType generateMatches(Collection matchables, Identifier attributeId) { - // - // This is our outer AnyOf - which is an OR - // - AnyOfType anyOf = new AnyOfType(); - for (Object matchable : matchables) { - // - // Default to string - // - Identifier idFunction = XACML3.ID_FUNCTION_STRING_EQUAL; - Identifier idDatatype = XACML3.ID_DATATYPE_STRING; - // - // See if we are another datatype - // - // We should add datetime support. But to do that we need - // probably more metadata to describe how that would be translated. - // - if (matchable instanceof Integer) { - idFunction = XACML3.ID_FUNCTION_INTEGER_EQUAL; - idDatatype = XACML3.ID_DATATYPE_INTEGER; - } else if (matchable instanceof Double) { - idFunction = XACML3.ID_FUNCTION_DOUBLE_EQUAL; - idDatatype = XACML3.ID_DATATYPE_DOUBLE; - } else if (matchable instanceof Boolean) { - idFunction = XACML3.ID_FUNCTION_BOOLEAN_EQUAL; - idDatatype = XACML3.ID_DATATYPE_BOOLEAN; - } - // - // Create a match for this - // - MatchType match = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator( - idFunction, - matchable.toString(), - idDatatype, - attributeId, - XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE); - // - // Now create an anyOf (OR) - // - anyOf.getAllOf().add(ToscaPolicyTranslatorUtils.buildAllOf(match)); - } - return anyOf; - } - - private ToscaPolicyTypeIdentifier getParentDerivedFrom(ToscaPolicyTypeIdentifier policyTypeId, - ToscaServiceTemplate template) { - for (Entry entrySet : template.getPolicyTypes().entrySet()) { - ToscaPolicyType policyType = entrySet.getValue(); - if (entrySet.getKey().equals(policyTypeId.getName()) - && policyType.getVersion().equals(policyTypeId.getVersion()) - && ! "tosca.policies.Root".equals(policyType.getDerivedFrom())) { - return new ToscaPolicyTypeIdentifier(policyType.getDerivedFrom(), "1.0.0"); - } - } - - return null; - } - - private ToscaPolicyType getParentPolicyType(ToscaPolicyTypeIdentifier policyTypeId, ToscaServiceTemplate template) { - for (Entry entrySet : template.getPolicyTypes().entrySet()) { - ToscaPolicyType policyType = entrySet.getValue(); - if (entrySet.getKey().equals(policyTypeId.getName()) - && policyType.getVersion().equals(policyTypeId.getVersion())) { - return policyType; - } - } - return null; - } - /** * findPolicyType - given the ToscaPolicyTypeIdentifier, finds it in memory, or * then tries to find it either locally on disk or pull it from the Policy @@ -889,8 +537,17 @@ public class StdMatchableTranslator extends StdBaseTranslator { // LOGGER.info("Read in local policy type {}", policyTypePath.toAbsolutePath()); try { - return standardYamlCoder.decode(new String(bytes, StandardCharsets.UTF_8), + // + // Decode the template + // + ToscaServiceTemplate template = standardYamlCoder.decode(new String(bytes, StandardCharsets.UTF_8), ToscaServiceTemplate.class); + // + // Ensure all the fields are setup correctly + // + JpaToscaServiceTemplate jtst = new JpaToscaServiceTemplate(); + jtst.fromAuthorative(template); + return jtst.toAuthorative(); } catch (CoderException e) { LOGGER.error("Failed to decode tosca template for {}", policyTypePath, e); } diff --git a/applications/common/src/test/java/org/onap/policy/pdp/xacml/application/common/ToscaPolicyTranslatorUtilsTest.java b/applications/common/src/test/java/org/onap/policy/pdp/xacml/application/common/ToscaPolicyTranslatorUtilsTest.java index 8b85a0df..5d451e2c 100644 --- a/applications/common/src/test/java/org/onap/policy/pdp/xacml/application/common/ToscaPolicyTranslatorUtilsTest.java +++ b/applications/common/src/test/java/org/onap/policy/pdp/xacml/application/common/ToscaPolicyTranslatorUtilsTest.java @@ -27,7 +27,10 @@ import static org.junit.Assert.assertTrue; import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; - +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AllOfType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType; import org.junit.Test; public class ToscaPolicyTranslatorUtilsTest { @@ -45,4 +48,24 @@ public class ToscaPolicyTranslatorUtilsTest { assertThat(ToscaPolicyTranslatorUtils.generateTimeInRange("T00:00:00Z", "T08:00:00Z")).isNotNull(); } + @Test + public void testBuildAndAppend() { + assertThat(ToscaPolicyTranslatorUtils.buildAndAppendAllof(null, new MatchType())).isInstanceOf(AnyOfType.class); + assertThat(ToscaPolicyTranslatorUtils.buildAndAppendAllof(null, new AllOfType())).isInstanceOf(AnyOfType.class); + assertThat(ToscaPolicyTranslatorUtils.buildAndAppendAllof(null, new String())).isNull(); + + assertThat(ToscaPolicyTranslatorUtils.buildAndAppendTarget(new TargetType(), + new AnyOfType()).getAnyOf()).hasSize(1); + assertThat(ToscaPolicyTranslatorUtils.buildAndAppendTarget(new TargetType(), + new MatchType()).getAnyOf()).hasSize(1); + assertThat(ToscaPolicyTranslatorUtils.buildAndAppendTarget(new TargetType(), + new String()).getAnyOf()).isEmpty(); + } + + @Test + public void testInteger() { + assertThat(ToscaPolicyTranslatorUtils.parseInteger("foo")).isNull(); + assertThat(ToscaPolicyTranslatorUtils.parseInteger("1")).isEqualTo(1); + assertThat(ToscaPolicyTranslatorUtils.parseInteger("1.0")).isEqualTo(1); + } } diff --git a/applications/common/src/test/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePolicyTypeTest.java b/applications/common/src/test/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePolicyTypeTest.java new file mode 100644 index 00000000..c23f7028 --- /dev/null +++ b/applications/common/src/test/java/org/onap/policy/pdp/xacml/application/common/matchable/MatchablePolicyTypeTest.java @@ -0,0 +1,300 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.pdp.xacml.application.common.matchable; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import com.att.research.xacml.api.Identifier; +import com.att.research.xacml.api.XACML3; +import com.att.research.xacml.std.IdentifierImpl; +import com.att.research.xacml.util.XACMLPolicyWriter; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AllOfType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType; +import org.junit.BeforeClass; +import org.junit.Test; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.common.utils.coder.StandardYamlCoder; +import org.onap.policy.common.utils.resources.ResourceUtils; +import org.onap.policy.models.tosca.authorative.concepts.ToscaDataType; +import org.onap.policy.models.tosca.authorative.concepts.ToscaEntrySchema; +import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy; +import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyType; +import org.onap.policy.models.tosca.authorative.concepts.ToscaProperty; +import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; +import org.onap.policy.models.tosca.simple.concepts.JpaToscaServiceTemplate; +import org.onap.policy.pdp.xacml.application.common.ToscaDictionary; +import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MatchablePolicyTypeTest implements MatchableCallback { + private static final Logger LOGGER = LoggerFactory.getLogger(MatchablePolicyTypeTest.class); + private static final StandardYamlCoder yamlCoder = new StandardYamlCoder(); + private static final String TEST_POLICYTYPE_FILE = "src/test/resources/matchable/onap.policies.Test-1.0.0.yaml"; + private static final String TEST_POLICY_FILE = "src/test/resources/matchable/test.policies.input.tosca.yaml"; + private static final String TEST_POLICYTYPE = "onap.policies.base.middle.Test"; + private static ToscaServiceTemplate testTemplate; + private static ToscaPolicy testPolicy; + + /** + * Loads our resources. + * + * @throws CoderException object + */ + @BeforeClass + public static void setupLoadPolicy() throws CoderException { + // + // Load our test policy type + // + String policyType = ResourceUtils.getResourceAsString(TEST_POLICYTYPE_FILE); + // + // Serialize it into a class + // + ToscaServiceTemplate serviceTemplate = yamlCoder.decode(policyType, ToscaServiceTemplate.class); + // + // Make sure all the fields are setup properly + // + JpaToscaServiceTemplate jtst = new JpaToscaServiceTemplate(); + jtst.fromAuthorative(serviceTemplate); + testTemplate = jtst.toAuthorative(); + // + // Make sure the Policy Types are there + // + assertEquals(3, testTemplate.getPolicyTypes().size()); + assertNotNull(testTemplate.getPolicyTypes().get("onap.policies.Base")); + assertNotNull(testTemplate.getPolicyTypes().get("onap.policies.base.Middle")); + assertNotNull(testTemplate.getPolicyTypes().get(TEST_POLICYTYPE)); + // + // Load our test policy + // + String policy = ResourceUtils.getResourceAsString(TEST_POLICY_FILE); + // + // Serialize it into a class + // + serviceTemplate = yamlCoder.decode(policy, ToscaServiceTemplate.class); + // + // Make sure all the fields are setup properly + // + jtst = new JpaToscaServiceTemplate(); + jtst.fromAuthorative(serviceTemplate); + ToscaServiceTemplate policyTemplate = jtst.toAuthorative(); + assertEquals(1, policyTemplate.getToscaTopologyTemplate().getPolicies().size()); + testPolicy = policyTemplate.getToscaTopologyTemplate().getPolicies().get(0).get("Test.policy"); + assertNotNull(testPolicy); + } + + @Test + public void testAllCodeCoverage() { + assertThatExceptionOfType(NullPointerException.class).isThrownBy(() -> + new MatchablePolicyType(null, null)); + + assertThatExceptionOfType(NullPointerException.class).isThrownBy(() -> + new MatchablePropertyTypeMap(null)); + assertThatExceptionOfType(NullPointerException.class).isThrownBy(() -> + MatchablePolicyType.isMatchable(null)); + assertThat(MatchablePolicyType.isMatchable(new ToscaProperty())).isFalse(); + // + // Unlikely these would be called - just get code coverage on them + // + ToscaEntrySchema schema = new ToscaEntrySchema(); + schema.setType("integer"); + assertThat(MatchablePolicyType.handlePrimitive("foo", schema)).isNotNull(); + schema.setType("float"); + assertThat(MatchablePolicyType.handlePrimitive("foo", schema)).isNotNull(); + schema.setType("boolean"); + assertThat(MatchablePolicyType.handlePrimitive("foo", schema)).isNotNull(); + schema.setType("timestamp"); + assertThat(MatchablePolicyType.handlePrimitive("foo", schema)).isNotNull(); + schema.setType("footype"); + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> + MatchablePolicyType.handlePrimitive("foo", schema) + ); + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> + MatchablePolicyType.handlePrimitive("foo", schema) + ); + ToscaProperty toscaProperty = new ToscaProperty(); + Map metadata = new HashMap<>(); + metadata.put("matchable", "true"); + toscaProperty.setMetadata(metadata); + toscaProperty.setType("garbage"); + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> + MatchablePolicyType.handlePrimitive("foo", toscaProperty) + ); + Map matchables = null; + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> + MatchablePolicyType.handleList("foo", toscaProperty, matchables, this) + ); + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> + MatchablePolicyType.handleMap("foo", toscaProperty, matchables, this) + ); + } + + @Test + public void testPrimitiveValidation() throws Exception { + ToscaProperty property = new ToscaProperty(); + MatchablePropertyTypeBoolean booleanValue = new MatchablePropertyTypeBoolean(property); + assertThat(booleanValue.validate(Boolean.TRUE)).isEqualTo(Boolean.TRUE); + assertThat(booleanValue.validate("no")).isEqualTo(Boolean.FALSE); + assertThat(booleanValue.validate("foo")).isEqualTo(Boolean.FALSE); + + MatchablePropertyTypeInteger integerValue = new MatchablePropertyTypeInteger(property); + assertThat(integerValue.validate("5")).isEqualTo(5); + assertThatExceptionOfType(ToscaPolicyConversionException.class).isThrownBy(() -> integerValue.validate("foo")); + + MatchablePropertyTypeFloat floatValue = new MatchablePropertyTypeFloat(property); + assertThat(floatValue.validate("5")).isEqualTo(5); + assertThat(floatValue.validate(Float.MIN_NORMAL)).isEqualTo(Float.MIN_NORMAL); + assertThatExceptionOfType(ToscaPolicyConversionException.class).isThrownBy(() -> floatValue.validate("foo")); + + MatchablePropertyTypeTimestamp timestampValue = new MatchablePropertyTypeTimestamp(property); + assertThat(timestampValue.validate("2018-10-11T22:12:44").getHour()).isEqualTo(22); + assertThatExceptionOfType(ToscaPolicyConversionException.class).isThrownBy(() -> + timestampValue.validate("foo")); + + ToscaEntrySchema schema = new ToscaEntrySchema(); + schema.setType("string"); + property.setEntrySchema(schema); + MatchablePropertyTypeMap mapValue = new MatchablePropertyTypeMap(property); + assertThat(mapValue.validate(new String("foo"))).hasSize(0); + + MatchablePropertyTypeList listValue = new MatchablePropertyTypeList(property); + assertThat(listValue.validate(new String("foo"))).hasSize(0); + } + + @Test + public void testMatchables() throws ToscaPolicyConversionException { + // + // Step 1: Create matchables from the PolicyType + // + MatchablePolicyType matchablePolicyType = new MatchablePolicyType(testTemplate.getPolicyTypes() + .get(TEST_POLICYTYPE), this); + assertThat(matchablePolicyType).isNotNull(); + assertThat(matchablePolicyType.getPolicyId()).isNotNull(); + assertThat(matchablePolicyType.getPolicyId().getName()).isEqualTo(TEST_POLICYTYPE); + // + // Dump them out to see what we have + // + matchablePolicyType.getMatchables().forEach((matchable, property) -> { + LOGGER.info("matchable: {}: {}", matchable, property); + }); + // + // Sanity check - these are the total possible match types available + // + assertThat(matchablePolicyType.getMatchables()).hasSize(19); + // + // Step 2) Go through example policy and generate data for our Target + // + final TargetType target = new TargetType(); + target.getAnyOf().add(new AnyOfType()); + generateTargetType(target, matchablePolicyType, testPolicy.getProperties()); + // + // Stuff results in a simple Policy + // + final PolicyType policy = new PolicyType(); + policy.setTarget(target); + policy.setPolicyId("foo"); + policy.setVersion("1"); + policy.setRuleCombiningAlgId(XACML3.DENY_UNLESS_PERMIT); + // + // Dump it out so we can see what was created + // + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + XACMLPolicyWriter.writePolicyFile(os, policy); + LOGGER.info("{}", os); + } catch (IOException e) { + LOGGER.error("Failed to create byte array stream", e); + } + // + // Sanity check - the example policy should have each possible match type plus + // an extra one for the list and an extra one for the map. + // + assertThat(policy.getTarget().getAnyOf()).hasSize(20); + } + + @Override + public ToscaPolicyType retrievePolicyType(String derivedFrom) { + for (Entry entrySet : testTemplate.getPolicyTypes().entrySet()) { + if (entrySet.getValue().getName().equals(derivedFrom)) { + return entrySet.getValue(); + } + } + return null; + } + + @Override + public ToscaDataType retrieveDataType(String datatype) { + return testTemplate.getDataTypes().get(datatype); + } + + @SuppressWarnings("unchecked") + private void generateTargetType(TargetType target, MatchablePolicyType matchablePolicyType, + Map properties) throws ToscaPolicyConversionException { + for (Entry entrySet : properties.entrySet()) { + String propertyName = entrySet.getKey(); + Object propertyValue = entrySet.getValue(); + MatchableProperty matchable = matchablePolicyType.get(propertyName); + if (matchable != null) { + Identifier id = new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + propertyName); + Object object = matchable.getType().generate(propertyValue, id); + // + // Depending on what type it is, add it into the target + // + if (object instanceof AnyOfType) { + target.getAnyOf().add((AnyOfType) object); + } else if (object instanceof MatchType) { + AllOfType allOf = new AllOfType(); + allOf.getMatch().add((MatchType) object); + AnyOfType anyOf = new AnyOfType(); + anyOf.getAllOf().add(allOf); + target.getAnyOf().add(anyOf); + } + } else { + // + // Here is the special case where we look for a Collection of values that may + // contain potential matchables + // + if (propertyValue instanceof List) { + for (Object listValue : ((List)propertyValue)) { + if (listValue instanceof Map) { + generateTargetType(target, matchablePolicyType, (Map) listValue); + } + } + } else if (propertyValue instanceof Map) { + generateTargetType(target, matchablePolicyType, (Map) propertyValue); + } + } + } + } +} diff --git a/applications/common/src/test/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchableTranslatorTest.java b/applications/common/src/test/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchableTranslatorTest.java index 584390cd..1de1d79d 100644 --- a/applications/common/src/test/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchableTranslatorTest.java +++ b/applications/common/src/test/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchableTranslatorTest.java @@ -160,7 +160,7 @@ public class StdMatchableTranslatorTest { } @Test - public void test() throws CoderException, ToscaPolicyConversionException, ParseException { + public void testMatchableTranslator() throws CoderException, ToscaPolicyConversionException, ParseException { // // Create our translator // diff --git a/applications/common/src/test/resources/matchable/onap.policies.Test-1.0.0.yaml b/applications/common/src/test/resources/matchable/onap.policies.Test-1.0.0.yaml index 7179c310..f0737b56 100644 --- a/applications/common/src/test/resources/matchable/onap.policies.Test-1.0.0.yaml +++ b/applications/common/src/test/resources/matchable/onap.policies.Test-1.0.0.yaml @@ -1,4 +1,4 @@ -tosca_definitions_version: tosca_simple_yaml_1_0_0 +tosca_definitions_version: tosca_simple_yaml_1_1_0 policy_types: onap.policies.Base: derived_from: tosca.policies.Root @@ -12,6 +12,7 @@ policy_types: matchable: true onap.policies.base.Middle: derived_from: onap.policies.Base + type_version: 1.0.0 version: 1.0.0 properties: middleNoMatch: @@ -25,47 +26,110 @@ policy_types: type_version: 1.0.0 version: 1.0.0 properties: - nonmatachableString: + nonmatchableString: type: string matchableString: type: string metadata: matchable: true - nonmatachableInteger: + nonmatchableInteger: type: integer metadata: matchable: false - matachableInteger: + matchableInteger: type: integer metadata: matchable: true - nonmatachableFloat: + nonmatchableFloat: type: float matchableFloat: type: float metadata: matchable: true - nonmatachableBoolean: + nonmatchableBoolean: type: boolean - matachableBoolean: + matchableBoolean: type: boolean metadata: matchable: true + nonmatchableTimestamp: + type: timestamp + matchableTimestamp: + type: timestamp + metadata: + matchable: true nonmatchableListInteger: type: list entry_schema: type: integer matchableListString: type: list + description: | + Every entry in the list is matchable, the attribute id will be the same for all value. eg. the + property name. metadata: matchable: true entry_schema: type: string - propertyOneMap: + matchableMapString: + type: map + description: | + Every entry in the map is matchable, however the attribute id will be set by the key. + metadata: + matchable: true + entry_schema: + type: string + nonmatchableMapString: type: map + description: | + Nothing gets matched - however you have no control over the LHS key. Someone could + easily set that value to a matchable property name defined elsewhere. entry_schema: - type: onap.datatype.one + type: string + badDataType: + type: i.do.not.exist + description: we can only ignore this - should get caught in the API + matchableDataType: + type: onap.datatype.zero + description: | + The matchable field in a datatype must be IGNORED, because this will result in too many assumptions + as we may go down many levels of datatypes, lists of datatypes, maps of datatypes, etc. Does every + field in the datatype become matchable? That does not make sense right now to write a Policy Type + like that. + metadata: + matchable: true data_types: + onap.datatype.zero: + derived_from: tosca.datatypes.Root + description: Note that we do not have to declare matchable for each property. + properties: + zeroStringMatchable: + type: string + metadata: + matchable: true + zeroBooleanMatchable: + type: boolean + metadata: + matchable: true + zeroFloatMatchable: + type: float + metadata: + matchable: true + zeroIntegerMatchable: + type: integer + metadata: + matchable: true + zeroTimestampMatchable: + type: timestamp + metadata: + matchable: true + zeroDatatypeOne: + type: onap.datatype.one + zeroBadDatatype: + type: list + description: we can only ignore this - should get caught in the API + entry_schema: + type: another.missing.datatype onap.datatype.one: derived_from: tosca.datatypes.Root properties: @@ -75,7 +139,7 @@ data_types: type: string metadata: matchable: true - propertyTwoList: + propertyTwoListOfDatatype: type: list entry_schema: type: onap.datatype.two @@ -88,16 +152,22 @@ data_types: type: string metadata: matchable: true - propertyThreeMap: + twoIntegerMatchable: + type: integer + metadata: + matchable: true + propertyThreeDatatype: type: map entry_schema: type: onap.datatype.three onap.datatype.three: derived_from: tosca.datatypes.Root properties: - threeString: - type: string threeStringMatchable: type: string + metadata: + matchable: true + threeIntegerMatchable: + type: integer metadata: matchable: true \ No newline at end of file diff --git a/applications/common/src/test/resources/matchable/test.policies.input.tosca.yaml b/applications/common/src/test/resources/matchable/test.policies.input.tosca.yaml index 80f72b2f..daffc2cb 100644 --- a/applications/common/src/test/resources/matchable/test.policies.input.tosca.yaml +++ b/applications/common/src/test/resources/matchable/test.policies.input.tosca.yaml @@ -1,4 +1,4 @@ -tosca_definitions_version: tosca_simple_yaml_1_0_0 +tosca_definitions_version: tosca_simple_yaml_1_1_0 topology_template: policies: - Test.policy: @@ -13,24 +13,38 @@ topology_template: baseMatch: base Match middleNoMatch: Do not match the middle middleMatch: middle Match - nonmatachableString: I am NON matchable + nonmatchableString: I am NON matchable matchableString: I should be matched - nonmatachableInteger: 0 - matachableInteger: 1000 - nonmatachableFloat: 0.0 + nonmatchableInteger: 0 + matchableInteger: 1000 + nonmatchableFloat: 0.0 matchableFloat: 1.1 - nonmatachableBoolean: false - matachableBoolean: true + nonmatchableBoolean: false + matchableBoolean: true + nonmatchableTimestamp: 2019-01-01T00:00:00Z + matchableTimestamp: 2020-01-01T00:00:00Z nonmatchableListInteger: {0, 1, 2} matchableListString: - match A - match B - propertyOneMap: + matchableMapString: + test1: matchableMap1 + test2: matchableMap2 + nonmatchableMapString: + risk: potential risk of matching + matchableDataType: + zeroStringMatchable: zero Match + zeroBooleanMatchable: true + zeroFloatMatchable: 9.9 + zeroIntegerMatchable: 1000 + zeroTimestampMatchable: 2020-01-01T23:59:59Z oneString: One is NOT matchable oneStringMatchable: One should be matched - propertyTwoList: + propertyTwoListOfDatatype: - twoString: Two is NOT matchable twoStringMatchable: Two should be matched - propertyThreeMap: - threeString: Three is NOT matchable - threeStringMatchable: Three should be matched \ No newline at end of file + twoIntegerMatchable: 55 + propertyThreeDatatype: + myThree: + threeStringMatchable: Three should match + threeIntegerMatchable: 66 \ No newline at end of file -- cgit 1.2.3-korg