Added HTTPS and CADI/AAF Support for PDP-X 63/79263/7
authorMichael Mokry <michael.mokry@att.com>
Wed, 27 Feb 2019 13:42:20 +0000 (07:42 -0600)
committerMichael Mokry <michael.mokry@att.com>
Mon, 11 Mar 2019 19:13:38 +0000 (14:13 -0500)
- Yay I found the mysterious https issue that caused the junit to fail.
Please don't laugh, it was a typo :)

- Made changes per review comments
1) Added builder class for RestServer constructor
2) removed some try/catch blocks in junit
3) Other minor changes

- More changes per review comments
- Added changes per Ram's review comments (not lombrok yet)
- Made changes for ONAP API CVS guidelines

Change-Id: Ie1a6225459b3ce235cd73828ccddec04c690f5fd
Issue-ID: POLICY-1436
Signed-off-by: Michael Mokry <michael.mokry@att.com>
main/src/main/java/org/onap/policy/pdpx/main/parameters/RestServerBuilder.java [new file with mode: 0644]
main/src/main/java/org/onap/policy/pdpx/main/parameters/RestServerParameters.java
main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpAafFilter.java [new file with mode: 0644]
main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpRestController.java
main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpRestServer.java
main/src/main/java/org/onap/policy/pdpx/main/startstop/Main.java
main/src/test/java/org/onap/policy/pdpx/main/parameters/CommonTestData.java
main/src/test/java/org/onap/policy/pdpx/main/parameters/TestXacmlPdpParameterGroup.java
main/src/test/java/org/onap/policy/pdpx/main/rest/TestXacmlPdpRestServer.java
main/src/test/resources/parameters/XacmlPdpConfigParameters_Https.json [new file with mode: 0644]
main/src/test/resources/ssl/policy-keystore [new file with mode: 0644]

diff --git a/main/src/main/java/org/onap/policy/pdpx/main/parameters/RestServerBuilder.java b/main/src/main/java/org/onap/policy/pdpx/main/parameters/RestServerBuilder.java
new file mode 100644 (file)
index 0000000..90bc09f
--- /dev/null
@@ -0,0 +1,85 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2019 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.parameters;
+
+public class RestServerBuilder {
+    private String host;
+    private int port;
+    private String userName;
+    private String password;
+    private boolean https;
+    private boolean aaf;
+
+
+    public String getHost() {
+        return host;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public boolean isHttps() {
+        return https;
+    }
+
+    public boolean isAaf() {
+        return aaf;
+    }
+
+    public RestServerBuilder setHost(String host) {
+        this.host = host;
+        return this;
+    }
+
+    public RestServerBuilder setPort(int port) {
+        this.port = port;
+        return this;
+    }
+
+    public RestServerBuilder setUserName(String userName) {
+        this.userName = userName;
+        return this;
+    }
+
+    public RestServerBuilder setPassword(String password) {
+        this.password = password;
+        return this;
+    }
+
+    public RestServerBuilder setHttps(boolean https) {
+        this.https = https;
+        return this;
+    }
+
+    public RestServerBuilder setAaf(boolean aaf) {
+        this.aaf = aaf;
+        return this;
+    }
+}
index e04a0f2..c452c75 100644 (file)
@@ -35,21 +35,22 @@ public class RestServerParameters implements ParameterGroup {
     private int port;
     private String userName;
     private String password;
+    private boolean https;
+    private boolean aaf;
 
     /**
      * Constructor for instantiating RestServerParameters.
      *
-     * @param host the host name
-     * @param port the port
-     * @param userName the user name
-     * @param password the password
+     * @param builder the RestServer builder
      */
-    public RestServerParameters(final String host, final int port, final String userName, final String password) {
+    public RestServerParameters(final RestServerBuilder builder) {
         super();
-        this.host = host;
-        this.port = port;
-        this.userName = userName;
-        this.password = password;
+        this.host = builder.getHost();
+        this.port = builder.getPort();
+        this.userName = builder.getUserName();
+        this.password = builder.getPassword();
+        this.https = builder.isHttps();
+        this.aaf = builder.isAaf();
     }
 
     /**
@@ -98,6 +99,24 @@ public class RestServerParameters implements ParameterGroup {
         return password;
     }
 
+    /**
+     * Return the https flag of this RestServerParameters instance.
+     *
+     * @return the https flag
+     */
+    public boolean isHttps() {
+        return https;
+    }
+
+    /**
+     * Return the aaf flag of this RestServerParameters instance.
+     *
+     * @return the aaf flag
+     */
+    public boolean isAaf() {
+        return aaf;
+    }
+
     /**
      * Set the name of this RestServerParameters instance.
      *
diff --git a/main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpAafFilter.java b/main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpAafFilter.java
new file mode 100644 (file)
index 0000000..5a4c3bc
--- /dev/null
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2019 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;
+
+import org.onap.policy.common.endpoints.http.server.aaf.AafGranularAuthFilter;
+
+/**
+ * Class to manage aaf filters for Xacml PDP component.
+ *
+ */
+public class XacmlPdpAafFilter extends AafGranularAuthFilter {
+
+    public static final String AAF_NODETYPE = "policy-pdpx";
+    public static final String AAF_ROOT_PERMISSION = DEFAULT_NAMESPACE + "." + AAF_NODETYPE;
+
+    @Override
+    public String getPermissionTypeRoot() {
+        return AAF_ROOT_PERMISSION;
+    }
+}
\ No newline at end of file
index dc091f0..965753a 100644 (file)
@@ -22,21 +22,27 @@ package org.onap.policy.pdpx.main.rest;
 
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
 import io.swagger.annotations.Authorization;
 import io.swagger.annotations.BasicAuthDefinition;
+import io.swagger.annotations.Extension;
+import io.swagger.annotations.ExtensionProperty;
 import io.swagger.annotations.Info;
+import io.swagger.annotations.ResponseHeader;
 import io.swagger.annotations.SecurityDefinition;
 import io.swagger.annotations.SwaggerDefinition;
-import io.swagger.annotations.Tag;
+import java.util.UUID;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
 import org.onap.policy.common.endpoints.report.HealthCheckReport;
 import org.onap.policy.pdpx.main.rest.model.Decision;
 import org.onap.policy.pdpx.main.rest.model.StatisticsReport;
@@ -52,10 +58,10 @@ import org.onap.policy.pdpx.main.rest.provider.StatisticsProvider;
 @Api
 @Produces(MediaType.APPLICATION_JSON)
 @Consumes(MediaType.APPLICATION_JSON)
-@SwaggerDefinition(info = @Info(description = "Policy Xacml PDP Service", version = "v1.0", title = "Policy Xacml PDP"),
-        consumes = {MediaType.APPLICATION_JSON}, produces = {MediaType.APPLICATION_JSON},
+@SwaggerDefinition(info = @Info(description = "Policy Xacml PDP Service", version = "1.0.0", title = "Policy Xacml PDP",
+        extensions = {@Extension(properties = {@ExtensionProperty(name = "planned-retirement-date", value = "tbd"),
+                @ExtensionProperty(name = "component", value = "Policy Framework")})}),
         schemes = {SwaggerDefinition.Scheme.HTTP, SwaggerDefinition.Scheme.HTTPS},
-        tags = {@Tag(name = "policy-pdpx", description = "Policy Xacml PDP Service Operations")},
         securityDefinition = @SecurityDefinition(basicAuthDefinitions = {@BasicAuthDefinition(key = "basicAuth")}))
 public class XacmlPdpRestController {
 
@@ -63,39 +69,108 @@ public class XacmlPdpRestController {
     @Path("/healthcheck")
     @ApiOperation(value = "Perform a system healthcheck",
             notes = "Provides healthy status of the Policy Xacml PDP component", response = HealthCheckReport.class,
-            authorizations = @Authorization(value = "basicAuth"))
-    @ApiResponses(value = {
-            @ApiResponse(code = 401, message = "Authentication Error"),
+            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 = {"HealthCheck",},
+            extensions = {@Extension(name = "interface info",
+                    properties = {@ExtensionProperty(name = "pdpx-version", value = "1.0.0"),
+                            @ExtensionProperty(name = "last-mod-release", value = "Dublin")})})
+    @ApiResponses(value = {@ApiResponse(code = 401, message = "Authentication Error"),
             @ApiResponse(code = 403, message = "Authorization Error"),
             @ApiResponse(code = 500, message = "Internal Server Error")})
-    public Response healthcheck() {
-        return Response.status(Response.Status.OK).entity(new HealthCheckProvider().performHealthCheck()).build();
+    public Response healthcheck(
+            @HeaderParam("X-ONAP-RequestID") @ApiParam("RequestID for http transaction") UUID requestId) {
+        return addLoggingHeaders(addVersionControlHeaders(Response.status(Response.Status.OK)), requestId)
+                .entity(new HealthCheckProvider().performHealthCheck()).build();
     }
 
     @GET
     @Path("/statistics")
     @ApiOperation(value = "Fetch current statistics",
             notes = "Provides current statistics of the Policy Xacml PDP component", response = StatisticsReport.class,
-            authorizations = @Authorization(value = "basicAuth"))
-    @ApiResponses(value = {
-            @ApiResponse(code = 401, message = "Authentication Error"),
+            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 = {"Statistics",},
+            extensions = {@Extension(name = "interface info",
+                    properties = {@ExtensionProperty(name = "pdpx-version", value = "1.0.0"),
+                            @ExtensionProperty(name = "last-mod-release", value = "Dublin")})})
+    @ApiResponses(value = {@ApiResponse(code = 401, message = "Authentication Error"),
             @ApiResponse(code = 403, message = "Authorization Error"),
             @ApiResponse(code = 500, message = "Internal Server Error")})
-    public Response statistics() {
-        return Response.status(Response.Status.OK).entity(new StatisticsProvider().fetchCurrentStatistics()).build();
+    public Response statistics(
+            @HeaderParam("X-ONAP-RequestID") @ApiParam("RequestID for http transaction") UUID requestId) {
+        return addLoggingHeaders(addVersionControlHeaders(Response.status(Response.Status.OK)), requestId)
+                .entity(new StatisticsProvider().fetchCurrentStatistics()).build();
     }
 
     @POST
     @Path("/decision")
     @ApiOperation(value = "Fetch the decision using specified decision parameters",
             notes = "Returns the policy decision from Policy Xacml PDP", response = Decision.class,
-            authorizations = @Authorization(value = "basicAuth"),
-            tags = {"Decision",})
+            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 = "Dublin")})})
     @ApiResponses(value = {@ApiResponse(code = 400, message = "Bad Request"),
             @ApiResponse(code = 401, message = "Authentication Error"),
             @ApiResponse(code = 403, message = "Authorization Error"),
             @ApiResponse(code = 500, message = "Internal Server Error")})
-    public Response decision(Decision body) {
-        return Response.status(Response.Status.OK).entity(new DecisionProvider().fetchDecision()).build();
+    public Response decision(Decision body,
+            @HeaderParam("X-ONAP-RequestID") @ApiParam("RequestID for http transaction") UUID requestId) {
+        return addLoggingHeaders(addVersionControlHeaders(Response.status(Response.Status.OK)), requestId)
+                .entity(new DecisionProvider().fetchDecision()).build();
+    }
+
+    private ResponseBuilder addVersionControlHeaders(ResponseBuilder rb) {
+        return rb.header("X-MinorVersion", "0").header("X-PatchVersion", "0").header("X-LatestVersion", "1.0.0");
+    }
+
+    private ResponseBuilder addLoggingHeaders(ResponseBuilder rb, UUID requestId) {
+        if (requestId == null) {
+            // Generate a random uuid if client does not embed requestId in rest request
+            return rb.header("X-ONAP-RequestID", UUID.randomUUID());
+        }
+        return rb.header("X-ONAP-RequestID", requestId);
     }
 }
index 90f0bfa..eece1ff 100644 (file)
@@ -25,6 +25,7 @@ import java.util.List;
 import java.util.Properties;
 import org.onap.policy.common.capabilities.Startable;
 import org.onap.policy.common.endpoints.http.server.HttpServletServer;
+import org.onap.policy.common.gson.JacksonHandler;
 import org.onap.policy.pdpx.main.parameters.RestServerParameters;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -60,6 +61,9 @@ public class XacmlPdpRestServer implements Startable {
         try {
             servers = HttpServletServer.factory.build(getServerProperties());
             for (final HttpServletServer server : servers) {
+                if (server.isAaf()) {
+                    server.addFilterClass(null, XacmlPdpAafFilter.class.getCanonicalName());
+                }
                 server.start();
             }
         } catch (final Exception exp) {
@@ -89,6 +93,10 @@ public class XacmlPdpRestServer implements Startable {
                 restServerParameters.getUserName());
         props.setProperty(HTTP_SERVER_SERVICES + SEPARATOR + restServerParameters.getName() + ".password",
                 restServerParameters.getPassword());
+        props.setProperty(HTTP_SERVER_SERVICES + SEPARATOR + restServerParameters.getName() + ".https",
+                String.valueOf(restServerParameters.isHttps()));
+        props.setProperty(HTTP_SERVER_SERVICES + SEPARATOR + restServerParameters.getName() + ".aaf",
+                String.valueOf(restServerParameters.isAaf()));
         return props;
     }
 
index 91b38f9..65e0a31 100644 (file)
@@ -51,7 +51,7 @@ public class Main {
      */
     public Main(final String[] args) {
         final String argumentString = Arrays.toString(args);
-        LOGGER.info("Starting policy xacml pdp service with arguments - " + argumentString);
+        LOGGER.info("Starting policy xacml pdp service with arguments - {}", argumentString);
 
         // Check the arguments
         final XacmlPdpCommandLineArguments arguments = new XacmlPdpCommandLineArguments();
index f50871e..1bf2294 100644 (file)
@@ -30,6 +30,8 @@ public class CommonTestData {
     private static final String REST_SERVER_USER = "healthcheck";
     private static final int REST_SERVER_PORT = 6969;
     private static final String REST_SERVER_HOST = "0.0.0.0";
+    private static final boolean REST_SERVER_HTTPS = false;
+    private static final boolean REST_SERVER_AAF = false;
     public static final String PDPX_GROUP_NAME = "XacmlPdpGroup";
 
     /**
@@ -39,13 +41,16 @@ public class CommonTestData {
      * @return the restServerParameters object
      */
     public RestServerParameters getRestServerParameters(final boolean isEmpty) {
-        final RestServerParameters restServerParameters;
+        final RestServerBuilder builder;
         if (!isEmpty) {
-            restServerParameters = new RestServerParameters(REST_SERVER_HOST, REST_SERVER_PORT, REST_SERVER_USER,
-                    REST_SERVER_PASSWORD);
+            builder = new RestServerBuilder().setHost(REST_SERVER_HOST).setPort(REST_SERVER_PORT)
+                    .setUserName(REST_SERVER_USER).setPassword(REST_SERVER_PASSWORD).setHttps(REST_SERVER_HTTPS)
+                    .setAaf(REST_SERVER_AAF);
         } else {
-            restServerParameters = new RestServerParameters(null, 0, null, null);
+            builder = new RestServerBuilder().setHost(null).setPort(0).setUserName(null).setPassword(null)
+                    .setHttps(REST_SERVER_HTTPS).setAaf(REST_SERVER_AAF);
         }
+        final RestServerParameters restServerParameters = new RestServerParameters(builder);
         return restServerParameters;
     }
 
index 4b9db99..48606c9 100644 (file)
@@ -47,6 +47,8 @@ public class TestXacmlPdpParameterGroup {
         assertEquals(restServerParameters.getUserName(), pdpxParameters.getRestServerParameters().getUserName());
         assertEquals(restServerParameters.getPassword(), pdpxParameters.getRestServerParameters().getPassword());
         assertEquals(CommonTestData.PDPX_GROUP_NAME, pdpxParameters.getName());
+        assertFalse(pdpxParameters.getRestServerParameters().isHttps());
+        assertFalse(pdpxParameters.getRestServerParameters().isAaf());
     }
 
     @Test
index 2170c8a..0f608e2 100644 (file)
@@ -18,7 +18,6 @@
  * ============LICENSE_END=========================================================
  */
 
-
 package org.onap.policy.pdpx.main.rest;
 
 import static org.junit.Assert.assertEquals;
@@ -26,6 +25,14 @@ import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+import java.util.Properties;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
 import javax.ws.rs.client.Client;
 import javax.ws.rs.client.ClientBuilder;
 import javax.ws.rs.client.Invocation;
@@ -33,19 +40,20 @@ import javax.ws.rs.client.WebTarget;
 import javax.ws.rs.core.MediaType;
 import org.glassfish.jersey.client.ClientConfig;
 import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
+import org.junit.After;
 import org.junit.Test;
 import org.onap.policy.common.endpoints.report.HealthCheckReport;
 import org.onap.policy.common.utils.network.NetworkUtil;
 import org.onap.policy.pdpx.main.PolicyXacmlPdpException;
 import org.onap.policy.pdpx.main.parameters.CommonTestData;
 import org.onap.policy.pdpx.main.parameters.RestServerParameters;
+import org.onap.policy.pdpx.main.rest.model.StatisticsReport;
 import org.onap.policy.pdpx.main.startstop.Main;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-
 /**
- * Class to perform unit test of HealthCheckMonitor.
+ * Class to perform unit test of {@link XacmlPdpRestServer}.
  *
  */
 public class TestXacmlPdpRestServer {
@@ -55,45 +63,124 @@ public class TestXacmlPdpRestServer {
     private static final String ALIVE = "alive";
     private static final String SELF = "self";
     private static final String NAME = "Policy Xacml PDP";
+    private static final String HEALTHCHECK_ENDPOINT = "healthcheck";
+    private static final String STATISTICS_ENDPOINT = "statistics";
+    private static String KEYSTORE = System.getProperty("user.dir") + "/src/test/resources/ssl/policy-keystore";
+    private Main main;
+    private XacmlPdpRestServer restServer;
 
-    @Test
-    public void testHealthCheckSuccess() throws PolicyXacmlPdpException, InterruptedException {
-        final String reportString = "Report [name=Policy Xacml PDP, url=self, healthy=true, code=200, message=alive]";
+    /**
+     * Method for cleanup after each test.
+     */
+    @After
+    public void teardown() {
         try {
-            final Main main = startXacmlPdpService();
-            final HealthCheckReport report = performHealthCheck();
-            validateReport(NAME, SELF, true, 200, ALIVE, reportString, report);
-            stopXacmlPdpService(main);
-        } catch (final Exception e) {
-            LOGGER.error("testHealthCheckSuccess failed", e);
-            fail("Test should not throw an exception");
+            if (NetworkUtil.isTcpPortOpen("localhost", 6969, 1, 1000L)) {
+                if (main != null) {
+                    stopXacmlPdpService(main);
+                }
+
+                if (restServer != null) {
+                    restServer.stop();
+                }
+            }
+        } catch (IOException | PolicyXacmlPdpException e) {
+            LOGGER.error("teardown failed", e);
+        } catch (InterruptedException ie) {
+            Thread.interrupted();
+            LOGGER.error("teardown failed", ie);
         }
+    }
 
+    @Test
+    public void testHealthCheckSuccess() throws IOException, InterruptedException {
+        main = startXacmlPdpService(true);
+        final Invocation.Builder invocationBuilder = sendHttpRequest(HEALTHCHECK_ENDPOINT);
+        final HealthCheckReport report = invocationBuilder.get(HealthCheckReport.class);
+        validateHealthCheckReport(NAME, SELF, true, 200, ALIVE, report);
     }
 
     @Test
-    public void testHealthCheckFailure() throws InterruptedException {
-        final String reportString =
-                "Report [name=Policy Xacml PDP, url=self, healthy=false, code=500, message=not alive]";
+    public void testHealthCheckFailure() throws InterruptedException, IOException {
         final RestServerParameters restServerParams = new CommonTestData().getRestServerParameters(false);
         restServerParams.setName(CommonTestData.PDPX_GROUP_NAME);
-        final XacmlPdpRestServer restServer = new XacmlPdpRestServer(restServerParams);
+        restServer = new XacmlPdpRestServer(restServerParams);
+        restServer.start();
+        final Invocation.Builder invocationBuilder = sendHttpRequest(HEALTHCHECK_ENDPOINT);
+        final HealthCheckReport report = invocationBuilder.get(HealthCheckReport.class);
+        validateHealthCheckReport(NAME, SELF, false, 500, NOT_ALIVE, report);
+        assertTrue(restServer.isAlive());
+        assertTrue(restServer.toString().startsWith("XacmlPdpRestServer [servers="));
+    }
+
+    @Test
+    public void testHttpsHealthCheckSuccess() throws Exception {
+        main = startXacmlPdpService(false);
+        final Invocation.Builder invocationBuilder = sendHttpsRequest(HEALTHCHECK_ENDPOINT);
+        final HealthCheckReport report = invocationBuilder.get(HealthCheckReport.class);
+        validateHealthCheckReport(NAME, SELF, true, 200, ALIVE, report);
+    }
+
+    @Test
+    public void testStatistics_200() throws IOException, InterruptedException {
+        main = startXacmlPdpService(true);
+        Invocation.Builder invocationBuilder = sendHttpRequest(STATISTICS_ENDPOINT);
+        StatisticsReport report = invocationBuilder.get(StatisticsReport.class);
+        validateStatisticsReport(report, 0, 200);
+        updateXacmlPdpStatistics();
+        invocationBuilder = sendHttpRequest(STATISTICS_ENDPOINT);
+        report = invocationBuilder.get(StatisticsReport.class);
+        validateStatisticsReport(report, 1, 200);
+        XacmlPdpStatisticsManager.resetAllStatistics();
+    }
+
+    @Test
+    public void testStatistics_500() throws IOException, InterruptedException {
+        final RestServerParameters restServerParams = new CommonTestData().getRestServerParameters(false);
+        restServerParams.setName(CommonTestData.PDPX_GROUP_NAME);
+        restServer = new XacmlPdpRestServer(restServerParams);
+        restServer.start();
+        final Invocation.Builder invocationBuilder = sendHttpRequest(STATISTICS_ENDPOINT);
+        final StatisticsReport report = invocationBuilder.get(StatisticsReport.class);
+        validateStatisticsReport(report, 0, 500);
+        XacmlPdpStatisticsManager.resetAllStatistics();
+    }
+
+    @Test
+    public void testHttpsStatistic() throws Exception {
+        main = startXacmlPdpService(false);
+        final Invocation.Builder invocationBuilder = sendHttpsRequest(STATISTICS_ENDPOINT);
+        final StatisticsReport report = invocationBuilder.get(StatisticsReport.class);
+        validateStatisticsReport(report, 0, 200);
+    }
 
+    @Test
+    public void testStatisticsConstructorIsPrivate() {
         try {
-            restServer.start();
-            final HealthCheckReport report = performHealthCheck();
-            validateReport(NAME, SELF, false, 500, NOT_ALIVE, reportString, report);
-            assertTrue(restServer.isAlive());
-            assertTrue(restServer.toString().startsWith("XacmlPdpRestServer [servers="));
-            restServer.shutdown();
-        } catch (final Exception e) {
-            LOGGER.error("testHealthCheckSuccess failed", e);
-            fail("Test should not throw an exception");
+            final Constructor<XacmlPdpStatisticsManager> constructor =
+                    XacmlPdpStatisticsManager.class.getDeclaredConstructor();
+            assertTrue(Modifier.isPrivate(constructor.getModifiers()));
+            constructor.setAccessible(true);
+            constructor.newInstance();
+            fail("Expected an InstantiationException to be thrown");
+        } catch (final Exception exp) {
+            assertTrue(exp.getCause().toString().contains("Instantiation of the class is not allowed"));
         }
     }
 
-    private Main startXacmlPdpService() {
-        final String[] xacmlPdpConfigParameters = {"-c", "parameters/XacmlPdpConfigParameters.json"};
+    private Main startXacmlPdpService(final boolean http) {
+        final String[] xacmlPdpConfigParameters = new String[2];
+        if (http) {
+            xacmlPdpConfigParameters[0] = "-c";
+            xacmlPdpConfigParameters[1] = "parameters/XacmlPdpConfigParameters.json";
+        } else {
+            final Properties systemProps = System.getProperties();
+            systemProps.put("javax.net.ssl.keyStore", KEYSTORE);
+            systemProps.put("javax.net.ssl.keyStorePassword", "Pol1cy_0nap");
+            System.setProperties(systemProps);
+            xacmlPdpConfigParameters[0] = "-c";
+            xacmlPdpConfigParameters[1] = "parameters/XacmlPdpConfigParameters_Https.json";
+        }
         return new Main(xacmlPdpConfigParameters);
     }
 
@@ -101,32 +188,80 @@ public class TestXacmlPdpRestServer {
         main.shutdown();
     }
 
-    private HealthCheckReport performHealthCheck() throws InterruptedException, IOException {
-
+    private Invocation.Builder sendHttpRequest(final String endpoint) throws IOException, InterruptedException {
         final ClientConfig clientConfig = new ClientConfig();
 
         final HttpAuthenticationFeature feature = HttpAuthenticationFeature.basic("healthcheck", "zb!XztG34");
         clientConfig.register(feature);
 
         final Client client = ClientBuilder.newClient(clientConfig);
-        final WebTarget webTarget = client.target("http://localhost:6969/policy/pdpx/v1/healthcheck");
+        final WebTarget webTarget = client.target("http://localhost:6969/policy/pdpx/v1/" + endpoint);
 
         final Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_JSON);
 
         if (!NetworkUtil.isTcpPortOpen("localhost", 6969, 6, 10000L)) {
-            throw new IllegalStateException("Cannot connect to port 6969");
+            throw new IllegalStateException("cannot connect to port 6969");
         }
+        return invocationBuilder;
+    }
+
+    private Invocation.Builder sendHttpsRequest(final String endpoint) throws Exception {
+
+        final TrustManager[] noopTrustManager = new TrustManager[] {new X509TrustManager() {
+
+            @Override
+            public X509Certificate[] getAcceptedIssuers() {
+                return new X509Certificate[0];
+            }
+
+            @Override
+            public void checkClientTrusted(final java.security.cert.X509Certificate[] certs, final String authType) {}
+
+            @Override
+            public void checkServerTrusted(final java.security.cert.X509Certificate[] certs, final String authType) {}
+        }};
+
+        final SSLContext sc = SSLContext.getInstance("TLSv1.2");
+        sc.init(null, noopTrustManager, new SecureRandom());
+        final ClientBuilder clientBuilder =
+                ClientBuilder.newBuilder().sslContext(sc).hostnameVerifier((host, session) -> true);
+        final Client client = clientBuilder.build();
+        final HttpAuthenticationFeature feature = HttpAuthenticationFeature.basic("healthcheck", "zb!XztG34");
+        client.register(feature);
 
-        return invocationBuilder.get(HealthCheckReport.class);
+        final WebTarget webTarget = client.target("https://localhost:6969/policy/pdpx/v1/" + endpoint);
+
+        final Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_JSON);
+
+        if (!NetworkUtil.isTcpPortOpen("localhost", 6969, 6, 10000L)) {
+            throw new IllegalStateException("cannot connect to port 6969");
+        }
+        return invocationBuilder;
+    }
+
+    private void updateXacmlPdpStatistics() {
+        XacmlPdpStatisticsManager.updateTotalPoliciesCount();
+        XacmlPdpStatisticsManager.updatePermitDecisionsCount();
+        XacmlPdpStatisticsManager.updateDenyDecisionsCount();
+        XacmlPdpStatisticsManager.updateIndeterminantDecisionsCount();
+        XacmlPdpStatisticsManager.updateNotApplicableDecisionsCount();
+    }
+
+    private void validateStatisticsReport(final StatisticsReport report, final int count, final int code) {
+        assertEquals(code, report.getCode());
+        assertEquals(count, report.getTotalPoliciesCount());
+        assertEquals(count, report.getPermitDecisionsCount());
+        assertEquals(count, report.getDenyDecisionsCount());
+        assertEquals(count, report.getIndeterminantDecisionsCount());
+        assertEquals(count, report.getNotApplicableDecisionsCount());
     }
 
-    private void validateReport(final String name, final String url, final boolean healthy, final int code,
-            final String message, final String reportString, final HealthCheckReport report) {
+    private void validateHealthCheckReport(final String name, final String url, final boolean healthy, final int code,
+            final String message, final HealthCheckReport report) {
         assertEquals(name, report.getName());
         assertEquals(url, report.getUrl());
         assertEquals(healthy, report.isHealthy());
         assertEquals(code, report.getCode());
         assertEquals(message, report.getMessage());
-        assertEquals(reportString, report.toString());
     }
 }
diff --git a/main/src/test/resources/parameters/XacmlPdpConfigParameters_Https.json b/main/src/test/resources/parameters/XacmlPdpConfigParameters_Https.json
new file mode 100644 (file)
index 0000000..b467fe4
--- /dev/null
@@ -0,0 +1,10 @@
+{
+    "name":"XacmlPdpGroup",
+    "restServerParameters":{
+        "host":"0.0.0.0",
+        "port":6969,
+        "userName":"healthcheck",
+        "password":"zb!XztG34",
+        "https":true
+    }
+}
\ No newline at end of file
diff --git a/main/src/test/resources/ssl/policy-keystore b/main/src/test/resources/ssl/policy-keystore
new file mode 100644 (file)
index 0000000..7d2b1ec
Binary files /dev/null and b/main/src/test/resources/ssl/policy-keystore differ