summaryrefslogtreecommitdiffstats
path: root/sidecar/rproxy/src
diff options
context:
space:
mode:
Diffstat (limited to 'sidecar/rproxy/src')
-rw-r--r--sidecar/rproxy/src/main/bin/start.sh31
-rw-r--r--sidecar/rproxy/src/main/docker/Dockerfile36
-rw-r--r--sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/ReverseProxyApplication.java182
-rw-r--r--sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/ReverseProxyAuthorizationFilter.java211
-rw-r--r--sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/ReverseProxyService.java166
-rw-r--r--sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/config/CadiProperties.java41
-rw-r--r--sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/config/ForwardProxyProperties.java67
-rw-r--r--sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/config/PrimaryServiceProperties.java58
-rw-r--r--sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/config/ReverseProxySSLProperties.java56
-rw-r--r--sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/config/ReverseProxyURIAuthorizationProperties.java38
-rw-r--r--sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/logging/ReverseProxyEntryExitLoggingAspect.java45
-rw-r--r--sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/logging/ReverseProxyMethodLogTime.java51
-rw-r--r--sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/logging/ReverseProxyMethodLogTimeAnnotation.java30
-rw-r--r--sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/mocks/ReverseProxyMockCadiFilter.java143
-rw-r--r--sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/utils/ReverseProxyAuthorization.java34
-rw-r--r--sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/utils/ReverseProxyUtils.java29
-rw-r--r--sidecar/rproxy/src/main/resources/application.properties22
-rw-r--r--sidecar/rproxy/src/test/java/org/onap/aaf/rproxy/PermissionMatchingTest.java262
-rw-r--r--sidecar/rproxy/src/test/java/org/onap/aaf/rproxy/ReverseProxyApplicationTest.java158
-rw-r--r--sidecar/rproxy/src/test/java/org/onap/aaf/rproxy/ReverseProxyIT.java48
-rw-r--r--sidecar/rproxy/src/test/java/org/onap/aaf/rproxy/ReverseProxyTestConfig.java42
-rw-r--r--sidecar/rproxy/src/test/resources/forward-proxy.properties4
-rw-r--r--sidecar/rproxy/src/test/resources/primary-service.properties3
23 files changed, 1757 insertions, 0 deletions
diff --git a/sidecar/rproxy/src/main/bin/start.sh b/sidecar/rproxy/src/main/bin/start.sh
new file mode 100644
index 0000000..4330af5
--- /dev/null
+++ b/sidecar/rproxy/src/main/bin/start.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+# ============LICENSE_START=======================================================
+# org.onap.aaf
+# ================================================================================
+# Copyright © 2018 European Software Marketing Ltd.
+# ================================================================================
+# 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=========================================================
+
+BASEDIR="/opt/app/rproxy"
+
+if [ -z "${KEY_STORE_PASSWORD}" ]; then
+ echo "KEY_STORE_PASSWORD must be set in order to start up process"
+ exit 1
+fi
+
+PROPS="-DKEY_STORE_PASSWORD=${KEY_STORE_PASSWORD}"
+JVM_MAX_HEAP=${MAX_HEAP:-1024}
+
+exec java -Xmx${JVM_MAX_HEAP}m ${PROPS} -jar ${BASEDIR}/rproxy-exec.jar
diff --git a/sidecar/rproxy/src/main/docker/Dockerfile b/sidecar/rproxy/src/main/docker/Dockerfile
new file mode 100644
index 0000000..56b32fa
--- /dev/null
+++ b/sidecar/rproxy/src/main/docker/Dockerfile
@@ -0,0 +1,36 @@
+FROM ubuntu:14.04
+
+ARG MICRO_HOME=/opt/app/rproxy
+ARG BIN_HOME=$MICRO_HOME/bin
+ARG JAR_FILE=rproxy-exec.jar
+
+RUN apt-get update
+
+# Install and setup java8
+RUN apt-get update && apt-get install -y software-properties-common
+## sudo -E is required to preserve the environment. If you remove that line, it will most like freeze at this step
+RUN sudo -E add-apt-repository ppa:openjdk-r/ppa && apt-get update && apt-get install -y openjdk-8-jdk
+
+RUN sudo dpkg --purge --force-depends ca-certificates-java
+RUN sudo apt-get install ca-certificates-java
+
+## Setup JAVA_HOME, this is useful for docker commandline
+ENV JAVA_HOME usr/lib/jvm/java-8-openjdk-$(dpkg --print-architecture)
+RUN export JAVA_HOME
+
+# Build up the deployment folder structure
+RUN mkdir -p $MICRO_HOME
+COPY ${JAR_FILE} $MICRO_HOME
+RUN mkdir -p $BIN_HOME
+COPY *.sh $BIN_HOME
+RUN chmod 755 $BIN_HOME/*
+RUN ln -s /logs $MICRO_HOME/logs
+RUN mkdir /logs
+# Create the appuser
+RUN groupadd -r appgroup && \
+ useradd -r -u 1001 -g appgroup appuser && \
+ chown -R appuser:appgroup $MICRO_HOME && \
+ chmod 777 /logs
+USER appuser
+
+CMD ["/opt/app/rproxy/bin/start.sh"]
diff --git a/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/ReverseProxyApplication.java b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/ReverseProxyApplication.java
new file mode 100644
index 0000000..ebceceb
--- /dev/null
+++ b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/ReverseProxyApplication.java
@@ -0,0 +1,182 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aaf
+ * ================================================================================
+ * Copyright © 2018 European Software Marketing Ltd.
+ * ================================================================================
+ * 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.aaf.rproxy;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.util.HashMap;
+import java.util.Properties;
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import javax.net.ssl.SSLContext;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.eclipse.jetty.util.security.Password;
+import org.onap.aaf.cadi.filter.CadiFilter;
+import org.onap.aaf.rproxy.config.ForwardProxyProperties;
+import org.onap.aaf.rproxy.config.PrimaryServiceProperties;
+import org.onap.aaf.rproxy.config.ReverseProxySSLProperties;
+import org.onap.aaf.rproxy.mocks.ReverseProxyMockCadiFilter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.boot.web.servlet.RegistrationBean;
+import org.springframework.boot.web.servlet.ServletComponentScan;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.core.env.Environment;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.util.ResourceUtils;
+import org.springframework.web.client.RestTemplate;
+
+@SpringBootApplication
+@ServletComponentScan
+@EnableConfigurationProperties(ReverseProxySSLProperties.class)
+@PropertySource("file:${CONFIG_HOME}/reverse-proxy.properties")
+public class ReverseProxyApplication extends SpringBootServletInitializer {
+
+ private static final String CADI_TRUSTSTORE_PASS = "cadi_truststore_password";
+
+ @Autowired
+ private Environment env;
+
+ /**
+ * Spring Boot Initialisation.
+ *
+ * @param args main args
+ */
+ public static void main(String[] args) {
+ String keyStorePassword = System.getProperty("KEY_STORE_PASSWORD");
+ if (keyStorePassword == null || keyStorePassword.isEmpty()) {
+ throw new IllegalArgumentException("Env property KEY_STORE_PASSWORD not set");
+ }
+ HashMap<String, Object> props = new HashMap<>();
+ props.put("server.ssl.key-store-password", Password.deobfuscate(keyStorePassword));
+ new ReverseProxyApplication()
+ .configure(new SpringApplicationBuilder(ReverseProxyApplication.class).properties(props)).run(args);
+ }
+
+ /**
+ * Set required trust store system properties using values from application.properties
+ */
+ @PostConstruct
+ public void setSystemProperties() {
+ String keyStorePath = env.getProperty("server.ssl.key-store");
+ if (keyStorePath != null) {
+ String keyStorePassword = env.getProperty("server.ssl.key-store-password");
+
+ if (keyStorePassword != null) {
+ System.setProperty("javax.net.ssl.keyStore", keyStorePath);
+ System.setProperty("javax.net.ssl.keyStorePassword", keyStorePassword);
+ System.setProperty("javax.net.ssl.trustStore", keyStorePath);
+ System.setProperty("javax.net.ssl.trustStorePassword", keyStorePassword);
+ } else {
+ throw new IllegalArgumentException("Env property server.ssl.key-store-password not set");
+ }
+ }
+ }
+
+ @Resource
+ private ReverseProxySSLProperties reverseProxySSLProperties;
+
+ @Resource
+ Properties cadiProps;
+
+ @Bean(name = "ForwardProxyProperties")
+ public ForwardProxyProperties forwardProxyProperties() {
+ return new ForwardProxyProperties();
+ }
+
+ @Bean(name = "PrimaryServiceProperties")
+ public PrimaryServiceProperties primaryServiceProperties() {
+ return new PrimaryServiceProperties();
+ }
+
+ @Profile("secure")
+ @Bean
+ public RestTemplate restTemplate(RestTemplateBuilder builder) throws GeneralSecurityException, IOException {
+ return new RestTemplate(new HttpComponentsClientHttpRequestFactory(getClientBuilder().build()));
+ }
+
+ @Profile("noHostVerification")
+ @Bean
+ public RestTemplate restTemplateNoHostVerification(RestTemplateBuilder builder)
+ throws GeneralSecurityException, IOException {
+ return new RestTemplate(new HttpComponentsClientHttpRequestFactory(
+ getClientBuilder().setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE).build()));
+ }
+
+ private HttpClientBuilder getClientBuilder() throws GeneralSecurityException, IOException {
+
+ SSLContext sslContext = SSLContextBuilder.create()
+ .loadKeyMaterial(ResourceUtils.getFile(reverseProxySSLProperties.getClientcert()),
+ reverseProxySSLProperties.getKeystorePassword().toCharArray(),
+ reverseProxySSLProperties.getKeystorePassword().toCharArray())
+ .loadTrustMaterial(ResourceUtils.getFile(reverseProxySSLProperties.getKeystore()),
+ reverseProxySSLProperties.getKeystorePassword().toCharArray())
+ .build();
+
+ return HttpClients.custom().setSSLContext(sslContext);
+ }
+
+ @Profile("cadi")
+ @Bean
+ public FilterRegistrationBean<CadiFilter> registerCADIFilter() {
+
+ FilterRegistrationBean<CadiFilter> filterRegistrationBean = new FilterRegistrationBean<>();
+
+ filterRegistrationBean.setFilter(new CadiFilter());
+ filterRegistrationBean.addUrlPatterns("/*");
+ filterRegistrationBean.setName("CADIFilter");
+ filterRegistrationBean.setOrder(RegistrationBean.HIGHEST_PRECEDENCE);
+
+ // Deobfuscate truststore password
+ String trustStorePassword = cadiProps.getProperty(CADI_TRUSTSTORE_PASS);
+ if (trustStorePassword != null) {
+ cadiProps.setProperty(CADI_TRUSTSTORE_PASS, Password.deobfuscate(trustStorePassword));
+ }
+
+ // Add filter init params
+ cadiProps.forEach((k, v) -> filterRegistrationBean.addInitParameter((String) k, (String) v));
+
+ return filterRegistrationBean;
+ }
+
+ @Profile("mockCadi")
+ @Bean
+ public FilterRegistrationBean<ReverseProxyMockCadiFilter> registerMockCADIFilter() {
+
+ FilterRegistrationBean<ReverseProxyMockCadiFilter> filterRegistrationBean = new FilterRegistrationBean<>();
+
+ filterRegistrationBean.setFilter(new ReverseProxyMockCadiFilter());
+ filterRegistrationBean.addUrlPatterns("/*");
+ filterRegistrationBean.setName("CADIFilter");
+ filterRegistrationBean.setOrder(RegistrationBean.HIGHEST_PRECEDENCE);
+
+ return filterRegistrationBean;
+ }
+}
diff --git a/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/ReverseProxyAuthorizationFilter.java b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/ReverseProxyAuthorizationFilter.java
new file mode 100644
index 0000000..6374c9d
--- /dev/null
+++ b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/ReverseProxyAuthorizationFilter.java
@@ -0,0 +1,211 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aaf
+ * ================================================================================
+ * Copyright © 2018 European Software Marketing Ltd.
+ * ================================================================================
+ * 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.aaf.rproxy;
+
+import com.google.gson.Gson;
+import com.google.gson.stream.JsonReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.Resource;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.http.HttpStatus;
+import org.onap.aaf.cadi.CadiWrap;
+import org.onap.aaf.cadi.Permission;
+import org.onap.aaf.rproxy.config.ReverseProxyURIAuthorizationProperties;
+import org.onap.aaf.rproxy.utils.ReverseProxyAuthorization;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+@Component
+@Order(1)
+@EnableConfigurationProperties(ReverseProxyURIAuthorizationProperties.class)
+public class ReverseProxyAuthorizationFilter implements Filter {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ReverseProxyAuthorizationFilter.class);
+
+ private ReverseProxyAuthorization[] reverseProxyAuthorizations = new ReverseProxyAuthorization[] {};
+
+ @Resource
+ private ReverseProxyURIAuthorizationProperties reverseProxyURIAuthorizationProperties;
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+
+ // Read in the URI Authorisation configuration file
+ String authFilePath = reverseProxyURIAuthorizationProperties.getConfigurationFile();
+ if (authFilePath != null) {
+ try (InputStream inputStream =
+ new FileInputStream(new File(reverseProxyURIAuthorizationProperties.getConfigurationFile()));
+ JsonReader jsonReader = new JsonReader(new InputStreamReader(inputStream))) {
+ reverseProxyAuthorizations = new Gson().fromJson(jsonReader, ReverseProxyAuthorization[].class);
+ } catch (IOException e) {
+ throw new ServletException("Authorizations config file not found.", e);
+ }
+ }
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
+ throws IOException, ServletException {
+
+ CadiWrap cadiWrap = (CadiWrap) servletRequest;
+ Principal principal = cadiWrap.getUserPrincipal();
+ List<Permission> grantedPermissions = new ArrayList<>();
+ cadiWrap.getLur().fishAll(principal, grantedPermissions);
+
+ if (LOGGER.isDebugEnabled()) {
+ logNeededPermissions();
+ }
+
+ String requestPath;
+ try {
+ requestPath = new URI(((HttpServletRequest) servletRequest).getRequestURI()).getPath();
+ } catch (URISyntaxException e) {
+ throw new ServletException("Request URI not valid", e);
+ }
+
+ if (authorizeRequest(grantedPermissions, requestPath)) {
+ LOGGER.info("Authorized");
+ filterChain.doFilter(servletRequest, servletResponse);
+ } else {
+ LOGGER.info("Unauthorized");
+ ((HttpServletResponse) servletResponse).setStatus(HttpStatus.FORBIDDEN_403);
+ ((HttpServletResponse) servletResponse).setContentType("application/json");
+ ((HttpServletResponse) servletResponse).sendError(HttpStatus.FORBIDDEN_403,
+ "Sorry, the request is not allowed");
+ }
+ }
+
+ /**
+ * Check if the granted permissions for the request path matches the configured needed permissions.
+ *
+ * @param grantedPermissions The granted permissions for the request path
+ * @param requestPath The request path
+ * @return true if permissions match
+ */
+ private boolean authorizeRequest(List<Permission> grantedPermissions, String requestPath) {
+ boolean authorized = false;
+ for (ReverseProxyAuthorization reverseProxyAuthorization : reverseProxyAuthorizations) {
+ if (requestPath.matches(reverseProxyAuthorization.getUri())) {
+ LOGGER.debug("The URI:{} matches:{}", requestPath, reverseProxyAuthorization.getUri());
+ if (checkPermissionsMatch(grantedPermissions, reverseProxyAuthorization)) {
+ authorized = true;
+ break;
+ }
+ } else {
+ LOGGER.debug("The URI:{} doesn't match any in the configuration:{}", requestPath,
+ reverseProxyAuthorization.getUri());
+ }
+ }
+ return authorized;
+ }
+
+ /**
+ * Check all needed permissions match the granted permissions.
+ *
+ * @param grantedPermissions the granted permissions
+ * @param reverseProxyAuthorization the bean that contains the needed permissions
+ * @return true if all needed permissions match
+ */
+ private boolean checkPermissionsMatch(List<Permission> grantedPermissions,
+ ReverseProxyAuthorization reverseProxyAuthorization) {
+
+ boolean matchedAllPermissions = true;
+ for (String neededPermission : reverseProxyAuthorization.getPermissions()) {
+
+ // Check needed permission is granted
+ boolean matchedNeededPermission = false;
+ for (Permission grantedPermission : grantedPermissions) {
+ if (checkGrantedPermission(neededPermission, grantedPermission.getKey())) {
+ LOGGER.debug("Permission match found - needed permission:{}, granted permission:{}",
+ neededPermission, grantedPermission.getKey());
+ matchedNeededPermission = true;
+ break;
+ }
+ }
+ if (!matchedNeededPermission) {
+ matchedAllPermissions = false;
+ break;
+ }
+ }
+ return matchedAllPermissions;
+ }
+
+ /**
+ * Check whether an AAF style permission matches a needed permission. Wildcards (*) are supported.
+ *
+ * @param neededPermission, the needed permission
+ * @param grantedPermission, the granted permission
+ *
+ * @return true if the needed permission matches a granted permission
+ */
+ private boolean checkGrantedPermission(String neededPermission, String grantedPermission) {
+ boolean permissionMatch = false;
+ if (grantedPermission.matches(neededPermission)) {
+ permissionMatch = true;
+ } else if (grantedPermission.contains("*")) {
+ String[] splitNeededPermission = neededPermission.split("\\\\\\|");
+ String[] splitGrantedPermission = grantedPermission.split("\\|");
+ if ((splitGrantedPermission[0].matches(splitNeededPermission[0]))
+ && (splitGrantedPermission[1].equals("*")
+ || splitGrantedPermission[1].matches(splitNeededPermission[1]))
+ && (splitGrantedPermission[2].equals("*")
+ || splitGrantedPermission[2].matches(splitNeededPermission[2]))) {
+ permissionMatch = true;
+ }
+ }
+ return permissionMatch;
+ }
+
+ /**
+ * Log the needed permissions for each URL configured.
+ */
+ private void logNeededPermissions() {
+ for (ReverseProxyAuthorization reverseProxyAuthorization : reverseProxyAuthorizations) {
+ LOGGER.debug("URI For authorization: {}", reverseProxyAuthorization.getUri());
+ for (String permission : reverseProxyAuthorization.getPermissions()) {
+ LOGGER.debug("\t Needed permission:{}", permission);
+ }
+ }
+ }
+
+ @Override
+ public void destroy() {
+ // No op
+ }
+}
diff --git a/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/ReverseProxyService.java b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/ReverseProxyService.java
new file mode 100644
index 0000000..b5c000c
--- /dev/null
+++ b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/ReverseProxyService.java
@@ -0,0 +1,166 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aaf
+ * ================================================================================
+ * Copyright © 2018 European Software Marketing Ltd.
+ * ================================================================================
+ * 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.aaf.rproxy;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Enumeration;
+import java.util.UUID;
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import org.onap.aaf.fproxy.data.CredentialCacheData;
+import org.onap.aaf.fproxy.data.CredentialCacheData.CredentialType;
+import org.onap.aaf.rproxy.config.ForwardProxyProperties;
+import org.onap.aaf.rproxy.config.PrimaryServiceProperties;
+import org.onap.aaf.rproxy.logging.ReverseProxyMethodLogTimeAnnotation;
+import org.onap.aaf.rproxy.utils.ReverseProxyUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.actuate.endpoint.InvalidEndpointRequestException;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
+
+@RestController
+@EnableConfigurationProperties({ForwardProxyProperties.class, PrimaryServiceProperties.class})
+public class ReverseProxyService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ReverseProxyService.class);
+
+ private String validatedTransactionId;
+
+ @Resource(name = "ForwardProxyProperties")
+ private ForwardProxyProperties forwardProxyProperties;
+
+ @Resource(name = "PrimaryServiceProperties")
+ private PrimaryServiceProperties primaryServiceProperties;
+
+ @Autowired
+ private RestTemplate restTemplate;
+
+ @Value("${transactionid.header.name}")
+ private String transactionIdHeader;
+
+ @RequestMapping("/**")
+ @ReverseProxyMethodLogTimeAnnotation
+ public ResponseEntity<String> handleRequest(HttpServletRequest request,
+ @RequestHeader(value = "${transactionid.header.name}", defaultValue = "") String transactionId,
+ @RequestBody(required = false) String requestBody, HttpMethod requestMethod) throws URISyntaxException {
+ validatedTransactionId = getValidTransactionId(transactionId);
+
+ // Extract Request Permissions and store in Forward Proxy cache
+ CredentialCacheData credentialCacheData = getCredentialDataFromRequest(request);
+ if (credentialCacheData != null) {
+ postCredentialsToCache(credentialCacheData);
+ }
+
+ // Call out to Primary Service & Return Response
+ URI requestURI = new URI(request.getRequestURI());
+
+ LOGGER.debug("Request URI: {}", request.getRequestURI());
+
+ // Get Request Endpoint & substitute in local values
+ URI primaryServiceURI = new URI(primaryServiceProperties.getProtocol(), requestURI.getUserInfo(),
+ primaryServiceProperties.getHost(), Integer.parseInt(primaryServiceProperties.getPort()),
+ requestURI.getPath(), requestURI.getQuery(), requestURI.getFragment());
+
+ LOGGER.debug("Primary Service URI:{}, HTTP Method: {}", primaryServiceURI, requestMethod);
+
+ HttpHeaders requestHeaders = setForwardedRequestHeaders(request);
+ HttpEntity<String> httpEntity = new HttpEntity<>(requestBody, requestHeaders);
+
+ return restTemplate.exchange(primaryServiceURI, requestMethod, httpEntity, String.class);
+ }
+
+ private String getValidTransactionId(String transactionId) {
+ LOGGER.debug("Request transaction ID: {}", transactionId);
+ if (transactionId == null || !ReverseProxyUtils.validTransactionId(transactionId)) {
+ transactionId = UUID.randomUUID().toString();
+ }
+ LOGGER.debug("Validated transaction ID: {}", transactionId);
+ return transactionId;
+ }
+
+ private HttpHeaders setForwardedRequestHeaders(HttpServletRequest httpServletRequest) {
+ HttpHeaders httpHeaders = new HttpHeaders();
+ Enumeration<String> headerNames = httpServletRequest.getHeaderNames();
+ while (headerNames.hasMoreElements()) {
+ String headerName = headerNames.nextElement();
+ if (!headerName.equals(transactionIdHeader)) {
+ httpHeaders.set(headerName, httpServletRequest.getHeader(headerName));
+ }
+ }
+ // Always set transaction ID
+ httpHeaders.set(transactionIdHeader, validatedTransactionId);
+
+ return httpHeaders;
+ }
+
+ /**
+ * Retrieves credential data from request.
+ *
+ * @param request The request to retrieve credentials from
+ * @return The retrieved credential data, or null if no credentials are found in request
+ */
+ private CredentialCacheData getCredentialDataFromRequest(HttpServletRequest request) {
+ CredentialCacheData credentialCacheData = null;
+ String authValue = request.getHeader(HttpHeaders.AUTHORIZATION);
+ if (authValue != null) {
+ credentialCacheData = new CredentialCacheData(HttpHeaders.AUTHORIZATION, authValue, CredentialType.HEADER);
+ }
+ return credentialCacheData;
+ }
+
+ /**
+ * Posts credential data to credential cache endpoint
+ *
+ * @param credentialCacheData The credential data to post
+ * @throws URISyntaxException
+ */
+ private void postCredentialsToCache(CredentialCacheData credentialCacheData) throws URISyntaxException {
+ URI forwardProxyURI = new URI(forwardProxyProperties.getProtocol(), null, forwardProxyProperties.getHost(),
+ forwardProxyProperties.getPort(), forwardProxyProperties.getCacheurl() + "/" + validatedTransactionId,
+ null, null);
+
+ ResponseEntity<String> response =
+ restTemplate.postForEntity(forwardProxyURI, credentialCacheData, String.class);
+
+ if (!response.getStatusCode().is2xxSuccessful()) {
+ throw new InvalidEndpointRequestException("Error posting to credential cache.",
+ "Status code: " + response.getStatusCodeValue() + " Message: " + response.getBody());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getName() + ": Forward proxy host:" + forwardProxyProperties.getHost()
+ + ": Primary service host:" + primaryServiceProperties.getHost();
+
+ }
+}
diff --git a/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/config/CadiProperties.java b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/config/CadiProperties.java
new file mode 100644
index 0000000..0afb506
--- /dev/null
+++ b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/config/CadiProperties.java
@@ -0,0 +1,41 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aaf
+ * ================================================================================
+ * Copyright © 2018 European Software Marketing Ltd.
+ * ================================================================================
+ * 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.aaf.rproxy.config;
+
+import java.net.MalformedURLException;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.beans.factory.config.PropertiesFactoryBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.UrlResource;
+
+@Configuration
+public class CadiProperties {
+
+ @Value("${CONFIG_HOME}")
+ private String configHome;
+
+ @Bean(name = "cadiProps")
+ public PropertiesFactoryBean mapper() throws MalformedURLException {
+ PropertiesFactoryBean bean = new PropertiesFactoryBean();
+ bean.setLocation(new UrlResource("file:" + configHome + "/cadi.properties"));
+ return bean;
+ }
+}
diff --git a/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/config/ForwardProxyProperties.java b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/config/ForwardProxyProperties.java
new file mode 100644
index 0000000..3607797
--- /dev/null
+++ b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/config/ForwardProxyProperties.java
@@ -0,0 +1,67 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aaf
+ * ================================================================================
+ * Copyright © 2018 European Software Marketing Ltd.
+ * ================================================================================
+ * 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.aaf.rproxy.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+
+@Configuration
+@PropertySource("file:${CONFIG_HOME}/forward-proxy.properties")
+@ConfigurationProperties(prefix = "forward-proxy")
+public class ForwardProxyProperties {
+
+ private String protocol;
+ private String host;
+ private int port;
+ private String cacheurl;
+
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ public void setProtocol(String protocol) {
+ this.protocol = protocol;
+ }
+
+ public String getCacheurl() {
+ return cacheurl;
+ }
+
+ public void setCacheurl(String cacheurl) {
+ this.cacheurl = cacheurl;
+ }
+}
diff --git a/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/config/PrimaryServiceProperties.java b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/config/PrimaryServiceProperties.java
new file mode 100644
index 0000000..994dc55
--- /dev/null
+++ b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/config/PrimaryServiceProperties.java
@@ -0,0 +1,58 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aaf
+ * ================================================================================
+ * Copyright © 2018 European Software Marketing Ltd.
+ * ================================================================================
+ * 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.aaf.rproxy.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+
+@Configuration
+@PropertySource("file:${CONFIG_HOME}/primary-service.properties")
+@ConfigurationProperties(prefix = "primary-service")
+public class PrimaryServiceProperties {
+
+ private String protocol;
+ private String host;
+ private String port;
+
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ public String getPort() {
+ return port;
+ }
+
+ public void setPort(String port) {
+ this.port = port;
+ }
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ public void setProtocol(String protocol) {
+ this.protocol = protocol;
+ }
+}
diff --git a/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/config/ReverseProxySSLProperties.java b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/config/ReverseProxySSLProperties.java
new file mode 100644
index 0000000..31e8a0d
--- /dev/null
+++ b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/config/ReverseProxySSLProperties.java
@@ -0,0 +1,56 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aaf
+ * ================================================================================
+ * Copyright © 2018 European Software Marketing Ltd.
+ * ================================================================================
+ * 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.aaf.rproxy.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@ConfigurationProperties(prefix = "server.ssl")
+public class ReverseProxySSLProperties {
+
+ private String keystore;
+ private String keystorePassword;
+ private String clientcert;
+
+ public String getKeystore() {
+ return keystore;
+ }
+
+ public void setKeystore(String keystore) {
+ this.keystore = keystore;
+ }
+
+ public String getClientcert() {
+ return clientcert;
+ }
+
+ public void setClientcert(String clientcert) {
+ this.clientcert = clientcert;
+ }
+
+ public String getKeystorePassword() {
+ return keystorePassword;
+ }
+
+ public void setKeystorePassword(String keystorePassword) {
+ this.keystorePassword = keystorePassword;
+ }
+}
diff --git a/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/config/ReverseProxyURIAuthorizationProperties.java b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/config/ReverseProxyURIAuthorizationProperties.java
new file mode 100644
index 0000000..da25d9c
--- /dev/null
+++ b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/config/ReverseProxyURIAuthorizationProperties.java
@@ -0,0 +1,38 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aaf
+ * ================================================================================
+ * Copyright © 2018 European Software Marketing Ltd.
+ * ================================================================================
+ * 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.aaf.rproxy.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@ConfigurationProperties(prefix = "uri.authorization")
+public class ReverseProxyURIAuthorizationProperties {
+
+ private String configurationFile;
+
+ public String getConfigurationFile() {
+ return configurationFile;
+ }
+
+ public void setConfigurationFile(String configurationFile) {
+ this.configurationFile = configurationFile;
+ }
+}
diff --git a/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/logging/ReverseProxyEntryExitLoggingAspect.java b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/logging/ReverseProxyEntryExitLoggingAspect.java
new file mode 100644
index 0000000..174420c
--- /dev/null
+++ b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/logging/ReverseProxyEntryExitLoggingAspect.java
@@ -0,0 +1,45 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aaf
+ * ================================================================================
+ * Copyright © 2018 European Software Marketing Ltd.
+ * ================================================================================
+ * 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.aaf.rproxy.logging;
+
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.After;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Aspect
+@Component
+public class ReverseProxyEntryExitLoggingAspect {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ReverseProxyEntryExitLoggingAspect.class);
+
+ @Before("execution(* org.onap.platform.security.*.*(..))")
+ public void before(JoinPoint joinPoint) {
+ LOGGER.info("Entry of {}#{}", joinPoint.getTarget().getClass(), joinPoint.getSignature().getName());
+ }
+
+ @After("execution(* org.onap.platform.security.*.*(..))")
+ public void after(JoinPoint joinPoint) {
+ LOGGER.info("Exit of {}#{}", joinPoint.getTarget().getClass(), joinPoint.getSignature().getName());
+ }
+}
diff --git a/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/logging/ReverseProxyMethodLogTime.java b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/logging/ReverseProxyMethodLogTime.java
new file mode 100644
index 0000000..00cfba6
--- /dev/null
+++ b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/logging/ReverseProxyMethodLogTime.java
@@ -0,0 +1,51 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aaf
+ * ================================================================================
+ * Copyright © 2018 European Software Marketing Ltd.
+ * ================================================================================
+ * 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.aaf.rproxy.logging;
+
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Configuration;
+
+@Aspect
+@Configuration
+public class ReverseProxyMethodLogTime {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ReverseProxyMethodLogTime.class);
+
+ @Around("@annotation(org.onap.aaf.rproxy.logging.ReverseProxyMethodLogTimeAnnotation)")
+ public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
+
+ long startTime = System.currentTimeMillis();
+
+ Object object = joinPoint.proceed();
+
+ long duration = System.currentTimeMillis() - startTime;
+
+ LOGGER.info("Time taken by {} is {} ms", joinPoint, duration);
+
+ return object;
+ }
+
+
+
+}
diff --git a/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/logging/ReverseProxyMethodLogTimeAnnotation.java b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/logging/ReverseProxyMethodLogTimeAnnotation.java
new file mode 100644
index 0000000..55f00f4
--- /dev/null
+++ b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/logging/ReverseProxyMethodLogTimeAnnotation.java
@@ -0,0 +1,30 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aaf
+ * ================================================================================
+ * Copyright © 2018 European Software Marketing Ltd.
+ * ================================================================================
+ * 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.aaf.rproxy.logging;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ReverseProxyMethodLogTimeAnnotation {
+}
diff --git a/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/mocks/ReverseProxyMockCadiFilter.java b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/mocks/ReverseProxyMockCadiFilter.java
new file mode 100644
index 0000000..10bfc3e
--- /dev/null
+++ b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/mocks/ReverseProxyMockCadiFilter.java
@@ -0,0 +1,143 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aaf
+ * ================================================================================
+ * Copyright © 2018 European Software Marketing Ltd.
+ * ================================================================================
+ * 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.aaf.rproxy.mocks;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.List;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import org.onap.aaf.cadi.CadiWrap;
+import org.onap.aaf.cadi.Lur;
+import org.onap.aaf.cadi.Permission;
+import org.onap.aaf.cadi.aaf.AAFPermission;
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+import org.onap.aaf.cadi.taf.TafResp;
+
+public class ReverseProxyMockCadiFilter implements Filter {
+
+ private FakeLur fakeLur = new FakeLur();
+
+ static class FakeLur implements Lur {
+
+ @Override
+ public void fishAll(Principal bait, List<Permission> permissions) {
+
+ final String WildcardPermissionType = "test.wildcard.access";
+ final String MultiplePermissionType = "test.multiple.access";
+ final String TestAuthAccessPermissionType = "test.auth.access";
+ final String PermissionAction = "permission";
+
+ String principalName = bait.getName();
+
+ if (principalName != null && principalName.equals("UserWithInstanceActionWildcardPermissionGranted")) {
+ permissions.add(new AAFPermission(null, WildcardPermissionType, "*", "*"));
+ }
+ else
+ if (principalName != null && principalName.equals("UserWithInstanceWildcardPermissionGranted")) {
+ permissions.add(new AAFPermission(null, WildcardPermissionType, "*", PermissionAction));
+ }
+ else
+ if (principalName != null && principalName.equals("UserWithActionWildcardPermissionGranted")) {
+ permissions.add(new AAFPermission(null, WildcardPermissionType, "first", "*"));
+ }
+ else {
+
+ // For single permission test
+ permissions.add(new AAFPermission(null, "test.single.access", "single", PermissionAction));
+
+ // For multiple permission test
+ permissions.add(new AAFPermission(null, MultiplePermissionType, "first", PermissionAction));
+ permissions.add(new AAFPermission(null, MultiplePermissionType, "second", PermissionAction));
+ permissions.add(new AAFPermission(null, MultiplePermissionType, "third", PermissionAction));
+
+ // For transaction id test
+ permissions.add(new AAFPermission(null, TestAuthAccessPermissionType, "rest", "write"));
+ permissions.add(new AAFPermission(null, TestAuthAccessPermissionType, "rpc", "write"));
+ }
+ }
+
+ @Override
+ public Permission createPerm(String p) {
+ return null;
+ }
+
+ @Override
+ public boolean fish(Principal bait, Permission... pond) {
+ return false;
+ }
+
+ @Override
+ public void destroy() {
+ // Mock implementation
+ }
+
+ @Override
+ public boolean handlesExclusively(Permission... pond) {
+ return false;
+ }
+
+ @Override
+ public boolean handles(Principal principal) {
+ return false;
+ }
+
+ @Override
+ public void clear(Principal p, StringBuilder report) {
+ // Mock implementation
+ }
+
+ }
+
+ @Override
+ public void destroy() {
+ // Mock implementation
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
+ throws IOException, ServletException {
+
+ String userName = ((HttpServletRequest)servletRequest).getHeader("PermissionsUser");
+
+ TaggedPrincipal mockTaggedPrincipal = mock(TaggedPrincipal.class);
+ when(mockTaggedPrincipal.getName()).thenReturn(userName);
+
+ TafResp tafResponseMock = mock(TafResp.class);
+ when(tafResponseMock.getPrincipal()).thenReturn(mockTaggedPrincipal);
+
+ CadiWrap cadiWrap = new CadiWrap((HttpServletRequest) servletRequest, tafResponseMock, fakeLur);
+ filterChain.doFilter(cadiWrap, servletResponse);
+ }
+
+ @Override
+ public void init(FilterConfig arg0) throws ServletException {
+ // Mock implementation
+ }
+
+}
diff --git a/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/utils/ReverseProxyAuthorization.java b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/utils/ReverseProxyAuthorization.java
new file mode 100644
index 0000000..6c5baa2
--- /dev/null
+++ b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/utils/ReverseProxyAuthorization.java
@@ -0,0 +1,34 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aaf
+ * ================================================================================
+ * Copyright © 2018 European Software Marketing Ltd.
+ * ================================================================================
+ * 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.aaf.rproxy.utils;
+
+public class ReverseProxyAuthorization {
+
+ private String uri;
+ private String[] permissions;
+
+ public String getUri() {
+ return uri;
+ }
+
+ public String[] getPermissions() {
+ return permissions;
+ }
+}
diff --git a/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/utils/ReverseProxyUtils.java b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/utils/ReverseProxyUtils.java
new file mode 100644
index 0000000..d2d1ba4
--- /dev/null
+++ b/sidecar/rproxy/src/main/java/org/onap/aaf/rproxy/utils/ReverseProxyUtils.java
@@ -0,0 +1,29 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aaf
+ * ================================================================================
+ * Copyright © 2018 European Software Marketing Ltd.
+ * ================================================================================
+ * 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.aaf.rproxy.utils;
+
+public class ReverseProxyUtils {
+
+ public static boolean validTransactionId(String transactionId) {
+ return transactionId.matches("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}");
+ }
+
+ private ReverseProxyUtils() {}
+}
diff --git a/sidecar/rproxy/src/main/resources/application.properties b/sidecar/rproxy/src/main/resources/application.properties
new file mode 100644
index 0000000..f291372
--- /dev/null
+++ b/sidecar/rproxy/src/main/resources/application.properties
@@ -0,0 +1,22 @@
+CONFIG_HOME=./config
+
+server.port=10692
+
+server.ssl.enabled=true
+server.ssl.protocol=TLS
+server.ssl.enabled-protocols=TLSv1.2
+server.ssl.key-store=${CONFIG_HOME}/auth/tomcat_keystore
+server.ssl.client-auth=need
+
+server.ssl.client-cert=${CONFIG_HOME}/auth/client-cert.p12
+
+server.servlet.contextPath=/
+
+uri.authorization.configuration-file=${CONFIG_HOME}/auth/uri-authorization.json
+
+logging.config=${CONFIG_HOME}/logback-spring.xml
+
+spring.profiles.default=secure,cadi
+
+# For Spring Boot Actuator endpoints
+management.endpoints.web.base-path=/rproxy \ No newline at end of file
diff --git a/sidecar/rproxy/src/test/java/org/onap/aaf/rproxy/PermissionMatchingTest.java b/sidecar/rproxy/src/test/java/org/onap/aaf/rproxy/PermissionMatchingTest.java
new file mode 100644
index 0000000..43e20fe
--- /dev/null
+++ b/sidecar/rproxy/src/test/java/org/onap/aaf/rproxy/PermissionMatchingTest.java
@@ -0,0 +1,262 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aaf
+ * ================================================================================
+ * Copyright © 2018 European Software Marketing Ltd.
+ * ================================================================================
+ * 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.aaf.rproxy;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.header;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import javax.annotation.Resource;
+import org.eclipse.jetty.util.security.Password;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.onap.aaf.rproxy.config.ForwardProxyProperties;
+import org.onap.aaf.rproxy.config.PrimaryServiceProperties;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.client.MockRestServiceServer;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.web.client.RestTemplate;
+
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
+@AutoConfigureMockMvc
+
+@TestPropertySource(locations = {"classpath:primary-service.properties", "classpath:forward-proxy.properties"})
+
+@ContextConfiguration(classes = ReverseProxyTestConfig.class)
+public class PermissionMatchingTest {
+
+ static {
+ System.setProperty("server.ssl.key-store-password",
+ Password.deobfuscate("OBF:1y0q1uvc1uum1uvg1pil1pjl1uuq1uvk1uuu1y10"));
+ }
+
+ @Value("${transactionid.header.name}")
+ private String transactionIdHeaderName;
+
+ @Resource(name = "PrimaryServiceProperties")
+ private PrimaryServiceProperties primaryServiceProps;
+
+ @Resource(name = "ForwardProxyProperties")
+ private ForwardProxyProperties forwardProxyProps;
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Autowired
+ private RestTemplate restTemplate;
+
+ private MockRestServiceServer mockServer;
+
+ private String primaryServiceBaseUrl;
+
+ @Before
+ public void setUp() throws Exception {
+ mockServer = MockRestServiceServer.createServer(restTemplate);
+ primaryServiceBaseUrl = primaryServiceProps.getProtocol() + "://" + primaryServiceProps.getHost() + ":"
+ + primaryServiceProps.getPort();
+ }
+
+ @Test
+ public void testURIMismatch() throws Exception {
+
+ String testUrl = "/uri/does/not/exist";
+ String testResponse = "Sorry, the request is not allowed";
+
+ mockMvc
+ .perform(get(testUrl))
+ .andExpect(status().isForbidden())
+ .andExpect(status().reason(testResponse));
+
+ }
+
+ @Test
+ public void testURINoPermission() throws Exception {
+
+ String testUrl = "/not/allowed/at/all";
+ String testResponse = "Sorry, the request is not allowed";
+
+ mockMvc
+ .perform(get(testUrl))
+ .andExpect(status().isForbidden())
+ .andExpect(status().reason(testResponse));
+
+ }
+
+ @Test
+ public void testURIMatchSinglePermissionMatch() throws Exception {
+
+ String transactionId = "63f88b50-6345-4a61-bc59-3a48cabb60a4";
+ String testUrl = "/single/permission/required";
+ String testResponse = "Response from MockRestService";
+
+ mockServer
+ .expect(requestTo(primaryServiceBaseUrl + testUrl))
+ .andExpect(method(HttpMethod.GET))
+ .andExpect(header(transactionIdHeaderName, transactionId))
+ .andRespond(withSuccess(testResponse, MediaType.APPLICATION_JSON));
+
+ // Send request to mock server with transaction Id
+ mockMvc
+ .perform(MockMvcRequestBuilders.get(testUrl).accept(MediaType.APPLICATION_JSON).header(transactionIdHeaderName, transactionId))
+ .andExpect(status().isOk())
+ .andExpect(content().string(equalTo(testResponse)));
+
+ mockServer.verify();
+
+ }
+
+ @Test
+ public void testURIMatchMultiplePermissionMatch() throws Exception {
+
+ String transactionId = "63f88b50-6345-4a61-bc59-3a48cabb60a4";
+ String testUrl = "/multiple/permissions/required";
+ String testResponse = "Response from MockRestService";
+
+ mockServer
+ .expect(requestTo(primaryServiceBaseUrl + testUrl))
+ .andExpect(method(HttpMethod.GET))
+ .andExpect(header(transactionIdHeaderName, transactionId))
+ .andRespond(withSuccess(testResponse, MediaType.APPLICATION_JSON));
+
+ // Send request to mock server with transaction Id
+ mockMvc
+ .perform(MockMvcRequestBuilders.get(testUrl).accept(MediaType.APPLICATION_JSON).header(transactionIdHeaderName, transactionId))
+ .andExpect(status().isOk())
+ .andExpect(content().string(equalTo(testResponse)));
+
+ mockServer.verify();
+
+ }
+
+ @Test
+ public void testURIMatchMultipleMissingOnePermissionMatch() throws Exception {
+
+ String testUrl = "/multiple/permissions/required/one/missing";
+ String testResponse = "Sorry, the request is not allowed";
+
+ mockMvc
+ .perform(get(testUrl))
+ .andExpect(status().isForbidden())
+ .andExpect(status().reason(testResponse));
+ }
+
+ @Test
+ public void testURIInstanceActionWildCardPermissionMatch() throws Exception {
+
+ String transactionId = "63f88b50-6345-4a61-bc59-3a48cabb60a4";
+ String testUrl = "/wildcard/permission/granted";
+ String testResponse = "Response from MockRestService";
+
+ mockServer
+ .expect(requestTo(primaryServiceBaseUrl + testUrl))
+ .andExpect(method(HttpMethod.GET))
+ .andExpect(header(transactionIdHeaderName, transactionId))
+ .andRespond(withSuccess(testResponse, MediaType.APPLICATION_JSON));
+
+ // Send request to mock server with transaction Id
+ mockMvc
+ .perform(MockMvcRequestBuilders
+ .get(testUrl)
+ .accept(MediaType.APPLICATION_JSON)
+ .header(transactionIdHeaderName, transactionId)
+ .header("PermissionsUser", "UserWithInstanceActionWildcardPermissionGranted")
+ )
+ .andExpect(status().isOk())
+ .andExpect(content().string(equalTo(testResponse)));
+
+ mockServer.verify();
+
+ }
+
+ @Test
+ public void testURIInstanceWildCardPermissionMatch() throws Exception {
+
+ String transactionId = "63f88b50-6345-4a61-bc59-3a48cabb60a4";
+ String testUrl = "/instance/wildcard/permission/granted";
+ String testResponse = "Response from MockRestService";
+
+ mockServer
+ .expect(requestTo(primaryServiceBaseUrl + testUrl))
+ .andExpect(method(HttpMethod.GET))
+ .andExpect(header(transactionIdHeaderName, transactionId))
+ .andRespond(withSuccess(testResponse, MediaType.APPLICATION_JSON));
+
+ // Send request to mock server with transaction Id
+ mockMvc
+ .perform(MockMvcRequestBuilders
+ .get(testUrl)
+ .accept(MediaType.APPLICATION_JSON)
+ .header(transactionIdHeaderName, transactionId)
+ .header("PermissionsUser", "UserWithInstanceWildcardPermissionGranted")
+ )
+ .andExpect(status().isOk())
+ .andExpect(content().string(equalTo(testResponse)));
+
+ mockServer.verify();
+
+ }
+
+ @Test
+ public void testURIActionWildCardPermissionMatch() throws Exception {
+
+ String transactionId = "63f88b50-6345-4a61-bc59-3a48cabb60a4";
+ String testUrl = "/action/wildcard/permission/granted";
+ String testResponse = "Response from MockRestService";
+
+ mockServer
+ .expect(requestTo(primaryServiceBaseUrl + testUrl))
+ .andExpect(method(HttpMethod.GET))
+ .andExpect(header(transactionIdHeaderName, transactionId))
+ .andRespond(withSuccess(testResponse, MediaType.APPLICATION_JSON));
+
+ // Send request to mock server with transaction Id
+ mockMvc
+ .perform(MockMvcRequestBuilders
+ .get(testUrl)
+ .accept(MediaType.APPLICATION_JSON)
+ .header(transactionIdHeaderName, transactionId)
+ .header("PermissionsUser", "UserWithActionWildcardPermissionGranted")
+ )
+ .andExpect(status().isOk())
+ .andExpect(content().string(equalTo(testResponse)));
+
+ mockServer.verify();
+
+ }
+
+}
diff --git a/sidecar/rproxy/src/test/java/org/onap/aaf/rproxy/ReverseProxyApplicationTest.java b/sidecar/rproxy/src/test/java/org/onap/aaf/rproxy/ReverseProxyApplicationTest.java
new file mode 100644
index 0000000..61a0923
--- /dev/null
+++ b/sidecar/rproxy/src/test/java/org/onap/aaf/rproxy/ReverseProxyApplicationTest.java
@@ -0,0 +1,158 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aaf
+ * ================================================================================
+ * Copyright © 2018 European Software Marketing Ltd.
+ * ================================================================================
+ * 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.aaf.rproxy;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.header;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import javax.annotation.Resource;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.util.security.Password;
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.onap.aaf.rproxy.config.ForwardProxyProperties;
+import org.onap.aaf.rproxy.config.PrimaryServiceProperties;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.client.MockRestServiceServer;
+import org.springframework.test.web.client.match.MockRestRequestMatchers;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.web.client.RestTemplate;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
+@AutoConfigureMockMvc
+@TestPropertySource(locations = {"classpath:primary-service.properties", "classpath:forward-proxy.properties"})
+@ContextConfiguration(classes = ReverseProxyTestConfig.class)
+public class ReverseProxyApplicationTest {
+
+ static {
+ System.setProperty("server.ssl.key-store-password",
+ Password.deobfuscate("OBF:1y0q1uvc1uum1uvg1pil1pjl1uuq1uvk1uuu1y10"));
+ }
+
+ @Value("${transactionid.header.name}")
+ private String transactionIdHeaderName;
+
+ @Resource(name = "PrimaryServiceProperties")
+ private PrimaryServiceProperties primaryServiceProps;
+
+ @Resource(name = "ForwardProxyProperties")
+ private ForwardProxyProperties forwardProxyProps;
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Autowired
+ private RestTemplate restTemplate;
+
+ private MockRestServiceServer mockServer;
+
+ private String primaryServiceBaseUrl;
+ private String forwardProxyBaseUrl;
+
+ @Before
+ public void setUp() throws Exception {
+ mockServer = MockRestServiceServer.createServer(restTemplate);
+ primaryServiceBaseUrl = primaryServiceProps.getProtocol() + "://" + primaryServiceProps.getHost() + ":"
+ + primaryServiceProps.getPort();
+ forwardProxyBaseUrl = forwardProxyProps.getProtocol() + "://" + forwardProxyProps.getHost() + ":"
+ + forwardProxyProps.getPort() + forwardProxyProps.getCacheurl() + "/";
+ }
+
+ @Test
+ public void checkForwardRequest() throws Exception {
+
+ String transactionId = "63f88b50-6345-4a61-bc59-3a48cabb60a4";
+ String testUrl = "/aai/v13/cloud-infrastructure/cloud-regions";
+ String testResponse = "Response from MockRestService";
+
+ mockServer.expect(requestTo(primaryServiceBaseUrl + testUrl)).andExpect(method(HttpMethod.GET))
+ .andExpect(header(transactionIdHeaderName, transactionId))
+ .andRespond(withSuccess(testResponse, MediaType.APPLICATION_JSON));
+
+ // Send request to mock server with transaction Id
+ mockMvc.perform(MockMvcRequestBuilders.get(testUrl).accept(MediaType.APPLICATION_JSON)
+ .header(transactionIdHeaderName, transactionId)).andExpect(status().isOk())
+ .andExpect(content().string(equalTo(testResponse)));
+
+ mockServer.verify();
+ }
+
+ @Test
+ public void checkTransactionIdIsSetIfEmptyInRequest() throws Exception {
+
+ String testUrl = "/aai/v13/cloud-infrastructure/cloud-regions";
+ String testResponse = "Response from MockRestService";
+
+ mockServer.expect(requestTo(primaryServiceBaseUrl + testUrl)).andExpect(method(HttpMethod.GET))
+ .andExpect(header(transactionIdHeaderName, Matchers.any(String.class)))
+ .andRespond(withSuccess(testResponse, MediaType.APPLICATION_JSON));
+
+ // Send request to mock server without transaction Id
+ mockMvc.perform(MockMvcRequestBuilders.get(testUrl).accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk()).andExpect(content().string(equalTo(testResponse)));
+
+ mockServer.verify();
+ }
+
+ @Test
+ public void checkBasicAuthCaching() throws Exception {
+
+ String transactionId = "63f88b50-6345-4a61-bc59-3a48cabb60a4";
+ String testUrl = "/aai/v13/cloud-infrastructure/cloud-regions";
+ String testResponse = "Response from MockRestService";
+ String testAuthValue = "testAuthValue";
+ String testCachePayload = "{ \"credentialName\":" + HttpHeader.AUTHORIZATION + ", \"credentialValue\":"
+ + testAuthValue + ", \"credentialType\":\"HEADER\" }";
+
+ mockServer.expect(requestTo(forwardProxyBaseUrl + transactionId)).andExpect(method(HttpMethod.POST))
+ .andExpect(MockRestRequestMatchers.content().json(testCachePayload))
+ .andRespond(withSuccess(testResponse, MediaType.APPLICATION_JSON));
+
+ mockServer.expect(requestTo(primaryServiceBaseUrl + testUrl)).andExpect(method(HttpMethod.GET))
+ .andExpect(header(transactionIdHeaderName, transactionId))
+ .andRespond(withSuccess(testResponse, MediaType.APPLICATION_JSON));
+
+ // Send request to mock server with transaction Id
+ mockMvc.perform(MockMvcRequestBuilders.get(testUrl).accept(MediaType.APPLICATION_JSON)
+ .header(transactionIdHeaderName, transactionId)
+ .header(HttpHeader.AUTHORIZATION.asString(), testAuthValue)).andExpect(status().isOk())
+ .andExpect(content().string(equalTo(testResponse)));
+
+ mockServer.verify();
+ }
+}
diff --git a/sidecar/rproxy/src/test/java/org/onap/aaf/rproxy/ReverseProxyIT.java b/sidecar/rproxy/src/test/java/org/onap/aaf/rproxy/ReverseProxyIT.java
new file mode 100644
index 0000000..615afd4
--- /dev/null
+++ b/sidecar/rproxy/src/test/java/org/onap/aaf/rproxy/ReverseProxyIT.java
@@ -0,0 +1,48 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aaf
+ * ================================================================================
+ * Copyright © 2018 European Software Marketing Ltd.
+ * ================================================================================
+ * 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.aaf.rproxy;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.eclipse.jetty.util.security.Password;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
+public class ReverseProxyIT {
+
+ static {
+ System.setProperty("server.ssl.key-store-password",
+ Password.deobfuscate("OBF:1y0q1uvc1uum1uvg1pil1pjl1uuq1uvk1uuu1y10"));
+ }
+
+ @Autowired
+ private ReverseProxyService rProxyService;
+
+ @Test
+ public void contexLoads() throws Exception {
+ assertThat(rProxyService).isNotNull();
+ }
+}
diff --git a/sidecar/rproxy/src/test/java/org/onap/aaf/rproxy/ReverseProxyTestConfig.java b/sidecar/rproxy/src/test/java/org/onap/aaf/rproxy/ReverseProxyTestConfig.java
new file mode 100644
index 0000000..c512e6a
--- /dev/null
+++ b/sidecar/rproxy/src/test/java/org/onap/aaf/rproxy/ReverseProxyTestConfig.java
@@ -0,0 +1,42 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aaf
+ * ================================================================================
+ * Copyright © 2018 European Software Marketing Ltd.
+ * ================================================================================
+ * 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.aaf.rproxy;
+
+import org.onap.aaf.rproxy.mocks.ReverseProxyMockCadiFilter;
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.boot.web.servlet.RegistrationBean;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+import org.springframework.context.annotation.Bean;
+
+@TestConfiguration
+public class ReverseProxyTestConfig extends SpringBootServletInitializer {
+
+ @Bean
+ public FilterRegistrationBean<ReverseProxyMockCadiFilter> registerCADIFilter() {
+ FilterRegistrationBean<ReverseProxyMockCadiFilter> filterRegistrationBean = new FilterRegistrationBean<>();
+ filterRegistrationBean.setFilter(new ReverseProxyMockCadiFilter());
+ filterRegistrationBean.addUrlPatterns("/*");
+ filterRegistrationBean.setName("CADIFilter");
+ filterRegistrationBean.setOrder(RegistrationBean.HIGHEST_PRECEDENCE);
+
+ return filterRegistrationBean;
+ }
+}
diff --git a/sidecar/rproxy/src/test/resources/forward-proxy.properties b/sidecar/rproxy/src/test/resources/forward-proxy.properties
new file mode 100644
index 0000000..119ccbd
--- /dev/null
+++ b/sidecar/rproxy/src/test/resources/forward-proxy.properties
@@ -0,0 +1,4 @@
+forward-proxy.protocol = https
+forward-proxy.host = localhost
+forward-proxy.port = 9501
+forward-proxy.cacheurl = /credential-cache \ No newline at end of file
diff --git a/sidecar/rproxy/src/test/resources/primary-service.properties b/sidecar/rproxy/src/test/resources/primary-service.properties
new file mode 100644
index 0000000..134e103
--- /dev/null
+++ b/sidecar/rproxy/src/test/resources/primary-service.properties
@@ -0,0 +1,3 @@
+primary-service.protocol = https
+primary-service.host = localhost
+primary-service.port = 9500