From: Pamela Dragosh Date: Fri, 30 Jul 2021 17:17:12 +0000 (-0400) Subject: Add tutorial example for multi-decisions X-Git-Tag: 2.5.0~16 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=39159b5a308f5da96f48a3e9d8cd17b42fa5fed4;p=policy%2Fxacml-pdp.git Add tutorial example for multi-decisions Issue-ID: POLICY-3514 Change-Id: Ifcaa56d35ef359f2b24cc111ca9af4021000514f Signed-off-by: Pamela Dragosh --- diff --git a/tutorials/tutorial-xacml-application/src/main/java/org/onap/policy/tutorial/tutorial/TutorialRequest.java b/tutorials/tutorial-xacml-application/src/main/java/org/onap/policy/tutorial/tutorial/TutorialRequest.java index 356480bc..cda1b436 100644 --- a/tutorials/tutorial-xacml-application/src/main/java/org/onap/policy/tutorial/tutorial/TutorialRequest.java +++ b/tutorials/tutorial-xacml-application/src/main/java/org/onap/policy/tutorial/tutorial/TutorialRequest.java @@ -18,10 +18,23 @@ package org.onap.policy.tutorial.tutorial; +import com.att.research.xacml.api.DataTypeException; +import com.att.research.xacml.api.Identifier; +import com.att.research.xacml.api.Request; +import com.att.research.xacml.api.XACML3; +import com.att.research.xacml.std.IdentifierImpl; +import com.att.research.xacml.std.StdAttributeValue; +import com.att.research.xacml.std.StdMutableAttribute; +import com.att.research.xacml.std.StdMutableRequest; +import com.att.research.xacml.std.StdMutableRequestAttributes; +import com.att.research.xacml.std.StdMutableRequestReference; +import com.att.research.xacml.std.StdRequestAttributesReference; +import com.att.research.xacml.std.annotations.RequestParser; import com.att.research.xacml.std.annotations.XACMLAction; import com.att.research.xacml.std.annotations.XACMLRequest; import com.att.research.xacml.std.annotations.XACMLResource; import com.att.research.xacml.std.annotations.XACMLSubject; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import lombok.Getter; @@ -34,6 +47,15 @@ import org.onap.policy.models.decisions.concepts.DecisionRequest; @ToString @XACMLRequest(ReturnPolicyIdList = true) public class TutorialRequest { + public static final Identifier ATTRIBUTE_ID_MULTIID = new IdentifierImpl("urn:org:onap:tutorial-multi-id"); + public static final Identifier ATTRIBUTE_ID_USER = new IdentifierImpl("urn:org:onap:tutorial-user"); + public static final Identifier ATTRIBUTE_ID_ENTITY = new IdentifierImpl("urn:org:onap:tutorial-entity"); + public static final Identifier ATTRIBUTE_ID_PERMISSION = new IdentifierImpl("urn:org:onap:tutorial-permission"); + // + // For use with a multi request + // + private static final StdRequestAttributesReference refSubjects = new StdRequestAttributesReference("subjects1"); + private static final StdRequestAttributesReference refActions = new StdRequestAttributesReference("actions1"); // // Excluding from results to demonstrate control as to which attributes can be returned. // @@ -46,7 +68,7 @@ public class TutorialRequest { @XACMLSubject(attributeId = "urn:org:onap:onap-instance", includeInResults = false) private String onapInstance; - @XACMLAction() + @XACMLAction private String action; // @@ -65,9 +87,13 @@ public class TutorialRequest { * createRequest. * * @param decisionRequest Incoming - * @return TutorialRequest object + * @return Request XACML Request object + * @throws DataTypeException DataTypeException + * @throws IllegalAccessException IllegalAccessException */ - public static TutorialRequest createRequest(DecisionRequest decisionRequest) { + @SuppressWarnings("unchecked") + public static Request createRequest(DecisionRequest decisionRequest) + throws IllegalAccessException, DataTypeException { // // Create our object // @@ -86,6 +112,18 @@ public class TutorialRequest { // Add the resource attributes // Map resources = decisionRequest.getResource(); + // + // Check if this is a multi-request + // + if (resources.containsKey("users")) { + // + // Setup the multi-request + // + return buildMultiRequest(request, (List) resources.get("users")); + } + // + // Continue as a single request + // for (Entry entrySet : resources.entrySet()) { if ("user".equals(entrySet.getKey())) { request.user = entrySet.getValue().toString(); @@ -98,6 +136,87 @@ public class TutorialRequest { } } - return request; + return RequestParser.parseRequest(request); + } + + @SuppressWarnings("unchecked") + protected static Request buildMultiRequest(TutorialRequest existingRequest, List users) + throws IllegalAccessException, DataTypeException { + // + // Create a single request absorbing the existing attributes, we will copy + // them over and assign them an ID. + // + StdMutableRequest singleRequest = new StdMutableRequest(RequestParser.parseRequest(existingRequest)); + // + // Copy the attributes and assign ID's + // + StdMutableRequest multiRequest = addMultiRequestIds(singleRequest); + // + // Iterate and add in the requests + // + users.forEach(user -> addUser(multiRequest, (Map) user)); + // + // Done + // + return multiRequest; + } + + protected static StdMutableRequest addMultiRequestIds(StdMutableRequest singleRequest) { + StdMutableRequest multiRequest = new StdMutableRequest(); + singleRequest.getRequestAttributes().forEach(attributes -> { + StdMutableRequestAttributes newAttributes = new StdMutableRequestAttributes(); + if (attributes.getCategory().equals(XACML3.ID_SUBJECT_CATEGORY_ACCESS_SUBJECT)) { + newAttributes.setCategory(XACML3.ID_SUBJECT_CATEGORY_ACCESS_SUBJECT); + newAttributes.setXmlId(refSubjects.getReferenceId()); + } else if (attributes.getCategory().equals(XACML3.ID_ATTRIBUTE_CATEGORY_ACTION)) { + newAttributes.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_ACTION); + newAttributes.setXmlId(refActions.getReferenceId()); + } + attributes.getAttributes().forEach(newAttributes::add); + multiRequest.add(newAttributes); + }); + return multiRequest; + } + + protected static void addUser(StdMutableRequest multiRequest, Map user) { + StdMutableRequestAttributes attributes = new StdMutableRequestAttributes(); + attributes.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE); + for (Entry entrySet : user.entrySet()) { + StdAttributeValue value = + new StdAttributeValue<>(XACML3.ID_DATATYPE_STRING, entrySet.getValue().toString()); + StdMutableAttribute attribute = new StdMutableAttribute(); + attribute.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE); + attribute.setIncludeInResults(true); + attribute.addValue(value); + if ("multiId".equals(entrySet.getKey())) { + attributes.setXmlId(entrySet.getValue().toString()); + attribute.setAttributeId(ATTRIBUTE_ID_MULTIID); + } else if ("user".equals(entrySet.getKey())) { + attribute.setAttributeId(ATTRIBUTE_ID_USER); + } else if ("entity".equals(entrySet.getKey())) { + attribute.setAttributeId(ATTRIBUTE_ID_ENTITY); + } else if ("permission".equals(entrySet.getKey())) { + attribute.setAttributeId(ATTRIBUTE_ID_PERMISSION); + } else { + throw new IllegalArgumentException("Unknown request attribute given"); + } + attributes.add(attribute); + } + // + // Add the attributes to the Multi-Request + // + multiRequest.add(attributes); + // + // Create the references + // + StdRequestAttributesReference attributesReference = new StdRequestAttributesReference(attributes.getXmlId()); + StdMutableRequestReference reference = new StdMutableRequestReference(); + reference.add(refSubjects); + reference.add(refActions); + reference.add(attributesReference); + // + // Add the reference to this request + // + multiRequest.add(reference); } } diff --git a/tutorials/tutorial-xacml-application/src/main/java/org/onap/policy/tutorial/tutorial/TutorialResponse.java b/tutorials/tutorial-xacml-application/src/main/java/org/onap/policy/tutorial/tutorial/TutorialResponse.java new file mode 100644 index 00000000..7ef6362b --- /dev/null +++ b/tutorials/tutorial-xacml-application/src/main/java/org/onap/policy/tutorial/tutorial/TutorialResponse.java @@ -0,0 +1,30 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.tutorial.tutorial; + +import java.util.List; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.onap.policy.models.decisions.concepts.DecisionResponse; + +@Data +@EqualsAndHashCode(callSuper = true) +public class TutorialResponse extends DecisionResponse { + private List permissions; +} diff --git a/tutorials/tutorial-xacml-application/src/main/java/org/onap/policy/tutorial/tutorial/TutorialResponsePermission.java b/tutorials/tutorial-xacml-application/src/main/java/org/onap/policy/tutorial/tutorial/TutorialResponsePermission.java new file mode 100644 index 00000000..5dd03cfb --- /dev/null +++ b/tutorials/tutorial-xacml-application/src/main/java/org/onap/policy/tutorial/tutorial/TutorialResponsePermission.java @@ -0,0 +1,28 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.tutorial.tutorial; + +import java.util.Map; +import lombok.Data; + +@Data +public class TutorialResponsePermission { + private String status; + private Map attributes; +} diff --git a/tutorials/tutorial-xacml-application/src/main/java/org/onap/policy/tutorial/tutorial/TutorialTranslator.java b/tutorials/tutorial-xacml-application/src/main/java/org/onap/policy/tutorial/tutorial/TutorialTranslator.java index 31bb1037..327a507a 100644 --- a/tutorials/tutorial-xacml-application/src/main/java/org/onap/policy/tutorial/tutorial/TutorialTranslator.java +++ b/tutorials/tutorial-xacml-application/src/main/java/org/onap/policy/tutorial/tutorial/TutorialTranslator.java @@ -28,7 +28,7 @@ import com.att.research.xacml.api.Response; import com.att.research.xacml.api.Result; import com.att.research.xacml.api.XACML3; import com.att.research.xacml.std.IdentifierImpl; -import com.att.research.xacml.std.annotations.RequestParser; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -93,9 +93,9 @@ public class TutorialTranslator extends StdBaseTranslator { // // For simplicity, let's just match on the action "authorize" and the user // - var matchAction = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator( - XACML3.ID_FUNCTION_STRING_EQUAL, "authorize", XACML3.ID_DATATYPE_STRING, - XACML3.ID_ACTION_ACTION_ID, XACML3.ID_ATTRIBUTE_CATEGORY_ACTION); + var matchAction = + ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(XACML3.ID_FUNCTION_STRING_EQUAL, "authorize", + XACML3.ID_DATATYPE_STRING, XACML3.ID_ACTION_ACTION_ID, XACML3.ID_ATTRIBUTE_CATEGORY_ACTION); Map props = toscaPolicy.getProperties(); var user = props.get("user").toString(); var matchUser = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(XACML3.ID_FUNCTION_STRING_EQUAL, user, @@ -119,9 +119,9 @@ public class TutorialTranslator extends StdBaseTranslator { ((Map) permission).get("entity"), XACML3.ID_DATATYPE_STRING, ID_TUTORIAL_ENTITY, XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE); - var matchPermission = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator( - XACML3.ID_FUNCTION_STRING_EQUAL, ((Map) permission).get("permission"), - XACML3.ID_DATATYPE_STRING, ID_TUTORIAL_PERM, XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE); + var matchPermission = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(XACML3.ID_FUNCTION_STRING_EQUAL, + ((Map) permission).get("permission"), XACML3.ID_DATATYPE_STRING, ID_TUTORIAL_PERM, + XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE); anyOf = new AnyOfType(); anyOf.getAllOf().add(ToscaPolicyTranslatorUtils.buildAllOf(matchEntity, matchPermission)); target = new TargetType(); @@ -147,7 +147,7 @@ public class TutorialTranslator extends StdBaseTranslator { @Override public Request convertRequest(DecisionRequest request) { try { - return RequestParser.parseRequest(TutorialRequest.createRequest(request)); + return TutorialRequest.createRequest(request); } catch (IllegalArgumentException | IllegalAccessException | DataTypeException e) { // Empty } @@ -156,15 +156,59 @@ public class TutorialTranslator extends StdBaseTranslator { @Override public DecisionResponse convertResponse(Response xacmlResponse) { + // + // Single or Multi? + // + if (xacmlResponse.getResults().size() > 1) { + return convertMultiResponse(xacmlResponse); + } else { + return convertSingleResponse(xacmlResponse.getResults().iterator().next()); + } + } + + protected DecisionResponse convertSingleResponse(Result xacmlResult) { var decisionResponse = new DecisionResponse(); // // Setup policies // decisionResponse.setPolicies(new HashMap<>()); // - // Iterate through all the results + // Check the result + // + if (xacmlResult.getDecision() == Decision.PERMIT) { + // + // This tutorial will simply set the status to Permit + // + decisionResponse.setStatus(Decision.PERMIT.toString()); + } else { + // + // This tutorial will simply set the status to Deny + // + decisionResponse.setStatus(Decision.DENY.toString()); + } + // + // Add attributes use the default scanAttributes. Note that one + // could override that method and return the structure as desired. + // The attributes returned by default method are in the format + // of XACML syntax. It may be more desirable to map them back to + // the original request name-value. + // + if (booleanReturnAttributes) { + scanAttributes(xacmlResult.getAttributes(), decisionResponse); + } + return decisionResponse; + } + + protected DecisionResponse convertMultiResponse(Response xacmlResponse) { + TutorialResponse decisionResponse = new TutorialResponse(); // + // Setup policies + // + decisionResponse.setPolicies(new HashMap<>()); + decisionResponse.setStatus("multi"); + List permissions = new ArrayList<>(); for (Result xacmlResult : xacmlResponse.getResults()) { + TutorialResponsePermission permission = new TutorialResponsePermission(); // // Check the result // @@ -172,12 +216,12 @@ public class TutorialTranslator extends StdBaseTranslator { // // This tutorial will simply set the status to Permit // - decisionResponse.setStatus(Decision.PERMIT.toString()); + permission.setStatus(Decision.PERMIT.toString()); } else { // // This tutorial will simply set the status to Deny // - decisionResponse.setStatus(Decision.DENY.toString()); + permission.setStatus(Decision.DENY.toString()); } // // Add attributes use the default scanAttributes. Note that one @@ -187,10 +231,22 @@ public class TutorialTranslator extends StdBaseTranslator { // the original request name-value. // if (booleanReturnAttributes) { + // + // Call existing method + // scanAttributes(xacmlResult.getAttributes(), decisionResponse); + // + // Move from overall response to the individual permission + // + permission.setAttributes(decisionResponse.getAttributes()); + decisionResponse.setAttributes(null); } + // + // Add it + // + permissions.add(permission); } - + decisionResponse.setPermissions(permissions); return decisionResponse; } diff --git a/tutorials/tutorial-xacml-application/src/test/java/org/onap/policy/tutorial/tutorial/TutorialApplicationTest.java b/tutorials/tutorial-xacml-application/src/test/java/org/onap/policy/tutorial/tutorial/TutorialApplicationTest.java index 66001260..a6f0e944 100644 --- a/tutorials/tutorial-xacml-application/src/test/java/org/onap/policy/tutorial/tutorial/TutorialApplicationTest.java +++ b/tutorials/tutorial-xacml-application/src/test/java/org/onap/policy/tutorial/tutorial/TutorialApplicationTest.java @@ -25,6 +25,7 @@ import com.att.research.xacml.api.Response; import com.att.research.xacml.api.XACML3; import java.io.File; import java.io.IOException; +import java.util.Map; import java.util.Properties; import java.util.ServiceLoader; import org.apache.commons.lang3.tuple.Pair; @@ -65,8 +66,8 @@ public class TutorialApplicationTest { // Setup our temporary folder // XacmlPolicyUtils.FileCreator myCreator = (String filename) -> policyFolder.newFile(filename); - propertiesFile = XacmlPolicyUtils.copyXacmlPropertiesContents("src/test/resources/xacml.properties", - properties, myCreator); + propertiesFile = XacmlPolicyUtils.copyXacmlPropertiesContents("src/test/resources/xacml.properties", properties, + myCreator); // // Load XacmlApplicationServiceProvider service // @@ -88,21 +89,20 @@ public class TutorialApplicationTest { // we just built for it. // service.initialize(propertiesFile.toPath().getParent(), null); - } - - @Test - public void test() throws CoderException, XacmlApplicationException, IOException { // // Now load the tutorial policies. // TestUtils.loadPolicies("src/test/resources/tutorial-policies.yaml", service); + } + + @Test + public void testSingleDecision() throws CoderException, XacmlApplicationException, IOException { // // Load a Decision request // - DecisionRequest decisionRequest = gson.decode( - TextFileUtils - .getTextFileAsString("src/test/resources/tutorial-decision-request.json"), - DecisionRequest.class); + DecisionRequest decisionRequest = + gson.decode(TextFileUtils.getTextFileAsString("src/test/resources/tutorial-decision-request.json"), + DecisionRequest.class); LOGGER.info("{}", gson.encode(decisionRequest, true)); // // Test a decision - should start with a permit @@ -114,7 +114,7 @@ public class TutorialApplicationTest { // Check that there are attributes // assertThat(decision.getLeft().getAttributes()).isNotNull().hasSize(1) - .containsKey(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE.stringValue()); + .containsKey(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE.stringValue()); // // This should be a deny // @@ -127,7 +127,73 @@ public class TutorialApplicationTest { // Check that there are attributes // assertThat(decision.getLeft().getAttributes()).isNotNull().hasSize(1) - .containsKey(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE.stringValue()); + .containsKey(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE.stringValue()); } + + @Test + public void testMultiDecision() throws CoderException, XacmlApplicationException, IOException { + // + // Load a Decision request + // + DecisionRequest decisionRequest = gson.decode( + TextFileUtils.getTextFileAsString("src/test/resources/tutorial-decision-multi-request.json"), + DecisionRequest.class); + LOGGER.info("{}", gson.encode(decisionRequest, true)); + // + // Test a decision - should start with a permit + // + Pair decision = service.makeDecision(decisionRequest, null); + LOGGER.info("{}", gson.encode(decision.getLeft(), true)); + assertEquals("multi", decision.getLeft().getStatus()); + // + // Check that there no attributes for the overall response + // + assertThat(decision.getLeft().getAttributes()).isNull(); + // + // Check that there are 7 decisions with attributes + // + assertThat(decision.getLeft()).isInstanceOf(TutorialResponse.class); + TutorialResponse tutorialResponse = (TutorialResponse) decision.getLeft(); + assertThat(tutorialResponse.getPermissions()).hasSize(7); + tutorialResponse.getPermissions().forEach(p -> checkPermission(p)); + } + + private void checkPermission(TutorialResponsePermission permission) { + assertThat(permission.getAttributes()).hasSize(1); + Object resourceAttributes = permission.getAttributes().get(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE.stringValue()); + assertThat(resourceAttributes).isNotNull().isInstanceOf(Map.class); + @SuppressWarnings("unchecked") + String multiId = ((Map) resourceAttributes).get("urn:org:onap:tutorial-multi-id").toString(); + assertThat(Integer.parseInt(multiId)).isBetween(1, 7); + switch (multiId) { + case "1": + assertThat(permission.getStatus()).isEqualTo("Permit"); + return; + case "2": + assertThat(permission.getStatus()).isEqualTo("Permit"); + return; + case "3": + assertThat(permission.getStatus()).isEqualTo("Deny"); + return; + case "4": + assertThat(permission.getStatus()).isEqualTo("Permit"); + return; + case "5": + assertThat(permission.getStatus()).isEqualTo("Deny"); + return; + case "6": + assertThat(permission.getStatus()).isEqualTo("Deny"); + return; + case "7": + assertThat(permission.getStatus()).isEqualTo("Deny"); + return; + default: + // + // Should not get here as we check the value range in line 168. + // But CodeStyle wants a default. + // + break; + } + } } diff --git a/tutorials/tutorial-xacml-application/src/test/resources/tutorial-decision-multi-request.json b/tutorials/tutorial-xacml-application/src/test/resources/tutorial-decision-multi-request.json new file mode 100644 index 00000000..9a7425d5 --- /dev/null +++ b/tutorials/tutorial-xacml-application/src/test/resources/tutorial-decision-multi-request.json @@ -0,0 +1,53 @@ +{ + "ONAPName": "TutorialPEP", + "ONAPComponent": "TutorialPEPComponent", + "ONAPInstance": "TutorialPEPInstance", + "requestId": "unique-request-id-tutorial", + "action": "authorize", + "resource": { + "users": [ + { + "multiId": 1, + "user": "demo", + "entity": "foo", + "permission" : "write" + }, + { + "multiId": 2, + "user": "demo", + "entity": "foo", + "permission" : "read" + }, + { + "multiId": 3, + "user": "audit", + "entity": "foo", + "permission" : "write" + }, + { + "multiId": 4, + "user": "audit", + "entity": "foo", + "permission" : "read" + }, + { + "multiId": 5, + "user": "demo", + "entity": "bar", + "permission" : "read" + }, + { + "multiId": 6, + "user": "demo", + "entity": "bar", + "permission" : "admin" + }, + { + "multiId": 7, + "user": "ceo", + "entity": "bar", + "permission" : "read" + } + ] + } +}