From 4b2ef1a5a9bf92aeb7edc1512f7a6cd8e1be99d8 Mon Sep 17 00:00:00 2001 From: Michael Mokry Date: Wed, 27 Feb 2019 07:42:20 -0600 Subject: [PATCH] Added HTTPS and CADI/AAF Support for PDP-X - 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 --- .../pdpx/main/parameters/RestServerBuilder.java | 85 +++++++++ .../pdpx/main/parameters/RestServerParameters.java | 37 +++- .../policy/pdpx/main/rest/XacmlPdpAafFilter.java | 38 ++++ .../pdpx/main/rest/XacmlPdpRestController.java | 111 +++++++++-- .../policy/pdpx/main/rest/XacmlPdpRestServer.java | 8 + .../org/onap/policy/pdpx/main/startstop/Main.java | 2 +- .../pdpx/main/parameters/CommonTestData.java | 13 +- .../parameters/TestXacmlPdpParameterGroup.java | 2 + .../pdpx/main/rest/TestXacmlPdpRestServer.java | 207 +++++++++++++++++---- .../parameters/XacmlPdpConfigParameters_Https.json | 10 + main/src/test/resources/ssl/policy-keystore | Bin 0 -> 4311 bytes 11 files changed, 445 insertions(+), 68 deletions(-) create mode 100644 main/src/main/java/org/onap/policy/pdpx/main/parameters/RestServerBuilder.java create mode 100644 main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpAafFilter.java create mode 100644 main/src/test/resources/parameters/XacmlPdpConfigParameters_Https.json create mode 100644 main/src/test/resources/ssl/policy-keystore 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 index 00000000..90bc09f9 --- /dev/null +++ b/main/src/main/java/org/onap/policy/pdpx/main/parameters/RestServerBuilder.java @@ -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; + } +} diff --git a/main/src/main/java/org/onap/policy/pdpx/main/parameters/RestServerParameters.java b/main/src/main/java/org/onap/policy/pdpx/main/parameters/RestServerParameters.java index e04a0f29..c452c758 100644 --- a/main/src/main/java/org/onap/policy/pdpx/main/parameters/RestServerParameters.java +++ b/main/src/main/java/org/onap/policy/pdpx/main/parameters/RestServerParameters.java @@ -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 index 00000000..5a4c3bc6 --- /dev/null +++ b/main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpAafFilter.java @@ -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 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 dc091f03..965753ae 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 @@ -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); } } diff --git a/main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpRestServer.java b/main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpRestServer.java index 90f0bfa1..eece1ffc 100644 --- a/main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpRestServer.java +++ b/main/src/main/java/org/onap/policy/pdpx/main/rest/XacmlPdpRestServer.java @@ -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; } diff --git a/main/src/main/java/org/onap/policy/pdpx/main/startstop/Main.java b/main/src/main/java/org/onap/policy/pdpx/main/startstop/Main.java index 91b38f9b..65e0a31e 100644 --- a/main/src/main/java/org/onap/policy/pdpx/main/startstop/Main.java +++ b/main/src/main/java/org/onap/policy/pdpx/main/startstop/Main.java @@ -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(); diff --git a/main/src/test/java/org/onap/policy/pdpx/main/parameters/CommonTestData.java b/main/src/test/java/org/onap/policy/pdpx/main/parameters/CommonTestData.java index f50871ec..1bf2294c 100644 --- a/main/src/test/java/org/onap/policy/pdpx/main/parameters/CommonTestData.java +++ b/main/src/test/java/org/onap/policy/pdpx/main/parameters/CommonTestData.java @@ -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; } diff --git a/main/src/test/java/org/onap/policy/pdpx/main/parameters/TestXacmlPdpParameterGroup.java b/main/src/test/java/org/onap/policy/pdpx/main/parameters/TestXacmlPdpParameterGroup.java index 4b9db99d..48606c98 100644 --- a/main/src/test/java/org/onap/policy/pdpx/main/parameters/TestXacmlPdpParameterGroup.java +++ b/main/src/test/java/org/onap/policy/pdpx/main/parameters/TestXacmlPdpParameterGroup.java @@ -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 diff --git a/main/src/test/java/org/onap/policy/pdpx/main/rest/TestXacmlPdpRestServer.java b/main/src/test/java/org/onap/policy/pdpx/main/rest/TestXacmlPdpRestServer.java index 2170c8a3..0f608e29 100644 --- a/main/src/test/java/org/onap/policy/pdpx/main/rest/TestXacmlPdpRestServer.java +++ b/main/src/test/java/org/onap/policy/pdpx/main/rest/TestXacmlPdpRestServer.java @@ -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 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 index 00000000..b467fe4c --- /dev/null +++ b/main/src/test/resources/parameters/XacmlPdpConfigParameters_Https.json @@ -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 index 0000000000000000000000000000000000000000..7d2b1eccea2831fd2ecfe01c2368733e109c89d8 GIT binary patch literal 4311 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 literal 0 HcmV?d00001 -- 2.16.6