From 4d750d21cffd991b4bc5b735455fc9fceb516937 Mon Sep 17 00:00:00 2001 From: wr148d Date: Thu, 11 Feb 2021 09:14:58 -0500 Subject: [PATCH] Update the Sparky backend to support proxying calls to resources and traversal Issue-ID: AAI-3250 Change-Id: I1c4d587ca30ea379ae28697b95ba2ca4602cd4e8 Signed-off-by: wr148d --- .../config/application.properties | 6 +- .../config/spring-beans/sparky-aai-proxy.xml | 15 ++ sparkybe-onap-application/pom.xml | 2 +- .../main/java/org/onap/aai/sparky/ProxyHelper.java | 194 ++++++++++++++ .../sparky/exception/ProxyServiceException.java | 36 +++ .../java/org/onap/aai/sparky/util/ProxyClient.java | 293 +++++++++++++++++++++ 6 files changed, 542 insertions(+), 4 deletions(-) create mode 100644 sparkybe-onap-application/config/spring-beans/sparky-aai-proxy.xml create mode 100644 sparkybe-onap-application/src/main/java/org/onap/aai/sparky/ProxyHelper.java create mode 100644 sparkybe-onap-service/src/main/java/org/onap/aai/sparky/exception/ProxyServiceException.java create mode 100644 sparkybe-onap-service/src/main/java/org/onap/aai/sparky/util/ProxyClient.java diff --git a/sparkybe-onap-application/config/application.properties b/sparkybe-onap-application/config/application.properties index c0616b5..cdebe11 100644 --- a/sparkybe-onap-application/config/application.properties +++ b/sparkybe-onap-application/config/application.properties @@ -3,18 +3,18 @@ # spring.mvc.favicon.enabled=false -#possible values: camel,http,ssl,portal,fe-dev,fe-prod,oxm-default,oxm-override,[resources|gizmo],sync,oxm-schema-dev,oxm-schema-prod +#possible values: camel,http,ssl,portal,fe-dev,fe-prod,oxm-default,oxm-override,[resources|gizmo],sync,oxm-schema-dev,oxm-schema-prod,aai-proxy #For oxm loading there needs to be a combo of [oxm-default OR oxm-override] AND [oxm-schema-dev OR oxm-schema-prod] # # Gizmo profile # -spring.profiles.active=camel,ssl,fe-dev,oxm-schema-dev,gizmo,oxm-default +spring.profiles.active=camel,http,oxm-default,oxm-schema-dev,fe-dev,resources,aai-proxy # # Resources profile # -#spring.profiles.active=camel,http,fe-dev,oxm-schema-dev,resources,sync,oxm-override +#spring.profiles.active=camel,http,fe-dev,oxm-schema-dev,resources,sync,oxm-override,aai-proxy portal.cadiFileLocation=${CONFIG_HOME}/portal/cadi.properties searchservice.hostname=127.0.0.1 diff --git a/sparkybe-onap-application/config/spring-beans/sparky-aai-proxy.xml b/sparkybe-onap-application/config/spring-beans/sparky-aai-proxy.xml new file mode 100644 index 0000000..bb0b98d --- /dev/null +++ b/sparkybe-onap-application/config/spring-beans/sparky-aai-proxy.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/sparkybe-onap-application/pom.xml b/sparkybe-onap-application/pom.xml index 445464a..5b1571b 100644 --- a/sparkybe-onap-application/pom.xml +++ b/sparkybe-onap-application/pom.xml @@ -253,7 +253,7 @@ org.onap.aai rest-client - 1.5.0 + 1.3.0 diff --git a/sparkybe-onap-application/src/main/java/org/onap/aai/sparky/ProxyHelper.java b/sparkybe-onap-application/src/main/java/org/onap/aai/sparky/ProxyHelper.java new file mode 100644 index 0000000..ed9557f --- /dev/null +++ b/sparkybe-onap-application/src/main/java/org/onap/aai/sparky/ProxyHelper.java @@ -0,0 +1,194 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 Amdocs + * ================================================================================ + * 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.aai.sparky; + +import org.onap.aai.cl.api.Logger; +import org.onap.aai.cl.eelf.LoggerFactory; +import org.onap.aai.restclient.client.OperationResult; +import org.onap.aai.sparky.security.portal.PortalRestAPICentralServiceImpl; +import org.onap.aai.sparky.util.ProxyClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; +import org.springframework.core.env.Environment; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.MultivaluedMap; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import static org.onap.aai.sparky.logging.AaiUiMsgs.INFO_GENERIC; + +@Profile("aai-proxy") +@RestController +public class ProxyHelper { + @Autowired + Environment env; + private static final Logger LOG = LoggerFactory.getInstance().getLogger(ProxyHelper.class); + public static final String SCHEMA_VERSION = "schema-version"; + private ProxyClient proxyClient; + + /** + * Proxy Helper Class + * + * @param pc the proxyclient + */ + public ProxyHelper(ProxyClient pc){ + proxyClient = pc; + } + + public Boolean isPortalEnabled(){ + List list = Arrays.asList(this.env.getActiveProfiles()); + return list.contains("portal"); + } + + /** + * Proxy Post Calls from Sparky Frontend + * + * @param request + * @return the response + */ + + @RequestMapping(value = "/proxy/**", method = {RequestMethod.POST}) + public String postProxy(HttpServletRequest request, HttpServletResponse response){ + OperationResult or = null; + String results = ""; + try { + or = proxyClient.post(request); + updateHeaders(response, or); + results = or.getResult(); + }catch(Exception e) { + results = e.getMessage(); + } + return results; + } + + /** + * Proxy Put Calls from Sparky Frontend + * + * @param request + * @return the response + * @throws Exception + */ + + @RequestMapping(value = "/proxy/**", method = {RequestMethod.PUT}) + public String putProxy(HttpServletRequest request, HttpServletResponse response){ + OperationResult or = null; + String results = ""; + try { + or = proxyClient.put(request); + updateHeaders(response, or); + results = or.getResult(); + }catch(Exception e) { + results = e.getMessage(); + } + return results; + } + + /** + * Proxy Get Calls from Sparky Frontend + * + * @param request + * @return the response + */ + + @RequestMapping(value = "/proxy/**", method = {RequestMethod.GET}) + public String getProxy(HttpServletRequest request, HttpServletResponse response){ + OperationResult or = null; + String results = ""; + try { + or = proxyClient.get(request); + updateHeaders(response, or); + results = or.getResult(); + }catch(Exception e) { + results = e.getMessage(); + } + return results; + } + + /** + * Bulk Single Transactions from Sparky Frontend + * + * @param request + * @return the response + */ + + @RequestMapping(value = "/aai/v*/bulk/single-transaction", method = {RequestMethod.POST}) + public String bulkSingleTransaction(HttpServletRequest request, HttpServletResponse response){ + String uid = "testuid"; + if(this.isPortalEnabled()) { + PortalRestAPICentralServiceImpl pr = new PortalRestAPICentralServiceImpl(); + LOG.info(INFO_GENERIC, "Getting UID from portal api"); + try { + uid = pr.getUserId(request); + }catch(Exception e){ + LOG.info(INFO_GENERIC, "error getting user id: " + e); + } + LOG.info(INFO_GENERIC, "getUserID: uid: " + uid); + } + OperationResult or = null; + String results = ""; + try { + or = proxyClient.bulkSingleTransaction(request, uid); + //updateHeaders(response, or); + results = or.getResult(); + if(results == null){ + results = or.getFailureCause(); + } + }catch(Exception e) { + results = e.getMessage(); + } + return results; + } + + + /** + * Update the Headers + * + * @param or the operation result object + * @return the response + */ + public void updateHeaders(HttpServletResponse response, OperationResult or){ + response.setHeader("Access-Control-Allow-Origin", "*"); + MultivaluedMap headers = or.getHeaders(); + response.setStatus(or.getResultCode()); + Iterator it; + String headerTags = ""; + if(headers != null) { + it = headers.keySet().iterator(); + while (it.hasNext()) { + String theKey = (String) it.next(); + headerTags = (headerTags.equals("")) ? theKey : headerTags+","+theKey; + response.setHeader(theKey, headers.getFirst(theKey)); + } + } + response.setHeader("Access-Control-Expose-Headers", headerTags); + /*if(or.getResultCode() != 200) { + throw new GenericServiceException(String.valueOf(or.getFailureCause()+"resultCode:"+or.getResultCode())); + }*/ + } + +} \ No newline at end of file diff --git a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/exception/ProxyServiceException.java b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/exception/ProxyServiceException.java new file mode 100644 index 0000000..3c6a59b --- /dev/null +++ b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/exception/ProxyServiceException.java @@ -0,0 +1,36 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2021 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.aai.sparky.exception; + +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.http.HttpStatus; + +@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) +public class ProxyServiceException extends RuntimeException { + + + private static final long serialVersionUID = 1L; + + public ProxyServiceException(String exception) { + super(exception); + } + +} diff --git a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/util/ProxyClient.java b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/util/ProxyClient.java new file mode 100644 index 0000000..4b057d8 --- /dev/null +++ b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/util/ProxyClient.java @@ -0,0 +1,293 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 Amdocs + * ================================================================================ + * 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.aai.sparky.util; + +import org.onap.aai.cl.mdc.MdcContext; +import org.onap.aai.restclient.client.OperationResult; +import org.onap.aai.restclient.client.RestClient; +import org.onap.aai.restclient.enums.RestAuthenticationMode; +import org.onap.aai.sparky.dal.rest.RestClientConstructionException; +import org.onap.aai.sparky.dal.rest.RestClientFactory; +import org.onap.aai.sparky.dal.rest.config.RestEndpointConfig; +import org.onap.aai.sparky.util.Encryptor; +import org.onap.aai.sparky.viewandinspect.config.SparkyConstants; +import org.onap.aai.sparky.exception.ProxyServiceException; +import org.slf4j.MDC; + +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.servlet.http.HttpServletRequest; +import java.io.BufferedReader; +import java.io.IOException; +import java.util.*; + +public class ProxyClient { + private RestEndpointConfig endpointConfig; + private RestClient restClient; + private RestClient historyRestClient; + private RestClient apertureRestClient; + private static final String HEADER_AUTHORIZATION = "Authorization"; + Map> headers; + + /** + * Proxy Client Service + * + * @param endpointConfig the configuration for the endpoints to call + */ + public ProxyClient(RestEndpointConfig endpointConfig) + throws IOException, RestClientConstructionException { + + this.endpointConfig = endpointConfig; + + if (endpointConfig.getRestAuthenticationMode() == RestAuthenticationMode.SSL_BASIC) { + String basicAuthPassword = endpointConfig.getBasicAuthPassword(); + if (basicAuthPassword != null && basicAuthPassword.startsWith("OBF:")) { + org.onap.aai.sparky.util.Encryptor enc = new Encryptor(); + endpointConfig.setBasicAuthPassword(enc.decryptValue(basicAuthPassword)); + } + } + this.restClient = RestClientFactory.buildClient(endpointConfig); + setHeaders(); + } + + public RestEndpointConfig getEndpointConfig() { + return endpointConfig; + } + + protected String getBasicAuthenticationCredentials() { + + String usernameAndPassword = String.join(":", endpointConfig.getBasicAuthUserName(), + endpointConfig.getBasicAuthPassword()); + return "Basic " + java.util.Base64.getEncoder().encodeToString(usernameAndPassword.getBytes()); + } + + /** + * Method to set common headers + * + */ + public void setHeaders() { + this.headers = new HashMap<>(); + headers.put("X-FromAppId", Arrays.asList("AAI-UI")); + headers.put("X-TransactionId", Arrays.asList(MDC.get(MdcContext.MDC_REQUEST_ID))); + //headers.put("X-FromAppId", Arrays.asList(MDC.get(MdcContext.MDC_PARTNER_NAME))); + if (endpointConfig.getRestAuthenticationMode() == RestAuthenticationMode.SSL_BASIC) { + headers.putIfAbsent(HEADER_AUTHORIZATION, new ArrayList()); + headers.get(HEADER_AUTHORIZATION).add(getBasicAuthenticationCredentials()); + } + } + + /** + * Method to set headers from request + * + */ + public void populateHeadersFromRequest(HttpServletRequest request) { + List includeList = Arrays.asList("X-DslApiVersion"); + Enumeration names = request.getHeaderNames(); + while (names.hasMoreElements()) { + String name = names.nextElement(); + String value = request.getHeader(name); + if(includeList.contains(name)) { + this.headers.put(name, Arrays.asList(value)); + } + } + this.headers.put("X-DslApiVersion",Arrays.asList("V2")); + } + + /** + * Method to get rest client + * + */ + public RestClient getRestClient(HttpServletRequest request) { + if(isHistory(request.getRequestURL().substring(request.getRequestURL().lastIndexOf("/proxy") + 6))){ + return this.historyRestClient; + }else if(isAperture(request.getRequestURL().substring(request.getRequestURL().lastIndexOf("/proxy") + 6))) { + return this.apertureRestClient; + }else{ + return this.restClient; + } + } + + /** + * Get server string from config + * + * @return the server url as a string + */ + public String getServer(RestEndpointConfig epc) { + switch (epc.getRestAuthenticationMode()) { + case SSL_BASIC: + case SSL_CERT: { + return String.format("https://%s:%s", epc.getEndpointIpAddress(), + epc.getEndpointServerPort()); + } + default: { + return String.format("http://%s:%s", epc.getEndpointIpAddress(), + epc.getEndpointServerPort()); + } + } + + } + + /** + * Get the request body from a request for use on the proxy call + * + * @param request + * @return the request's body in string form + */ + private String getRequestBody(final HttpServletRequest request) { + final StringBuilder builder = new StringBuilder(); + try (BufferedReader reader = request.getReader()) { + if (reader == null) { + return null; + } + String line; + while ((line = reader.readLine()) != null) { + builder.append(line); + } + return builder.toString(); + } catch (final Exception e) { + return null; + } + } + + /** + * Get whether the incoming call was a history call or not + * + * @param uri + * @return true or false if it is a history traversal call + */ + private Boolean isHistory(String uri){ + String[] tokens = uri.split("/"); + return tokens.length >= 3 && tokens[2].equals("history-traversal"); + } + + /** + * Get whether the incoming call was a aperture call or not + * + * @param uri + * @return true or false if it is a aperture traversal call + */ + private Boolean isAperture(String uri){ + String[] tokens = uri.split("/"); + return tokens[2].equals("aperture"); + } + + + /** + * Build Url, builds the full url to call + * + * @param request + * @return the string of the endpoint to call + */ + public String buildUrl(HttpServletRequest request) { + RestEndpointConfig epc = endpointConfig; + String uri = + request.getRequestURL().substring(request.getRequestURL().lastIndexOf("/proxy") + 6); + String server = getServer(epc); + if (request.getQueryString() != null) { + uri = String.format("%s?%s", uri, request.getQueryString()); + } + return String.format("%s%s", server, uri); + } + + /** + * Method to make the get call to proxy + * + * @param request + * @return the operation result object from the call + */ + public OperationResult get(HttpServletRequest request) { + OperationResult resp = null; + String url = buildUrl(request); + this.populateHeadersFromRequest(request); + try { + resp = (getRestClient(request)).get(url, this.headers, MediaType.APPLICATION_JSON_TYPE); + } catch (Exception e) { + throw new ProxyServiceException("Exception while processing GET request- " + e); + } + return resp; + } + + /** + * Method to make the post call to proxy + * + * @param request + * @return the operation result object from the call + */ + public OperationResult post(HttpServletRequest request) { + OperationResult resp = null; + String url = buildUrl(request); + this.populateHeadersFromRequest(request); + try { + resp = (getRestClient(request)).post(url, getRequestBody(request), this.headers, + MediaType.APPLICATION_JSON_TYPE, MediaType.APPLICATION_JSON_TYPE); + } catch (Exception e) { + throw new ProxyServiceException("Exception while processing POST request- " + e); + } + return resp; + } + + /** + * Method to make the patch call to proxy + * + * @param request + * @return the operation result object from the call + */ + public OperationResult bulkSingleTransaction(HttpServletRequest request, String attuid) { + OperationResult resp = null; + Map> updatedHeaders = this.headers; + String url; + RestEndpointConfig epc = endpointConfig; + String uri = request.getRequestURL().substring(request.getRequestURL().lastIndexOf("/aai")); + String server = getServer(epc); + if (request.getQueryString() != null) { + uri = String.format("%s?%s", uri, request.getQueryString()); + } + url = String.format("%s%s", server, uri); + this.populateHeadersFromRequest(request); + updatedHeaders.put("X-FromAppId", Arrays.asList("AAI-UI-" + attuid)); + try { + resp = (getRestClient(request)).post(url, getRequestBody(request), updatedHeaders, + MediaType.APPLICATION_JSON_TYPE, MediaType.APPLICATION_JSON_TYPE); + } catch (Exception e) { + throw new ProxyServiceException("Exception while processing PATCH request- " + e); + } + return resp; + } + + /** + * Method to make the put call to proxy + * + * @param request + * @return the operation result object from the call + */ + public OperationResult put(HttpServletRequest request) { + OperationResult resp = null; + String url = buildUrl(request); + this.populateHeadersFromRequest(request); + try { + resp = (getRestClient(request)).put(url, getRequestBody(request), this.headers, + MediaType.APPLICATION_JSON_TYPE, MediaType.APPLICATION_JSON_TYPE); + } catch (Exception e) { + throw new ProxyServiceException("Exception while processing PUT request- " + e); + } + return resp; + } +} -- 2.16.6