summaryrefslogtreecommitdiffstats
path: root/main/src
diff options
context:
space:
mode:
authorChenfei Gao <cgao@research.att.com>2020-02-28 14:45:48 -0500
committerChenfei Gao <cgao@research.att.com>2020-02-28 14:45:55 -0500
commitbdef9f5dfd8c95eff86e710ef94c1fab4e652854 (patch)
treebcc7327976e890e3fec352a4dbf8d1f150e98bda /main/src
parentfcd767926048397607d9e0f0288f2a0982f6bbcc (diff)
Build XACML PDP support for native xacml policy type
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>
Diffstat (limited to 'main/src')
-rw-r--r--main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpApplicationManager.java4
-rw-r--r--main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpRestController.java57
-rw-r--r--main/src/main/java/org/onap/policy/pdpx/main/rest/provider/DecisionProvider.java41
-rw-r--r--main/src/main/java/org/onap/policy/pdpx/main/rest/serialization/XacmlJsonExceptionMapper.java68
-rw-r--r--main/src/main/java/org/onap/policy/pdpx/main/rest/serialization/XacmlJsonMessageBodyHandler.java103
-rw-r--r--main/src/main/java/org/onap/policy/pdpx/main/rest/serialization/XacmlXmlExceptionMapper.java68
-rw-r--r--main/src/main/java/org/onap/policy/pdpx/main/rest/serialization/XacmlXmlMessageBodyHandler.java103
-rw-r--r--main/src/main/java/org/onap/policy/pdpx/main/startstop/XacmlPdpActivator.java7
-rw-r--r--main/src/main/java/org/onap/policy/pdpx/main/startstop/XacmlPdpRestServer.java69
-rw-r--r--main/src/test/java/org/onap/policy/pdpx/main/rest/TestDecision.java35
-rw-r--r--main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/CommonSerialization.java83
-rw-r--r--main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/TestXacmlJsonExceptionMapper.java57
-rw-r--r--main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/TestXacmlJsonMessageBodyHandler.java59
-rw-r--r--main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/TestXacmlXmlExceptionMapper.java57
-rw-r--r--main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/TestXacmlXmlMessageBodyHandler.java101
-rw-r--r--main/src/test/resources/apps/native/xacml.properties31
-rw-r--r--main/src/test/resources/decisions/decision.native.request.xml19
-rw-r--r--main/src/test/resources/decisions/decision.native.response.xml14
-rw-r--r--main/src/test/resources/decisions/decision.single.output.json (renamed from main/src/test/resources/decisions/decsion.single.output.json)0
19 files changed, 964 insertions, 12 deletions
diff --git a/main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpApplicationManager.java b/main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpApplicationManager.java
index 2f054bd1..0e4bef25 100644
--- a/main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpApplicationManager.java
+++ b/main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpApplicationManager.java
@@ -118,6 +118,10 @@ public class XacmlPdpApplicationManager {
return providerActionMap.get(request.getAction());
}
+ public XacmlApplicationServiceProvider findNativeApplication() {
+ return providerActionMap.get("native");
+ }
+
/**
* getToscaPolicies.
*
diff --git a/main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpRestController.java b/main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpRestController.java
index f668411b..ad6784b3 100644
--- a/main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpRestController.java
+++ b/main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpRestController.java
@@ -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");
}
diff --git a/main/src/main/java/org/onap/policy/pdpx/main/rest/provider/DecisionProvider.java b/main/src/main/java/org/onap/policy/pdpx/main/rest/provider/DecisionProvider.java
index b6a4e5a0..1c83b058 100644
--- a/main/src/main/java/org/onap/policy/pdpx/main/rest/provider/DecisionProvider.java
+++ b/main/src/main/java/org/onap/policy/pdpx/main/rest/provider/DecisionProvider.java
@@ -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
index 00000000..81fc2d26
--- /dev/null
+++ b/main/src/main/java/org/onap/policy/pdpx/main/rest/serialization/XacmlJsonExceptionMapper.java
@@ -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
index 00000000..6bf2efd3
--- /dev/null
+++ b/main/src/main/java/org/onap/policy/pdpx/main/rest/serialization/XacmlJsonMessageBodyHandler.java
@@ -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
index 00000000..8e62abec
--- /dev/null
+++ b/main/src/main/java/org/onap/policy/pdpx/main/rest/serialization/XacmlXmlExceptionMapper.java
@@ -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
index 00000000..ddd77528
--- /dev/null
+++ b/main/src/main/java/org/onap/policy/pdpx/main/rest/serialization/XacmlXmlMessageBodyHandler.java
@@ -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
diff --git a/main/src/main/java/org/onap/policy/pdpx/main/startstop/XacmlPdpActivator.java b/main/src/main/java/org/onap/policy/pdpx/main/startstop/XacmlPdpActivator.java
index 9dd7f8ea..1d0145fd 100644
--- a/main/src/main/java/org/onap/policy/pdpx/main/startstop/XacmlPdpActivator.java
+++ b/main/src/main/java/org/onap/policy/pdpx/main/startstop/XacmlPdpActivator.java
@@ -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
index 00000000..a92d750e
--- /dev/null
+++ b/main/src/main/java/org/onap/policy/pdpx/main/startstop/XacmlPdpRestServer.java
@@ -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;
+ }
+}
diff --git a/main/src/test/java/org/onap/policy/pdpx/main/rest/TestDecision.java b/main/src/test/java/org/onap/policy/pdpx/main/rest/TestDecision.java
index e3ad7b67..178e4b14 100644
--- a/main/src/test/java/org/onap/policy/pdpx/main/rest/TestDecision.java
+++ b/main/src/test/java/org/onap/policy/pdpx/main/rest/TestDecision.java
@@ -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
index 00000000..2b4a4fd0
--- /dev/null
+++ b/main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/CommonSerialization.java
@@ -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
index 00000000..1b3cc209
--- /dev/null
+++ b/main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/TestXacmlJsonExceptionMapper.java
@@ -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
index 00000000..5d5a4b88
--- /dev/null
+++ b/main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/TestXacmlJsonMessageBodyHandler.java
@@ -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
index 00000000..c9b7bac5
--- /dev/null
+++ b/main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/TestXacmlXmlExceptionMapper.java
@@ -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
index 00000000..4ae75387
--- /dev/null
+++ b/main/src/test/java/org/onap/policy/pdpx/main/rest/serialization/TestXacmlXmlMessageBodyHandler.java
@@ -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
index 00000000..5ea247cf
--- /dev/null
+++ b/main/src/test/resources/apps/native/xacml.properties
@@ -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
index 00000000..3fe984a4
--- /dev/null
+++ b/main/src/test/resources/decisions/decision.native.request.xml
@@ -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
index 00000000..8c484e0a
--- /dev/null
+++ b/main/src/test/resources/decisions/decision.native.response.xml
@@ -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
diff --git a/main/src/test/resources/decisions/decsion.single.output.json b/main/src/test/resources/decisions/decision.single.output.json
index 707a3964..707a3964 100644
--- a/main/src/test/resources/decisions/decsion.single.output.json
+++ b/main/src/test/resources/decisions/decision.single.output.json