Build XACML PDP support for native xacml policy type 27/102627/1
authorChenfei Gao <cgao@research.att.com>
Fri, 28 Feb 2020 19:45:48 +0000 (14:45 -0500)
committerChenfei Gao <cgao@research.att.com>
Fri, 28 Feb 2020 19:45:55 +0000 (14:45 -0500)
Added a new native application to the service loader framework
Added a new translator for the native application
Added custom serialization providers for xacml+json and xacml+xml
Added a new endpoint for native xacml decision api
Added a new api provider function to handle the native xacml api calls
Added corresponding junit tests

Issue-ID: POLICY-2182
Change-Id: I30fa4637612c324d543f9952386cf1a27a52d76c
Signed-off-by: Chenfei Gao <cgao@research.att.com>
30 files changed:
applications/native/pom.xml [new file with mode: 0644]
applications/native/src/main/java/org/onap/policy/xacml/pdp/application/nativ/NativePdpApplication.java [new file with mode: 0644]
applications/native/src/main/java/org/onap/policy/xacml/pdp/application/nativ/NativePdpApplicationTranslator.java [new file with mode: 0644]
applications/native/src/main/resources/META-INF/services/org.onap.policy.pdp.xacml.application.common.XacmlApplicationServiceProvider [new file with mode: 0644]
applications/native/src/test/java/org/onap/policy/xacml/pdp/application/nativ/NativePdpApplicationTest.java [new file with mode: 0644]
applications/native/src/test/resources/policies/native.policy.xml [new file with mode: 0644]
applications/native/src/test/resources/policies/native.policy.yaml [new file with mode: 0644]
applications/native/src/test/resources/requests/native.policy.request.xml [new file with mode: 0644]
applications/native/src/test/resources/xacml.properties [new file with mode: 0644]
applications/pom.xml
main/pom.xml
main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpApplicationManager.java
main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpRestController.java
main/src/main/java/org/onap/policy/pdpx/main/rest/provider/DecisionProvider.java
main/src/main/java/org/onap/policy/pdpx/main/rest/serialization/XacmlJsonExceptionMapper.java [new file with mode: 0644]
main/src/main/java/org/onap/policy/pdpx/main/rest/serialization/XacmlJsonMessageBodyHandler.java [new file with mode: 0644]
main/src/main/java/org/onap/policy/pdpx/main/rest/serialization/XacmlXmlExceptionMapper.java [new file with mode: 0644]
main/src/main/java/org/onap/policy/pdpx/main/rest/serialization/XacmlXmlMessageBodyHandler.java [new file with mode: 0644]
main/src/main/java/org/onap/policy/pdpx/main/startstop/XacmlPdpActivator.java
main/src/main/java/org/onap/policy/pdpx/main/startstop/XacmlPdpRestServer.java [new file with mode: 0644]
main/src/test/java/org/onap/policy/pdpx/main/rest/TestDecision.java
main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/CommonSerialization.java [new file with mode: 0644]
main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/TestXacmlJsonExceptionMapper.java [new file with mode: 0644]
main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/TestXacmlJsonMessageBodyHandler.java [new file with mode: 0644]
main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/TestXacmlXmlExceptionMapper.java [new file with mode: 0644]
main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/TestXacmlXmlMessageBodyHandler.java [new file with mode: 0644]
main/src/test/resources/apps/native/xacml.properties [new file with mode: 0644]
main/src/test/resources/decisions/decision.native.request.xml [new file with mode: 0644]
main/src/test/resources/decisions/decision.native.response.xml [new file with mode: 0644]
main/src/test/resources/decisions/decision.single.output.json [moved from main/src/test/resources/decisions/decsion.single.output.json with 100% similarity]

diff --git a/applications/native/pom.xml b/applications/native/pom.xml
new file mode 100644 (file)
index 0000000..f65a7d3
--- /dev/null
@@ -0,0 +1,28 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.onap.policy.xacml-pdp.applications</groupId>
+    <artifactId>applications</artifactId>
+    <version>2.2.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>native</artifactId>
+
+  <name>${project.artifactId}</name>
+  <description>This modules contains the xacml application that evaluates native policies and requests.</description>
+  
+  <dependencies>
+      <dependency>
+          <groupId>org.onap.policy.xacml-pdp.applications</groupId>
+          <artifactId>common</artifactId>
+          <version>${project.version}</version>
+      </dependency>
+      <dependency>
+          <groupId>org.onap.policy.xacml-pdp</groupId>
+          <artifactId>xacml-test</artifactId>
+          <version>${project.version}</version>
+          <scope>test</scope>
+      </dependency>
+  </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/applications/native/src/main/java/org/onap/policy/xacml/pdp/application/nativ/NativePdpApplication.java b/applications/native/src/main/java/org/onap/policy/xacml/pdp/application/nativ/NativePdpApplication.java
new file mode 100644 (file)
index 0000000..0d862d1
--- /dev/null
@@ -0,0 +1,78 @@
+/*-
+ * ============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.xacml.pdp.application.nativ;
+
+import com.att.research.xacml.api.Request;
+import com.att.research.xacml.api.Response;
+import java.util.Arrays;
+import java.util.List;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
+import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslator;
+import org.onap.policy.pdp.xacml.application.common.std.StdXacmlApplicationServiceProvider;
+
+/**
+ * This class implements an application that handles onap.policies.native.Xacml policies.
+ *
+ * @author Chenfei Gao (cgao@research.att.com)
+ *
+ */
+public class NativePdpApplication extends StdXacmlApplicationServiceProvider {
+
+    private static final ToscaPolicyTypeIdentifier supportedPolicyType = new ToscaPolicyTypeIdentifier(
+            "onap.policies.native.Xacml", "1.0.0");
+    private NativePdpApplicationTranslator translator = new NativePdpApplicationTranslator();
+
+    @Override
+    public String applicationName() {
+        return "native";
+    }
+
+    @Override
+    public List<String> actionDecisionsSupported() {
+        return Arrays.asList("native");
+    }
+
+    @Override
+    public synchronized List<ToscaPolicyTypeIdentifier> supportedPolicyTypes() {
+        return Arrays.asList(supportedPolicyType);
+    }
+
+    @Override
+    public boolean canSupportPolicyType(ToscaPolicyTypeIdentifier policyTypeId) {
+        return supportedPolicyType.equals(policyTypeId);
+    }
+
+    @Override
+    protected ToscaPolicyTranslator getTranslator(String type) {
+        return translator;
+    }
+
+    /**
+     * Makes decision for the incoming native xacml request.
+     * @param request the native xacml request
+     * @return the native xacml response
+     */
+    public Response makeNativeDecision(Request request) {
+        return this.xacmlDecision(request);
+    }
+}
diff --git a/applications/native/src/main/java/org/onap/policy/xacml/pdp/application/nativ/NativePdpApplicationTranslator.java b/applications/native/src/main/java/org/onap/policy/xacml/pdp/application/nativ/NativePdpApplicationTranslator.java
new file mode 100644 (file)
index 0000000..98a1c65
--- /dev/null
@@ -0,0 +1,112 @@
+/*-
+ * ============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.xacml.pdp.application.nativ;
+
+import com.att.research.xacml.api.Request;
+import com.att.research.xacml.api.Response;
+import com.att.research.xacml.util.XACMLPolicyScanner;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.Map;
+import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType;
+import org.onap.policy.models.decisions.concepts.DecisionRequest;
+import org.onap.policy.models.decisions.concepts.DecisionResponse;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
+import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException;
+import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class implements one translator that interprets TOSCA policy and decision API request/response payload.
+ *
+ * @author Chenfei Gao (cgao@research.att.com)
+ *
+ */
+public class NativePdpApplicationTranslator implements ToscaPolicyTranslator {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(NativePdpApplicationTranslator.class);
+    private static final String POLICY = "policy";
+
+    public NativePdpApplicationTranslator() {
+        super();
+    }
+
+    @Override
+    public PolicyType convertPolicy(ToscaPolicy toscaPolicy) throws ToscaPolicyConversionException {
+        //
+        // Extract the Base64 encoded policy xml string and decode it
+        //
+        String encodedXacmlPolicy = getNativeXacmlPolicy(toscaPolicy);
+        String decodedXacmlPolicy;
+        try {
+            decodedXacmlPolicy = new String(Base64.getDecoder().decode(encodedXacmlPolicy), StandardCharsets.UTF_8);
+        } catch (IllegalArgumentException exc) {
+            throw new ToscaPolicyConversionException("error on Base64 decoding the native policy", exc);
+        }
+        LOGGER.debug("Decoded xacml policy {}",decodedXacmlPolicy);
+        //
+        // Scan the string and convert to xacml PolicyType
+        //
+        try (InputStream is = new ByteArrayInputStream(decodedXacmlPolicy.getBytes(StandardCharsets.UTF_8))) {
+            //
+            // Here we assume it is PolicyType, not PolicySetType
+            // PolicySetType will be addressed later
+            //
+            return (PolicyType) XACMLPolicyScanner.readPolicy(is);
+        } catch (IOException exc) {
+            throw new ToscaPolicyConversionException("Failed to read policy", exc);
+        }
+    }
+
+    private String getNativeXacmlPolicy(ToscaPolicy toscaPolicy) throws ToscaPolicyConversionException {
+
+        Map<String, Object> propertyMap = toscaPolicy.getProperties();
+        if (propertyMap.isEmpty() || !propertyMap.containsKey(POLICY)) {
+            throw new ToscaPolicyConversionException("no xacml native policy found in the tosca policy");
+        }
+
+        String nativePolicyString = propertyMap.get(POLICY).toString();
+        LOGGER.debug("Base64 encoded native xacml policy {}", nativePolicyString);
+        return nativePolicyString;
+    }
+
+    @Override
+    public Request convertRequest(DecisionRequest request) {
+        //
+        // We do nothing to DecisionRequest for native xacml application
+        //
+        return null;
+    }
+
+    @Override
+    public DecisionResponse convertResponse(Response xacmlResponse) {
+        //
+        // We do nothing to DecisionResponse for native xacml application
+        //
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/applications/native/src/main/resources/META-INF/services/org.onap.policy.pdp.xacml.application.common.XacmlApplicationServiceProvider b/applications/native/src/main/resources/META-INF/services/org.onap.policy.pdp.xacml.application.common.XacmlApplicationServiceProvider
new file mode 100644 (file)
index 0000000..480ad05
--- /dev/null
@@ -0,0 +1 @@
+org.onap.policy.xacml.pdp.application.nativ.NativePdpApplication
\ No newline at end of file
diff --git a/applications/native/src/test/java/org/onap/policy/xacml/pdp/application/nativ/NativePdpApplicationTest.java b/applications/native/src/test/java/org/onap/policy/xacml/pdp/application/nativ/NativePdpApplicationTest.java
new file mode 100644 (file)
index 0000000..b25c2a3
--- /dev/null
@@ -0,0 +1,163 @@
+/*-
+ * ============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.xacml.pdp.application.nativ;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import com.att.research.xacml.api.Decision;
+import com.att.research.xacml.api.Request;
+import com.att.research.xacml.api.Response;
+import com.att.research.xacml.std.dom.DOMRequest;
+import com.att.research.xacml.std.dom.DOMResponse;
+import java.io.File;
+import java.util.Properties;
+import java.util.ServiceLoader;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.onap.policy.common.endpoints.parameters.RestServerParameters;
+import org.onap.policy.common.utils.resources.TextFileUtils;
+import org.onap.policy.pdp.xacml.application.common.XacmlApplicationServiceProvider;
+import org.onap.policy.pdp.xacml.application.common.XacmlPolicyUtils;
+import org.onap.policy.pdp.xacml.xacmltest.TestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NativePdpApplicationTest {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(NativePdpApplicationTest.class);
+    private static final String PERMIT = "Permit";
+    private static Properties properties = new Properties();
+    private static File propertiesFile;
+    private static RestServerParameters clientParams = new RestServerParameters();
+    private static NativePdpApplication service;
+    private static Request request;
+
+    @ClassRule
+    public static final TemporaryFolder policyFolder = new TemporaryFolder();
+
+    /**
+     * Copies the xacml.properties and policies files into
+     * temporary folder and loads the service provider saving
+     * instance of provider off for other tests to use.
+     */
+    @BeforeClass
+    public static void setup() throws Exception {
+        LOGGER.info("Setting up class");
+        //
+        // Setup our temporary folder
+        //
+        XacmlPolicyUtils.FileCreator myCreator = (filename) -> policyFolder.newFile(filename);
+        propertiesFile = XacmlPolicyUtils.copyXacmlPropertiesContents("src/test/resources/xacml.properties",
+                properties, myCreator);
+        //
+        // Load service
+        //
+        ServiceLoader<XacmlApplicationServiceProvider> applicationLoader =
+                ServiceLoader.load(XacmlApplicationServiceProvider.class);
+        //
+        // Find the native application and save for use in all the tests
+        //
+        StringBuilder strDump = new StringBuilder("Loaded applications:" + XacmlPolicyUtils.LINE_SEPARATOR);
+        for (XacmlApplicationServiceProvider application : applicationLoader) {
+            //
+            // Is it our service?
+            //
+            if (application instanceof NativePdpApplication) {
+                //
+                // Should be the first and only one
+                //
+                assertThat(service).isNull();
+                service = (NativePdpApplication) application;
+            }
+            strDump.append(application.applicationName());
+            strDump.append(" supports ");
+            strDump.append(application.supportedPolicyTypes());
+            strDump.append(XacmlPolicyUtils.LINE_SEPARATOR);
+        }
+        LOGGER.info("{}", strDump);
+        //
+        // Tell it to initialize based on the properties file
+        // we just built for it.
+        //
+        service.initialize(propertiesFile.toPath().getParent(), clientParams);
+        //
+        // Load XACML Request
+        //
+        request = DOMRequest.load(
+                TextFileUtils.getTextFileAsString(
+                        "src/test/resources/requests/native.policy.request.xml"));
+    }
+
+    @Test
+    public void testNativePolicy() throws Exception {
+
+        LOGGER.info("*********** Running native policy test *************");
+        //
+        // Now load the TOSCA compliant native policy - make sure
+        // the pdp can support it and have it load into the PDP.
+        //
+        TestUtils.loadPolicies("src/test/resources/policies/native.policy.yaml", service);
+        //
+        // Send the request and verify decision result
+        //
+        requestAndCheckDecision(request, PERMIT);
+    }
+
+    /**
+     * Request a decision and check that it matches expectation.
+     *
+     * @param request to send to XACML PDP
+     * @param expected from the response
+     * @throws Exception on errors requesting a decision and checking the returned decision
+     *
+     **/
+    private void requestAndCheckDecision(Request request, String expected) throws Exception {
+        //
+        // Ask for a decision
+        //
+        Response decision = service.makeNativeDecision(request);
+        //
+        // Check decision
+        //
+        checkDecision(expected, decision);
+    }
+
+    /**
+     * Check that decision matches expectation.
+     *
+     * @param expected from the response
+     * @param response received
+     * @throws Exception on errors checking the decision
+     *
+     **/
+    public void checkDecision(String expected, Response response) throws Exception {
+        LOGGER.info("Looking for {} Decision", expected);
+        assertThat(response).isNotNull();
+        Decision decision = response.getResults().iterator().next().getDecision();
+        assertThat(decision).isNotNull();
+        assertThat(decision.toString()).isEqualTo(expected);
+        LOGGER.info("Xacml response we received {}", DOMResponse.toString(response));
+    }
+}
\ No newline at end of file
diff --git a/applications/native/src/test/resources/policies/native.policy.xml b/applications/native/src/test/resources/policies/native.policy.xml
new file mode 100644 (file)
index 0000000..d6e4f4f
--- /dev/null
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<Policy xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" PolicyId="urn:oasis:names:tc:xacml:2.0:conformance-test:IIA1:policy" RuleCombiningAlgId="urn:oasis:names:tc:xacml:3.0:rule-combining-algorithm:deny-overrides" Version="1.0" xsi:schemaLocation="urn:oasis:names:tc:xacml:3.0:policy:schema:os access_control-xacml-2.0-policy-schema-os.xsd">
+    <Description>
+        Policy for Conformance Test IIA001.
+    </Description>
+    <Target/>
+    <Rule Effect="Permit" RuleId="urn:oasis:names:tc:xacml:2.0:conformance-test:IIA1:rule">
+        <Description>
+            Julius Hibbert can read or write Bart Simpson's medical record.
+        </Description>
+        <Target>
+            <AnyOf>
+                <AllOf>
+                    <Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
+                        <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">Julius Hibbert</AttributeValue>
+                        <AttributeDesignator AttributeId="urn:oasis:names:tc:xacml:1.0:subject:subject-id" Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="false"/>
+                    </Match>
+                </AllOf>
+            </AnyOf>
+            <AnyOf>
+                <AllOf>
+                    <Match MatchId="urn:oasis:names:tc:xacml:1.0:function:anyURI-equal">
+                        <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#anyURI">http://medico.com/record/patient/BartSimpson</AttributeValue>
+                        <AttributeDesignator AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id" Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource" DataType="http://www.w3.org/2001/XMLSchema#anyURI" MustBePresent="false"/>
+                    </Match>
+                </AllOf>
+            </AnyOf>
+            <AnyOf>
+                <AllOf>
+                    <Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
+                        <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">read</AttributeValue>
+                        <AttributeDesignator AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id" Category="urn:oasis:names:tc:xacml:3.0:attribute-category:action" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="false"/>
+                    </Match>
+                </AllOf>
+                <AllOf>
+                    <Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
+                        <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">write</AttributeValue>
+                        <AttributeDesignator AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id" Category="urn:oasis:names:tc:xacml:3.0:attribute-category:action" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="false"/>
+                    </Match>
+                </AllOf>
+            </AnyOf>
+        </Target>
+    </Rule>
+</Policy>
diff --git a/applications/native/src/test/resources/policies/native.policy.yaml b/applications/native/src/test/resources/policies/native.policy.yaml
new file mode 100644 (file)
index 0000000..00bc5db
--- /dev/null
@@ -0,0 +1,12 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+topology_template:
+  policies:
+    -
+      native.access.control:
+        type: onap.policies.native.Xacml
+        version: 1.0.0
+        metadata:
+          policy-id: native.access.control
+          policy-version: 1
+        properties:
+          policy: PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxQb2xpY3kgeG1sbnM9InVybjpvYXNpczpuYW1lczp0Yzp4YWNtbDozLjA6Y29yZTpzY2hlbWE6d2QtMTciIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIFBvbGljeUlkPSJ1cm46b2FzaXM6bmFtZXM6dGM6eGFjbWw6Mi4wOmNvbmZvcm1hbmNlLXRlc3Q6SUlBMTpwb2xpY3kiIFJ1bGVDb21iaW5pbmdBbGdJZD0idXJuOm9hc2lzOm5hbWVzOnRjOnhhY21sOjMuMDpydWxlLWNvbWJpbmluZy1hbGdvcml0aG06ZGVueS1vdmVycmlkZXMiIFZlcnNpb249IjEuMCIgeHNpOnNjaGVtYUxvY2F0aW9uPSJ1cm46b2FzaXM6bmFtZXM6dGM6eGFjbWw6My4wOnBvbGljeTpzY2hlbWE6b3MgYWNjZXNzX2NvbnRyb2wteGFjbWwtMi4wLXBvbGljeS1zY2hlbWEtb3MueHNkIj4KICAgIDxEZXNjcmlwdGlvbj4KICAgICAgICBQb2xpY3kgZm9yIENvbmZvcm1hbmNlIFRlc3QgSUlBMDAxLgogICAgPC9EZXNjcmlwdGlvbj4KICAgIDxUYXJnZXQvPgogICAgPFJ1bGUgRWZmZWN0PSJQZXJtaXQiIFJ1bGVJZD0idXJuOm9hc2lzOm5hbWVzOnRjOnhhY21sOjIuMDpjb25mb3JtYW5jZS10ZXN0OklJQTE6cnVsZSI+CiAgICAgICAgPERlc2NyaXB0aW9uPgogICAgICAgICAgICBKdWxpdXMgSGliYmVydCBjYW4gcmVhZCBvciB3cml0ZSBCYXJ0IFNpbXBzb24ncyBtZWRpY2FsIHJlY29yZC4KICAgICAgICA8L0Rlc2NyaXB0aW9uPgogICAgICAgIDxUYXJnZXQ+CiAgICAgICAgICAgIDxBbnlPZj4KICAgICAgICAgICAgICAgIDxBbGxPZj4KICAgICAgICAgICAgICAgICAgICA8TWF0Y2ggTWF0Y2hJZD0idXJuOm9hc2lzOm5hbWVzOnRjOnhhY21sOjEuMDpmdW5jdGlvbjpzdHJpbmctZXF1YWwiPgogICAgICAgICAgICAgICAgICAgICAgICA8QXR0cmlidXRlVmFsdWUgRGF0YVR5cGU9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hI3N0cmluZyI+SnVsaXVzIEhpYmJlcnQ8L0F0dHJpYnV0ZVZhbHVlPgogICAgICAgICAgICAgICAgICAgICAgICA8QXR0cmlidXRlRGVzaWduYXRvciBBdHRyaWJ1dGVJZD0idXJuOm9hc2lzOm5hbWVzOnRjOnhhY21sOjEuMDpzdWJqZWN0OnN1YmplY3QtaWQiIENhdGVnb3J5PSJ1cm46b2FzaXM6bmFtZXM6dGM6eGFjbWw6MS4wOnN1YmplY3QtY2F0ZWdvcnk6YWNjZXNzLXN1YmplY3QiIERhdGFUeXBlPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSNzdHJpbmciIE11c3RCZVByZXNlbnQ9ImZhbHNlIi8+CiAgICAgICAgICAgICAgICAgICAgPC9NYXRjaD4KICAgICAgICAgICAgICAgIDwvQWxsT2Y+CiAgICAgICAgICAgIDwvQW55T2Y+CiAgICAgICAgICAgIDxBbnlPZj4KICAgICAgICAgICAgICAgIDxBbGxPZj4KICAgICAgICAgICAgICAgICAgICA8TWF0Y2ggTWF0Y2hJZD0idXJuOm9hc2lzOm5hbWVzOnRjOnhhY21sOjEuMDpmdW5jdGlvbjphbnlVUkktZXF1YWwiPgogICAgICAgICAgICAgICAgICAgICAgICA8QXR0cmlidXRlVmFsdWUgRGF0YVR5cGU9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hI2FueVVSSSI+aHR0cDovL21lZGljby5jb20vcmVjb3JkL3BhdGllbnQvQmFydFNpbXBzb248L0F0dHJpYnV0ZVZhbHVlPgogICAgICAgICAgICAgICAgICAgICAgICA8QXR0cmlidXRlRGVzaWduYXRvciBBdHRyaWJ1dGVJZD0idXJuOm9hc2lzOm5hbWVzOnRjOnhhY21sOjEuMDpyZXNvdXJjZTpyZXNvdXJjZS1pZCIgQ2F0ZWdvcnk9InVybjpvYXNpczpuYW1lczp0Yzp4YWNtbDozLjA6YXR0cmlidXRlLWNhdGVnb3J5OnJlc291cmNlIiBEYXRhVHlwZT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEjYW55VVJJIiBNdXN0QmVQcmVzZW50PSJmYWxzZSIvPgogICAgICAgICAgICAgICAgICAgIDwvTWF0Y2g+CiAgICAgICAgICAgICAgICA8L0FsbE9mPgogICAgICAgICAgICA8L0FueU9mPgogICAgICAgICAgICA8QW55T2Y+CiAgICAgICAgICAgICAgICA8QWxsT2Y+CiAgICAgICAgICAgICAgICAgICAgPE1hdGNoIE1hdGNoSWQ9InVybjpvYXNpczpuYW1lczp0Yzp4YWNtbDoxLjA6ZnVuY3Rpb246c3RyaW5nLWVxdWFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgPEF0dHJpYnV0ZVZhbHVlIERhdGFUeXBlPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSNzdHJpbmciPnJlYWQ8L0F0dHJpYnV0ZVZhbHVlPgogICAgICAgICAgICAgICAgICAgICAgICA8QXR0cmlidXRlRGVzaWduYXRvciBBdHRyaWJ1dGVJZD0idXJuOm9hc2lzOm5hbWVzOnRjOnhhY21sOjEuMDphY3Rpb246YWN0aW9uLWlkIiBDYXRlZ29yeT0idXJuOm9hc2lzOm5hbWVzOnRjOnhhY21sOjMuMDphdHRyaWJ1dGUtY2F0ZWdvcnk6YWN0aW9uIiBEYXRhVHlwZT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEjc3RyaW5nIiBNdXN0QmVQcmVzZW50PSJmYWxzZSIvPgogICAgICAgICAgICAgICAgICAgIDwvTWF0Y2g+CiAgICAgICAgICAgICAgICA8L0FsbE9mPgogICAgICAgICAgICAgICAgPEFsbE9mPgogICAgICAgICAgICAgICAgICAgIDxNYXRjaCBNYXRjaElkPSJ1cm46b2FzaXM6bmFtZXM6dGM6eGFjbWw6MS4wOmZ1bmN0aW9uOnN0cmluZy1lcXVhbCI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxBdHRyaWJ1dGVWYWx1ZSBEYXRhVHlwZT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEjc3RyaW5nIj53cml0ZTwvQXR0cmlidXRlVmFsdWU+CiAgICAgICAgICAgICAgICAgICAgICAgIDxBdHRyaWJ1dGVEZXNpZ25hdG9yIEF0dHJpYnV0ZUlkPSJ1cm46b2FzaXM6bmFtZXM6dGM6eGFjbWw6MS4wOmFjdGlvbjphY3Rpb24taWQiIENhdGVnb3J5PSJ1cm46b2FzaXM6bmFtZXM6dGM6eGFjbWw6My4wOmF0dHJpYnV0ZS1jYXRlZ29yeTphY3Rpb24iIERhdGFUeXBlPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSNzdHJpbmciIE11c3RCZVByZXNlbnQ9ImZhbHNlIi8+CiAgICAgICAgICAgICAgICAgICAgPC9NYXRjaD4KICAgICAgICAgICAgICAgIDwvQWxsT2Y+CiAgICAgICAgICAgIDwvQW55T2Y+CiAgICAgICAgPC9UYXJnZXQ+CiAgICA8L1J1bGU+CjwvUG9saWN5Pgo=
\ No newline at end of file
diff --git a/applications/native/src/test/resources/requests/native.policy.request.xml b/applications/native/src/test/resources/requests/native.policy.request.xml
new file mode 100644 (file)
index 0000000..41dcf18
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Request xsi:schemaLocation="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17 http://docs.oasis-open.org/xacml/3.0/xacml-core-v3-schema-wd-17.xsd" ReturnPolicyIdList="false" CombinedDecision="false" xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <Attributes Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject">
+    <Attribute IncludeInResult="false" AttributeId="urn:oasis:names:tc:xacml:1.0:subject:subject-id">
+      <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">Julius Hibbert</AttributeValue>
+    </Attribute>
+  </Attributes>
+  <Attributes Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource">
+    <Attribute IncludeInResult="false" AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id">
+      <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#anyURI">http://medico.com/record/patient/BartSimpson</AttributeValue>
+    </Attribute>
+  </Attributes>
+  <Attributes Category="urn:oasis:names:tc:xacml:3.0:attribute-category:action">
+    <Attribute IncludeInResult="false" AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id">
+      <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">read</AttributeValue>
+    </Attribute>
+  </Attributes>
+  <Attributes Category="urn:oasis:names:tc:xacml:3.0:attribute-category:environment" />
+</Request>
diff --git a/applications/native/src/test/resources/xacml.properties b/applications/native/src/test/resources/xacml.properties
new file mode 100644 (file)
index 0000000..3d4d025
--- /dev/null
@@ -0,0 +1,53 @@
+#
+# Properties that the embedded PDP engine uses to configure and load
+#
+# Standard API Factories
+#
+xacml.dataTypeFactory=com.att.research.xacml.std.StdDataTypeFactory
+xacml.pdpEngineFactory=com.att.research.xacmlatt.pdp.ATTPDPEngineFactory
+xacml.pepEngineFactory=com.att.research.xacml.std.pep.StdEngineFactory
+xacml.pipFinderFactory=com.att.research.xacml.std.pip.StdPIPFinderFactory
+xacml.traceEngineFactory=com.att.research.xacml.std.trace.LoggingTraceEngineFactory
+#
+# AT&T PDP Implementation Factories
+#
+xacml.att.evaluationContextFactory=com.att.research.xacmlatt.pdp.std.StdEvaluationContextFactory
+xacml.att.combiningAlgorithmFactory=com.att.research.xacmlatt.pdp.std.StdCombiningAlgorithmFactory
+xacml.att.functionDefinitionFactory=com.att.research.xacmlatt.pdp.std.StdFunctionDefinitionFactory
+#
+# ONAP PDP Implementation Factories
+#
+xacml.att.policyFinderFactory=org.onap.policy.pdp.xacml.application.common.OnapPolicyFinderFactory
+
+#
+# Use a root combining algorithm
+#
+xacml.att.policyFinderFactory.combineRootPolicies=urn:oasis:names:tc:xacml:3.0:policy-combining-algorithm:deny-overrides
+
+#
+# PIP Engine Definitions
+#
+count-recent-operations.classname=org.onap.policy.pdp.xacml.application.common.operationshistory.CountRecentOperationsPip
+count-recent-operations.issuer=urn:org:onap:xacml:guard:count-recent-operations
+count-recent-operations.name=CountRecentOperations
+count-recent-operations.description=Returns operation counts based on time window
+count-recent-operations.persistenceunit=OperationsHistoryPUTest
+
+get-operation-outcome.classname=org.onap.policy.pdp.xacml.application.common.operationshistory.GetOperationOutcomePip
+get-operation-outcome.issuer=urn:org:onap:xacml:guard:get-operation-outcome
+get-operation-outcome.name=GetOperationOutcome
+get-operation-outcome.description=Returns operation outcome
+get-operation-outcome.persistenceunit=OperationsHistoryPUTest
+
+#
+# Make pips available to finder
+#
+xacml.pip.engines=count-recent-operations,get-operation-outcome
+
+#
+# JPA Properties
+#
+javax.persistence.jdbc.driver=org.h2.Driver
+javax.persistence.jdbc.url=jdbc:h2:mem:testdb;DATABASE_TO_UPPER=FALSE
+javax.persistence.jdbc.user=policy
+javax.persistence.jdbc.password=P01icY
index c1af6b8..d6deba6 100644 (file)
@@ -41,6 +41,7 @@
     <module>guard</module>
     <module>optimization</module>
     <module>naming</module>
+    <module>native</module>
   </modules>
 
 
index b6026e9..32d0a43 100644 (file)
             <artifactId>naming</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.onap.policy.xacml-pdp.applications</groupId>
+            <artifactId>native</artifactId>
+            <version>${project.version}</version>
+        </dependency>
         <dependency>
             <groupId>org.onap.policy.models</groupId>
             <artifactId>policy-models-pdp</artifactId>
index 2f054bd..0e4bef2 100644 (file)
@@ -118,6 +118,10 @@ public class XacmlPdpApplicationManager {
         return providerActionMap.get(request.getAction());
     }
 
+    public XacmlApplicationServiceProvider findNativeApplication() {
+        return providerActionMap.get("native");
+    }
+
     /**
      * getToscaPolicies.
      *
index f668411..ad6784b 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2019-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.
@@ -20,6 +20,7 @@
 
 package org.onap.policy.pdpx.main.rest;
 
+import com.att.research.xacml.api.Request;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
@@ -73,7 +74,8 @@ import org.slf4j.LoggerFactory;
 public class XacmlPdpRestController {
     private static final Logger LOGGER = LoggerFactory.getLogger(XacmlPdpRestController.class);
     public static final String APPLICATION_YAML = "application/yaml";
-
+    public static final String APPLICATION_XACML_JSON = "application/xacml+json";
+    public static final String APPLICATION_XACML_XML = "application/xacml+xml";
 
     @GET
     @Path("/healthcheck")
@@ -188,6 +190,57 @@ public class XacmlPdpRestController {
         }
     }
 
+    /**
+     * Our native decision entry point.
+     *
+     * @param body Should be an Xacml Request object
+     * @param requestId Unique request id
+     * @return Xacml Response or ErrorResponse object
+     */
+    @POST
+    @Path("/xacml")
+    @Produces({XacmlPdpRestController.APPLICATION_XACML_JSON, XacmlPdpRestController.APPLICATION_XACML_XML})
+    @Consumes({XacmlPdpRestController.APPLICATION_XACML_JSON, XacmlPdpRestController.APPLICATION_XACML_XML})
+    @ApiOperation(value = "Fetch the decision using specified decision parameters",
+            notes = "Returns the policy decision from Policy Xacml PDP",
+            response = com.att.research.xacml.api.Response.class,
+            responseHeaders = {
+                    @ResponseHeader(name = "X-MinorVersion",
+                            description = "Used to request or communicate a MINOR version back from the client"
+                                    + " to the server, and from the server back to the client",
+                            response = String.class),
+                    @ResponseHeader(name = "X-PatchVersion",
+                            description = "Used only to communicate a PATCH version in a response for"
+                                    + " troubleshooting purposes only, and will not be provided by"
+                                    + " the client on request",
+                            response = String.class),
+                    @ResponseHeader(name = "X-LatestVersion",
+                            description = "Used only to communicate an API's latest version", response = String.class),
+                    @ResponseHeader(name = "X-ONAP-RequestID",
+                            description = "Used to track REST transactions for logging purpose",
+                            response = UUID.class)},
+            authorizations = @Authorization(value = "basicAuth"), tags = {"Decision",},
+            extensions = {@Extension(name = "interface info",
+                    properties = {@ExtensionProperty(name = "pdpx-version", value = "1.0.0"),
+                            @ExtensionProperty(name = "last-mod-release", value = "Frankfurt")})})
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Bad Request", response = ErrorResponse.class),
+            @ApiResponse(code = 401, message = "Authentication Error"),
+            @ApiResponse(code = 403, message = "Authorization Error"),
+            @ApiResponse(code = 500, message = "Internal Server Error")})
+    public Response xacml(Request body,
+            @HeaderParam("X-ONAP-RequestID") @ApiParam("RequestID for http transaction") UUID requestId) {
+        try {
+            return addLoggingHeaders(addVersionControlHeaders(Response.status(Response.Status.OK)), requestId)
+                    .entity(new DecisionProvider().fetchNativeDecision(body)).build();
+        } catch (DecisionException e) {
+            LOGGER.error("Decision exception", e);
+            XacmlPdpStatisticsManager.getCurrent().updateErrorCount();
+            return addLoggingHeaders(
+                    addVersionControlHeaders(Response.status((e.getErrorResponse().getResponseCode()))), requestId)
+                    .entity(e.getErrorResponse()).build();
+        }
+    }
+
     private ResponseBuilder addVersionControlHeaders(ResponseBuilder rb) {
         return rb.header("X-MinorVersion", "0").header("X-PatchVersion", "0").header("X-LatestVersion", "1.0.0");
     }
index b6a4e5a..1c83b05 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.onap.policy.pdpx.main.rest.provider;
 
+import com.att.research.xacml.api.Request;
 import com.att.research.xacml.api.Response;
 import com.att.research.xacml.api.Result;
 import java.util.Map;
@@ -30,6 +31,7 @@ import org.onap.policy.models.decisions.concepts.DecisionResponse;
 import org.onap.policy.pdp.xacml.application.common.XacmlApplicationServiceProvider;
 import org.onap.policy.pdpx.main.rest.XacmlPdpApplicationManager;
 import org.onap.policy.pdpx.main.rest.XacmlPdpStatisticsManager;
+import org.onap.policy.xacml.pdp.application.nativ.NativePdpApplication;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -64,6 +66,33 @@ public class DecisionProvider {
         return decision.getKey();
     }
 
+    /**
+     * Retrieves the policy decision for the native xacml request.
+     *
+     * @param request the xacml request
+     * @return the xacml response
+     */
+    public Response fetchNativeDecision(Request request) {
+        LOGGER.debug("Fetching decision {}", request);
+        //
+        // Assign native request to native application directly
+        //
+        XacmlApplicationServiceProvider nativeApp = findNativeApplication();
+        //
+        // Make xacml decision
+        //
+        Response decision = ((NativePdpApplication) nativeApp).makeNativeDecision(request);
+        LOGGER.debug("Xacml decision {}", decision);
+        //
+        // Calculate statistics
+        //
+        this.calculateStatistic(decision);
+        //
+        // Return the string decision
+        //
+        return decision;
+    }
+
     private XacmlApplicationServiceProvider findApplication(DecisionRequest request) {
         XacmlApplicationServiceProvider application = XacmlPdpApplicationManager.getCurrent().findApplication(request);
         if (application != null) {
@@ -73,8 +102,16 @@ public class DecisionProvider {
                 "No application for action " + request.getAction());
     }
 
-    private void calculateStatistic(Response xacmlResponse) {
+    private XacmlApplicationServiceProvider findNativeApplication() {
+        XacmlApplicationServiceProvider application = XacmlPdpApplicationManager.getCurrent().findNativeApplication();
+        if (application instanceof NativePdpApplication) {
+            return application;
+        }
+        throw new DecisionException(javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR,
+                "Native PDP application cannot be found");
+    }
 
+    private void calculateStatistic(Response xacmlResponse) {
         for (Result result : xacmlResponse.getResults()) {
             switch (result.getDecision()) {
                 case PERMIT:
@@ -98,9 +135,7 @@ public class DecisionProvider {
 
                 default:
                     break;
-
             }
         }
     }
-
 }
diff --git a/main/src/main/java/org/onap/policy/pdpx/main/rest/serialization/XacmlJsonExceptionMapper.java b/main/src/main/java/org/onap/policy/pdpx/main/rest/serialization/XacmlJsonExceptionMapper.java
new file mode 100644 (file)
index 0000000..81fc2d2
--- /dev/null
@@ -0,0 +1,68 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * 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.pdpx.main.rest.serialization;
+
+import java.io.IOException;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import lombok.Getter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Catches IOException when decoding/encoding a REST xacml request/response and converts them from an HTTP 500
+ * error code to an HTTP 400 error code.
+ *
+ * @author Chenfei Gao (cgao@research.att.com)
+ */
+@Provider
+@Produces(XacmlJsonMessageBodyHandler.APPLICATION_XACML_JSON)
+public class XacmlJsonExceptionMapper implements ExceptionMapper<IOException> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(XacmlJsonExceptionMapper.class);
+    private static final String INVALID_REQUEST = "invalid JSON xacml request";
+    private static final String INVALID_RESPONSE = "invalid JSON xacml response";
+
+    @Override
+    public Response toResponse(IOException exc) {
+        if (exc.getMessage().contains("json request")) {
+            LOGGER.warn(INVALID_REQUEST, exc);
+            return Response.status(Response.Status.BAD_REQUEST).entity(new SimpleResponse(INVALID_REQUEST)).build();
+        } else if (exc.getMessage().contains("json response")) {
+            LOGGER.warn(INVALID_RESPONSE, exc);
+            return Response.status(Response.Status.BAD_REQUEST).entity(new SimpleResponse(INVALID_RESPONSE)).build();
+        } else {
+            // Unexpected 500
+            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+        }
+    }
+
+    @Getter
+    private static class SimpleResponse {
+        private String errorDetails;
+
+        public SimpleResponse(String errorDetails) {
+            this.errorDetails = errorDetails;
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/src/main/java/org/onap/policy/pdpx/main/rest/serialization/XacmlJsonMessageBodyHandler.java b/main/src/main/java/org/onap/policy/pdpx/main/rest/serialization/XacmlJsonMessageBodyHandler.java
new file mode 100644 (file)
index 0000000..6bf2efd
--- /dev/null
@@ -0,0 +1,103 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * 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.pdpx.main.rest.serialization;
+
+import com.att.research.xacml.api.Request;
+import com.att.research.xacml.api.Response;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Provider that serializes and de-serializes xacml request/response json.
+ *
+ * @author Chenfei Gao (cgao@research.att.com)
+ */
+@Provider
+@Consumes(XacmlJsonMessageBodyHandler.APPLICATION_XACML_JSON)
+@Produces(XacmlJsonMessageBodyHandler.APPLICATION_XACML_JSON)
+public class XacmlJsonMessageBodyHandler implements MessageBodyReader<Request>, MessageBodyWriter<Response> {
+
+    public static final String APPLICATION_XACML_JSON = "application/xacml+json";
+
+    @Override
+    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+        return canHandle(mediaType, type);
+    }
+
+    @Override
+    public void writeTo(Response response, Class<?> type, Type genericType, Annotation[] annotations,
+            MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
+                    throws IOException {
+
+        try (OutputStreamWriter writer = new OutputStreamWriter(entityStream, StandardCharsets.UTF_8)) {
+            //TODO
+            //writer.write(JsonResponseTranslator.toString(response, true));
+        } catch (Exception exc) {
+            throw new IOException("failed to convert a json response to a string");
+        }
+    }
+
+    @Override
+    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+        return canHandle(mediaType, type);
+    }
+
+    @Override
+    public Request readFrom(Class<Request> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException {
+
+        Request jsonRequest = null;
+        try {
+            //TODO
+            //jsonRequest = JsonResponseTranslator.load(entityStream);
+        } catch (Exception exc) {
+            throw new IOException("failed to decode incoming request string to a json request");
+        }
+        return jsonRequest;
+    }
+
+    /**
+     * Determines if this provider can handle the given media type.
+     * @param mediaType the media type of interest
+     * @param type the class type of the object to read/write
+     * @return {@code true} if this provider handles the given media type and class type
+     *         {@code false} otherwise
+     */
+    private boolean canHandle(MediaType mediaType, Class<?> type) {
+        if (mediaType == null) {
+            return false;
+        }
+        return ("xacml+json".equals(mediaType.getSubtype()))
+                && (type == Request.class || type == Response.class);
+    }
+}
\ No newline at end of file
diff --git a/main/src/main/java/org/onap/policy/pdpx/main/rest/serialization/XacmlXmlExceptionMapper.java b/main/src/main/java/org/onap/policy/pdpx/main/rest/serialization/XacmlXmlExceptionMapper.java
new file mode 100644 (file)
index 0000000..8e62abe
--- /dev/null
@@ -0,0 +1,68 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * 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.pdpx.main.rest.serialization;
+
+import java.io.IOException;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import lombok.Getter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Catches IOException when decoding/encoding a REST xacml request/response and converts them from an HTTP 500
+ * error code to an HTTP 400 error code.
+ *
+ * @author Chenfei Gao (cgao@research.att.com)
+ */
+@Provider
+@Produces(XacmlXmlMessageBodyHandler.APPLICATION_XACML_XML)
+public class XacmlXmlExceptionMapper implements ExceptionMapper<IOException> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(XacmlXmlExceptionMapper.class);
+    private static final String INVALID_REQUEST = "invalid XML xacml request";
+    private static final String INVALID_RESPONSE = "invalid XML xacml response";
+
+    @Override
+    public Response toResponse(IOException exc) {
+        if (exc.getMessage().contains("dom request")) {
+            LOGGER.warn(INVALID_REQUEST, exc);
+            return Response.status(Response.Status.BAD_REQUEST).entity(new SimpleResponse(INVALID_REQUEST)).build();
+        } else if (exc.getMessage().contains("dom response")) {
+            LOGGER.warn(INVALID_RESPONSE, exc);
+            return Response.status(Response.Status.BAD_REQUEST).entity(new SimpleResponse(INVALID_RESPONSE)).build();
+        } else {
+            // Unexpected 500
+            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+        }
+    }
+
+    @Getter
+    private static class SimpleResponse {
+        private String errorDetails;
+
+        public SimpleResponse(String errorDetails) {
+            this.errorDetails = errorDetails;
+        }
+    }
+}
\ No newline at end of file
diff --git a/main/src/main/java/org/onap/policy/pdpx/main/rest/serialization/XacmlXmlMessageBodyHandler.java b/main/src/main/java/org/onap/policy/pdpx/main/rest/serialization/XacmlXmlMessageBodyHandler.java
new file mode 100644 (file)
index 0000000..ddd7752
--- /dev/null
@@ -0,0 +1,103 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * 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.pdpx.main.rest.serialization;
+
+import com.att.research.xacml.api.Request;
+import com.att.research.xacml.api.Response;
+import com.att.research.xacml.std.dom.DOMRequest;
+import com.att.research.xacml.std.dom.DOMResponse;
+import com.att.research.xacml.std.dom.DOMStructureException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Provider that serializes and de-serializes xacml request/response xml.
+ *
+ * @author Chenfei Gao (cgao@research.att.com)
+ */
+@Provider
+@Consumes(XacmlXmlMessageBodyHandler.APPLICATION_XACML_XML)
+@Produces(XacmlXmlMessageBodyHandler.APPLICATION_XACML_XML)
+public class XacmlXmlMessageBodyHandler implements MessageBodyReader<Request>, MessageBodyWriter<Response> {
+
+    public static final String APPLICATION_XACML_XML = "application/xacml+xml";
+
+    @Override
+    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+        return canHandle(mediaType, type);
+    }
+
+    @Override
+    public void writeTo(Response response, Class<?> type, Type genericType, Annotation[] annotations,
+            MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
+                    throws IOException {
+
+        try (OutputStreamWriter writer = new OutputStreamWriter(entityStream, StandardCharsets.UTF_8)) {
+            writer.write(DOMResponse.toString(response, true));
+        } catch (Exception exc) {
+            throw new IOException("failed to convert a dom response to a string");
+        }
+    }
+
+    @Override
+    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+        return canHandle(mediaType, type);
+    }
+
+    @Override
+    public Request readFrom(Class<Request> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException {
+
+        try {
+            return DOMRequest.load(entityStream);
+        } catch (DOMStructureException e) {
+            throw new IOException("failed to decode incoming request string to a dom request");
+        }
+    }
+
+    /**
+     * Determines if this provider can handle the given media type.
+     * @param mediaType the media type of interest
+     * @param type the class type of the object to read/write
+     * @return {@code true} if this provider handles the given media type and class type
+     *         {@code false} otherwise
+     */
+    private boolean canHandle(MediaType mediaType, Class<?> type) {
+        if (mediaType == null) {
+            return false;
+        }
+
+        return ("xacml+xml".equals(mediaType.getSubtype()))
+                && (type == Request.class || type == Response.class);
+    }
+}
\ No newline at end of file
index 9dd7f8e..1d0145f 100644 (file)
@@ -28,7 +28,6 @@ import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager;
 import org.onap.policy.common.endpoints.event.comm.TopicSource;
 import org.onap.policy.common.endpoints.event.comm.client.TopicSinkClient;
 import org.onap.policy.common.endpoints.event.comm.client.TopicSinkClientException;
-import org.onap.policy.common.endpoints.http.server.RestServer;
 import org.onap.policy.common.endpoints.listeners.MessageTypeDispatcher;
 import org.onap.policy.common.parameters.ParameterService;
 import org.onap.policy.common.utils.services.ServiceManagerContainer;
@@ -62,7 +61,7 @@ public class XacmlPdpActivator extends ServiceManagerContainer {
     @Getter
     @Setter
     private static XacmlPdpActivator current = null;
-    private final RestServer restServer;
+    private final XacmlPdpRestServer restServer;
 
     // The parameters of this policy xacml pdp activator
     private final XacmlPdpParameterGroup xacmlPdpParameterGroup;
@@ -115,8 +114,8 @@ public class XacmlPdpActivator extends ServiceManagerContainer {
             msgDispatcher.register(PdpMessageType.PDP_UPDATE.name(),
                             new XacmlPdpUpdateListener(sinkClient, state, heartbeat, appmgr));
 
-            restServer = new RestServer(xacmlPdpParameterGroup.getRestServerParameters(), XacmlPdpAafFilter.class,
-                                XacmlPdpRestController.class);
+            restServer = new XacmlPdpRestServer(xacmlPdpParameterGroup.getRestServerParameters(),
+                    XacmlPdpAafFilter.class, XacmlPdpRestController.class);
 
         } catch (RuntimeException | TopicSinkClientException e) {
             throw new PolicyXacmlPdpRuntimeException(e.getMessage(), e);
diff --git a/main/src/main/java/org/onap/policy/pdpx/main/startstop/XacmlPdpRestServer.java b/main/src/main/java/org/onap/policy/pdpx/main/startstop/XacmlPdpRestServer.java
new file mode 100644 (file)
index 0000000..a92d750
--- /dev/null
@@ -0,0 +1,69 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * 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.pdpx.main.startstop;
+
+import java.util.Properties;
+import org.onap.policy.common.endpoints.http.server.JsonExceptionMapper;
+import org.onap.policy.common.endpoints.http.server.RestServer;
+import org.onap.policy.common.endpoints.http.server.YamlExceptionMapper;
+import org.onap.policy.common.endpoints.http.server.YamlMessageBodyHandler;
+import org.onap.policy.common.endpoints.http.server.aaf.AafAuthFilter;
+import org.onap.policy.common.endpoints.parameters.RestServerParameters;
+import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties;
+import org.onap.policy.common.gson.GsonMessageBodyHandler;
+import org.onap.policy.pdpx.main.rest.serialization.XacmlXmlExceptionMapper;
+import org.onap.policy.pdpx.main.rest.serialization.XacmlXmlMessageBodyHandler;
+
+/**
+ * Class to manage life cycle of a rest server that is particularly used by xacml pdp.
+ *
+ * @author Chenfei Gao (cgao@research.att.com)
+ */
+public class XacmlPdpRestServer extends RestServer {
+
+    /**
+     * Constructs the object.
+     *
+     * @param restServerParameters the rest server parameters
+     * @param aafFilter class of object to use to filter AAF requests, or {@code null}
+     * @param jaxrsProviders classes providing the services
+     */
+    public XacmlPdpRestServer(final RestServerParameters restServerParameters,
+            Class<? extends AafAuthFilter> aafFilter, Class<?>... jaxrsProviders) {
+
+        super(restServerParameters, aafFilter, jaxrsProviders);
+    }
+
+    @Override
+    protected Properties getServerProperties(RestServerParameters restServerParameters, String names) {
+
+        final Properties props = super.getServerProperties(restServerParameters, names);
+        final String svcpfx =
+                        PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + restServerParameters.getName();
+
+        props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERIALIZATION_PROVIDER,
+                String.join(",", GsonMessageBodyHandler.class.getName(), YamlMessageBodyHandler.class.getName(),
+                                JsonExceptionMapper.class.getName(), YamlExceptionMapper.class.getName(),
+                                //XacmlJsonMessageBodyHandler.class.getName(), XacmlJsonExceptionMapper.class.getName(),
+                                XacmlXmlMessageBodyHandler.class.getName(), XacmlXmlExceptionMapper.class.getName()));
+        return props;
+    }
+}
index e3ad7b6..178e4b1 100644 (file)
@@ -24,6 +24,7 @@ package org.onap.policy.pdpx.main.rest;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.assertEquals;
 
+import com.att.research.xacml.std.dom.DOMStructureException;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import java.io.File;
@@ -54,6 +55,7 @@ import org.onap.policy.common.endpoints.parameters.RestServerParameters;
 import org.onap.policy.common.endpoints.parameters.TopicParameterGroup;
 import org.onap.policy.common.gson.GsonMessageBodyHandler;
 import org.onap.policy.common.utils.network.NetworkUtil;
+import org.onap.policy.common.utils.resources.ResourceUtils;
 import org.onap.policy.models.decisions.concepts.DecisionRequest;
 import org.onap.policy.models.decisions.concepts.DecisionResponse;
 import org.onap.policy.models.errors.concepts.ErrorResponse;
@@ -73,6 +75,7 @@ public class TestDecision {
     private static Main main;
     private static HttpClient client;
     private static CommonTestData testData = new CommonTestData();
+    private static final String APPLICATION_XACML_XML = "application/xacml+xml";
 
     @ClassRule
     public static final TemporaryFolder appsFolder = new TemporaryFolder();
@@ -179,6 +182,22 @@ public class TestDecision {
         assertThat(response.getStatus()).isEqualTo("Permit");
     }
 
+    @Test
+    public void testDecision_Native() throws IOException, DOMStructureException {
+
+        LOGGER.info("Running test testDecision_Native");
+
+        String requestAsString = ResourceUtils.getResourceAsString(
+                "src/test/resources/decisions/decision.native.request.xml");
+        if (requestAsString == null) {
+            throw new IOException("failed to read the xml request");
+        }
+
+        String response = getNativeDecision(requestAsString);
+        LOGGER.info("Response {}", response);
+        assertThat(response).contains("NOTAPPLICABLE");
+    }
+
     private static Main startXacmlPdpService(File params) throws PolicyXacmlPdpException {
         final String[] XacmlPdpConfigParameters = {"-c", params.getAbsolutePath()};
         return new Main(XacmlPdpConfigParameters);
@@ -191,17 +210,27 @@ public class TestDecision {
     private DecisionResponse getDecision(DecisionRequest request) {
 
         Entity<DecisionRequest> entityRequest = Entity.entity(request, MediaType.APPLICATION_JSON);
-        Response response = client.post("", entityRequest, Collections.emptyMap());
+        Response response = client.post("/decision", entityRequest, Collections.emptyMap());
 
         assertEquals(200, response.getStatus());
 
         return HttpClient.getBody(response, DecisionResponse.class);
     }
 
+    private String getNativeDecision(String request) {
+
+        Entity<String> entityRequest = Entity.entity(request, APPLICATION_XACML_XML);
+        Response response = client.post("/xacml", entityRequest, Collections.emptyMap());
+
+        assertEquals(200, response.getStatus());
+
+        return HttpClient.getBody(response, String.class);
+    }
+
     private ErrorResponse getErrorDecision(DecisionRequest request) {
 
         Entity<DecisionRequest> entityRequest = Entity.entity(request, MediaType.APPLICATION_JSON);
-        Response response = client.post("", entityRequest, Collections.emptyMap());
+        Response response = client.post("/decision", entityRequest, Collections.emptyMap());
 
         assertEquals(400, response.getStatus());
 
@@ -213,7 +242,7 @@ public class TestDecision {
                 .clientName("testDecisionClient")
                 .serializationProvider(GsonMessageBodyHandler.class.getName())
                 .useHttps(false).allowSelfSignedCerts(false).hostname("localhost").port(port)
-                .basePath("policy/pdpx/v1/decision")
+                .basePath("policy/pdpx/v1")
                 .userName("healthcheck").password("zb!XztG34").managed(true).build());
     }
 
diff --git a/main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/CommonSerialization.java b/main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/CommonSerialization.java
new file mode 100644 (file)
index 0000000..2b4a4fd
--- /dev/null
@@ -0,0 +1,83 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * 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.pdpx.main.rest.serialization;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.att.research.xacml.api.Request;
+import com.att.research.xacml.api.Response;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import javax.ws.rs.core.MediaType;
+
+public class CommonSerialization {
+
+    @SuppressWarnings("rawtypes")
+    private static final Class REQUEST_CLASS = Request.class;
+    @SuppressWarnings("rawtypes")
+    private static final Class RESPONSE_CLASS = Response.class;
+    @SuppressWarnings("rawtypes")
+    private static final Class GENERAL_CLASS = MyObject.class;
+
+    public static void testIsWritableOrReadable(String primaryType, String subType,
+            MultiArgsFunction<Class<?>, Type, Annotation[], MediaType, Boolean> getter) {
+
+        // null media type
+        assertFalse(getter.apply(null, null, null, null));
+
+        // valid media type and class type
+        assertTrue("writeable " + subType, getter.apply(
+                REQUEST_CLASS, null, null, new MediaType(primaryType, subType)));
+        assertTrue("writeable " + subType, getter.apply(
+                RESPONSE_CLASS, null, null, new MediaType(primaryType, subType)));
+
+        // valid media type but invalid class type
+        assertFalse(getter.apply(
+                GENERAL_CLASS, null, null, new MediaType(primaryType, subType)));
+
+        // null subtype or invalid media type
+        assertFalse(getter.apply(null, null, null, new MediaType(primaryType, null)));
+        assertFalse(getter.apply(null, null, null, MediaType.APPLICATION_JSON_TYPE));
+    }
+
+    public static class MyObject {
+        private int id;
+
+        public MyObject() {
+            super();
+        }
+
+        public MyObject(int id) {
+            this.id = id;
+        }
+
+        @Override
+        public String toString() {
+            return "MyObject [id=" + id + "]";
+        }
+    }
+
+    @FunctionalInterface
+    public interface MultiArgsFunction<T, U, V, W, R> {
+        public R apply(T value1, U value2, V value3, W value4);
+    }
+}
\ No newline at end of file
diff --git a/main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/TestXacmlJsonExceptionMapper.java b/main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/TestXacmlJsonExceptionMapper.java
new file mode 100644 (file)
index 0000000..1b3cc20
--- /dev/null
@@ -0,0 +1,57 @@
+/*-
+ * ============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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.pdpx.main.rest.serialization;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import javax.ws.rs.core.Response;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+
+public class TestXacmlJsonExceptionMapper {
+    private XacmlJsonExceptionMapper mapper;
+
+    @Before
+    public void setUp() {
+        mapper = new XacmlJsonExceptionMapper();
+    }
+
+    @Test
+    public void testToResponse() throws CoderException {
+        final IOException writeToEx = new IOException("failed to convert a json response to a string");
+        final IOException readFromEx = new IOException("failed to decode incoming request string to a json request");
+        final IOException unexpectedEx = new IOException("unexpected exception");
+        final Response writeToResp = mapper.toResponse(writeToEx);
+        final Response readFromResp = mapper.toResponse(readFromEx);
+        final Response unexpectedResp = mapper.toResponse(unexpectedEx);
+
+        assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), writeToResp.getStatus());
+        assertEquals("{'errorDetails':'invalid JSON xacml response'}".replace('\'', '"'),
+                        new StandardCoder().encode(writeToResp.getEntity()));
+        assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), readFromResp.getStatus());
+        assertEquals("{'errorDetails':'invalid JSON xacml request'}".replace('\'', '"'),
+                        new StandardCoder().encode(readFromResp.getEntity()));
+        assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), unexpectedResp.getStatus());
+    }
+}
\ No newline at end of file
diff --git a/main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/TestXacmlJsonMessageBodyHandler.java b/main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/TestXacmlJsonMessageBodyHandler.java
new file mode 100644 (file)
index 0000000..5d5a4b8
--- /dev/null
@@ -0,0 +1,59 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * 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.pdpx.main.rest.serialization;
+
+import com.att.research.xacml.std.dom.DOMStructureException;
+import java.io.IOException;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestXacmlJsonMessageBodyHandler {
+
+    private static final String PRIMARY_TYPE = "application";
+    private static final String SUB_TYPE = "xacml+json";
+
+    private XacmlJsonMessageBodyHandler hdlr;
+
+    @Before
+    public void setUp() {
+        hdlr = new XacmlJsonMessageBodyHandler();
+    }
+
+    @Test
+    public void testIsWriteable() {
+        CommonSerialization.testIsWritableOrReadable(PRIMARY_TYPE, SUB_TYPE, hdlr::isWriteable);
+    }
+
+    @Test
+    public void testWriteTo() throws IOException, DOMStructureException {
+        //TODO: placeholder for JsonResponseTranslator
+    }
+
+    @Test
+    public void testIsReadable() {
+        CommonSerialization.testIsWritableOrReadable(PRIMARY_TYPE, SUB_TYPE, hdlr::isReadable);
+    }
+
+    @Test
+    public void testReadFrom() throws IOException {
+        //TODO: placeholder for JsonRequestTranslator
+    }
+}
\ No newline at end of file
diff --git a/main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/TestXacmlXmlExceptionMapper.java b/main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/TestXacmlXmlExceptionMapper.java
new file mode 100644 (file)
index 0000000..c9b7bac
--- /dev/null
@@ -0,0 +1,57 @@
+/*-
+ * ============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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.pdpx.main.rest.serialization;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import javax.ws.rs.core.Response;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+
+public class TestXacmlXmlExceptionMapper {
+    private XacmlXmlExceptionMapper mapper;
+
+    @Before
+    public void setUp() {
+        mapper = new XacmlXmlExceptionMapper();
+    }
+
+    @Test
+    public void testToResponse() throws CoderException {
+        final IOException writeToEx = new IOException("failed to convert a dom response to a string");
+        final IOException readFromEx = new IOException("failed to decode incoming request string to a dom request");
+        final IOException unexpectedEx = new IOException("unexpected exception");
+        final Response writeToResp = mapper.toResponse(writeToEx);
+        final Response readFromResp = mapper.toResponse(readFromEx);
+        final Response unexpectedResp = mapper.toResponse(unexpectedEx);
+
+        assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), writeToResp.getStatus());
+        assertEquals("{'errorDetails':'invalid XML xacml response'}".replace('\'', '"'),
+                        new StandardCoder().encode(writeToResp.getEntity()));
+        assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), readFromResp.getStatus());
+        assertEquals("{'errorDetails':'invalid XML xacml request'}".replace('\'', '"'),
+                        new StandardCoder().encode(readFromResp.getEntity()));
+        assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), unexpectedResp.getStatus());
+    }
+}
\ No newline at end of file
diff --git a/main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/TestXacmlXmlMessageBodyHandler.java b/main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/TestXacmlXmlMessageBodyHandler.java
new file mode 100644 (file)
index 0000000..4ae7538
--- /dev/null
@@ -0,0 +1,101 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * 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.pdpx.main.rest.serialization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.att.research.xacml.api.Request;
+import com.att.research.xacml.api.RequestAttributes;
+import com.att.research.xacml.api.Response;
+import com.att.research.xacml.std.dom.DOMResponse;
+import com.att.research.xacml.std.dom.DOMStructureException;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Iterator;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.policy.common.utils.resources.ResourceUtils;
+
+public class TestXacmlXmlMessageBodyHandler {
+
+    private static final String PRIMARY_TYPE = "application";
+    private static final String SUB_TYPE = "xacml+xml";
+
+    @SuppressWarnings("rawtypes")
+    private static final Class REQUEST_CLASS = Request.class;
+    @SuppressWarnings("rawtypes")
+    private static final Class RESPONSE_CLASS = Response.class;
+
+    private XacmlXmlMessageBodyHandler hdlr;
+
+    @Before
+    public void setUp() {
+        hdlr = new XacmlXmlMessageBodyHandler();
+    }
+
+    @Test
+    public void testIsWriteable() {
+        CommonSerialization.testIsWritableOrReadable(PRIMARY_TYPE, SUB_TYPE, hdlr::isWriteable);
+    }
+
+    @Test
+    public void testWriteTo() throws IOException, DOMStructureException {
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        Response resp = DOMResponse.load(ResourceUtils.getResourceAsString(
+                "src/test/resources/decisions/decision.native.response.xml"));
+        hdlr.writeTo(resp, RESPONSE_CLASS, RESPONSE_CLASS, null, null, null, stream);
+        assertEquals(resp, DOMResponse.load(new ByteArrayInputStream(stream.toByteArray())));
+    }
+
+    @Test
+    public void testIsReadable() {
+        CommonSerialization.testIsWritableOrReadable(PRIMARY_TYPE, SUB_TYPE, hdlr::isReadable);
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testReadFrom() throws IOException {
+        Request req = hdlr.readFrom(REQUEST_CLASS, REQUEST_CLASS, null, null, null, ResourceUtils.getResourceAsStream(
+                "src/test/resources/decisions/decision.native.request.xml"));
+        assertFalse(req.getCombinedDecision());
+        assertFalse(req.getReturnPolicyIdList());
+        assertTrue(req.getRequestAttributes().size() == 4);
+        Iterator<RequestAttributes> iter = req.getRequestAttributes().iterator();
+
+        RequestAttributes firstRequestAttributes = iter.next();
+        assertTrue(firstRequestAttributes.getAttributes().size() == 1);
+        assertEquals("Julius Hibbert", firstRequestAttributes.getAttributes().iterator().next()
+                .getValues().iterator().next().getValue().toString());
+
+        RequestAttributes secondRequestAttributes = iter.next();
+        assertTrue(secondRequestAttributes.getAttributes().size() == 1);
+        assertEquals("http://medico.com/record/patient/BartSimpson", secondRequestAttributes.getAttributes()
+                .iterator().next().getValues().iterator().next().getValue().toString());
+
+        RequestAttributes thirdRequestAttributes = iter.next();
+        assertTrue(thirdRequestAttributes.getAttributes().size() == 1);
+        assertEquals("read", thirdRequestAttributes.getAttributes().iterator().next()
+                .getValues().iterator().next().getValue().toString());
+    }
+}
\ No newline at end of file
diff --git a/main/src/test/resources/apps/native/xacml.properties b/main/src/test/resources/apps/native/xacml.properties
new file mode 100644 (file)
index 0000000..5ea247c
--- /dev/null
@@ -0,0 +1,31 @@
+#
+# Properties that the embedded PDP engine uses to configure and load
+#
+# Standard API Factories
+#
+xacml.dataTypeFactory=com.att.research.xacml.std.StdDataTypeFactory
+xacml.pdpEngineFactory=com.att.research.xacmlatt.pdp.ATTPDPEngineFactory
+xacml.pepEngineFactory=com.att.research.xacml.std.pep.StdEngineFactory
+xacml.pipFinderFactory=com.att.research.xacml.std.pip.StdPIPFinderFactory
+xacml.traceEngineFactory=com.att.research.xacml.std.trace.LoggingTraceEngineFactory
+#
+# AT&T PDP Implementation Factories
+#
+xacml.att.evaluationContextFactory=com.att.research.xacmlatt.pdp.std.StdEvaluationContextFactory
+xacml.att.combiningAlgorithmFactory=com.att.research.xacmlatt.pdp.std.StdCombiningAlgorithmFactory
+xacml.att.functionDefinitionFactory=com.att.research.xacmlatt.pdp.std.StdFunctionDefinitionFactory
+#
+# ONAP PDP Implementation Factories
+#
+xacml.att.policyFinderFactory=org.onap.policy.pdp.xacml.application.common.OnapPolicyFinderFactory
+
+#
+# Use a root combining algorithm
+#
+xacml.att.policyFinderFactory.combineRootPolicies=urn:com:att:xacml:3.0:policy-combining-algorithm:combined-permit-overrides
+
+#
+# Policies to load
+#
+xacml.rootPolicies=
+xacml.referencedPolicies=
\ No newline at end of file
diff --git a/main/src/test/resources/decisions/decision.native.request.xml b/main/src/test/resources/decisions/decision.native.request.xml
new file mode 100644 (file)
index 0000000..3fe984a
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Request xsi:schemaLocation="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17 http://docs.oasis-open.org/xacml/3.0/xacml-core-v3-schema-wd-17.xsd" ReturnPolicyIdList="false" CombinedDecision="false" xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <Attributes Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject">
+    <Attribute IncludeInResult="false" AttributeId="urn:oasis:names:tc:xacml:1.0:subject:subject-id">
+      <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">Julius Hibbert</AttributeValue>
+    </Attribute>
+  </Attributes>
+  <Attributes Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource">
+    <Attribute IncludeInResult="false" AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id">
+      <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#anyURI">http://medico.com/record/patient/BartSimpson</AttributeValue>
+    </Attribute>
+  </Attributes>
+  <Attributes Category="urn:oasis:names:tc:xacml:3.0:attribute-category:action">
+    <Attribute IncludeInResult="false" AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id">
+      <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">read</AttributeValue>
+    </Attribute>
+  </Attributes>
+  <Attributes Category="urn:oasis:names:tc:xacml:3.0:attribute-category:environment" />
+</Request>
\ No newline at end of file
diff --git a/main/src/test/resources/decisions/decision.native.response.xml b/main/src/test/resources/decisions/decision.native.response.xml
new file mode 100644 (file)
index 0000000..8c484e0
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Response
+      xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17
+        http://docs.oasis-open.org/xacml/3.0/xacml-core-v3-schema-wd-17.xsd">
+    <Result>
+        <Decision>Permit</Decision>
+        <Status>
+            <StatusCode
+                  Value="urn:oasis:names:tc:xacml:1.0:status:ok"/>
+        </Status>
+    </Result>
+</Response>
\ No newline at end of file