From: Dominik Mizyn Date: Wed, 4 Mar 2020 14:48:01 +0000 (+0100) Subject: MicroserviceProxyController up X-Git-Tag: 3.2.0~16^2 X-Git-Url: https://gerrit.onap.org/r/gitweb?p=portal.git;a=commitdiff_plain;h=6aa4d5e4b2792d7cd5037ec8f5a87bfbaa0b1ade MicroserviceProxyController up MicroserviceProxyController up and all needed services Issue-ID: PORTAL-710 Change-Id: I524a3083c22c7f52e4d8797c3e31c2de102be554 Signed-off-by: Dominik Mizyn --- diff --git a/portal-BE/src/main/java/org/onap/portal/controller/MicroserviceProxyController.java b/portal-BE/src/main/java/org/onap/portal/controller/MicroserviceProxyController.java new file mode 100644 index 00000000..3a4fd96d --- /dev/null +++ b/portal-BE/src/main/java/org/onap/portal/controller/MicroserviceProxyController.java @@ -0,0 +1,116 @@ +/*- + * ============LICENSE_START========================================== + * ONAP Portal + * =================================================================== + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * + * Unless otherwise specified, all software contained herein is licensed + * under the Apache License, Version 2.0 (the "License"); + * you may not use this software 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. + * + * Unless otherwise specified, all documentation contained herein is licensed + * under the Creative Commons License, Attribution 4.0 Intl. (the "License"); + * you may not use this documentation except in compliance with the License. + * You may obtain a copy of the License at + * + * https://creativecommons.org/licenses/by/4.0/ + * + * Unless required by applicable law or agreed to in writing, documentation + * 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.portal.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.security.Principal; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.onap.portal.domain.db.fn.FnUser; +import org.onap.portal.service.MicroserviceProxyService; +import org.onap.portal.service.user.FnUserService; +import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.HttpClientErrorException; + +@RestController +@Configuration +@EnableAspectJAutoProxy +public class MicroserviceProxyController { + + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MicroserviceProxyController.class); + + private final MicroserviceProxyService microserviceProxyService; + private final FnUserService fnUserService; + + @Autowired + public MicroserviceProxyController(MicroserviceProxyService microserviceProxyService, + FnUserService fnUserService) { + this.microserviceProxyService = microserviceProxyService; + this.fnUserService = fnUserService; + } + + @RequestMapping(value = {"/portalApi/microservice/proxy/{serviceId}"}, method = { + RequestMethod.GET}, produces = "application/json") + public String getMicroserviceProxy(Principal principal, HttpServletRequest request, HttpServletResponse response, + @PathVariable("serviceId") long serviceId) throws Exception { + FnUser user = fnUserService.loadUserByUsername(principal.getName()); + String answer = ""; + try { + answer = microserviceProxyService.proxyToDestination(serviceId, user, request); + } catch (HttpClientErrorException e) { + answer = e.getResponseBodyAsString(); + } + return isValidJSON(answer) ? answer + : "{\"error\":\"" + answer.replace(System.getProperty("line.separator"), "") + "\"}"; + } + + @RequestMapping(value = {"/portalApi/microservice/proxy/parameter/{widgetId}"}, method = { + RequestMethod.GET}, produces = "application/json") + public String getMicroserviceProxyByWidgetId(Principal principal, HttpServletRequest request, + HttpServletResponse response, + @PathVariable("widgetId") long widgetId) throws Exception { + FnUser user = fnUserService.loadUserByUsername(principal.getName()); + String answer = ""; + try { + answer = microserviceProxyService.proxyToDestinationByWidgetId(widgetId, user, request); + } catch (HttpClientErrorException e) { + answer = e.getResponseBodyAsString(); + } + return isValidJSON(answer) ? answer + : "{\"error\":\"" + answer.replace(System.getProperty("line.separator"), "") + "\"}"; + } + + private boolean isValidJSON(String response) { + try { + final ObjectMapper mapper = new ObjectMapper(); + mapper.readTree(response); + return true; + } catch (IOException e) { + logger.debug(EELFLoggerDelegate.debugLogger, "isValidJSON failed", e); + return false; + } + } +} diff --git a/portal-BE/src/main/java/org/onap/portal/restTemplates/PortalWMSTemplate.java b/portal-BE/src/main/java/org/onap/portal/restTemplates/PortalWMSTemplate.java new file mode 100644 index 00000000..74707445 --- /dev/null +++ b/portal-BE/src/main/java/org/onap/portal/restTemplates/PortalWMSTemplate.java @@ -0,0 +1,36 @@ +package org.onap.portal.restTemplates; + +import org.onap.portal.domain.dto.ecomp.WidgetServiceHeaders; +import org.onap.portal.service.WidgetMService; +import org.onap.portal.utils.EcompPortalUtils; +import org.onap.portalsdk.core.util.SystemProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +@Component +public class PortalWMSTemplate { + + private final RestTemplate template = new RestTemplate(); + + private final WidgetMService widgetMService; + + @Autowired + public PortalWMSTemplate(WidgetMService widgetMService) { + this.widgetMService = widgetMService; + } + + + @SuppressWarnings("rawtypes") + public ResponseEntity proxyToDestinationByWidgetId(long widgetId) throws Exception { + return template.exchange( + EcompPortalUtils.widgetMsProtocol() + "://" + + widgetMService.getServiceLocation("widgets-service", + SystemProperties.getProperty("microservices.widget.local.port")) + + "/widget/microservices/widgetCatalog/parameters/" + widgetId, + HttpMethod.GET, new HttpEntity(WidgetServiceHeaders.getInstance()), Long.class); + } +} diff --git a/portal-BE/src/main/java/org/onap/portal/service/MicroserviceProxyService.java b/portal-BE/src/main/java/org/onap/portal/service/MicroserviceProxyService.java new file mode 100644 index 00000000..9273b284 --- /dev/null +++ b/portal-BE/src/main/java/org/onap/portal/service/MicroserviceProxyService.java @@ -0,0 +1,210 @@ +package org.onap.portal.service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import javax.servlet.http.HttpServletRequest; +import org.apache.commons.codec.binary.Base64; +import org.onap.portal.domain.db.ep.EpMicroservice; +import org.onap.portal.domain.db.ep.EpMicroserviceParameter; +import org.onap.portal.domain.db.ep.EpWidgetCatalogParameter; +import org.onap.portal.domain.db.fn.FnUser; +import org.onap.portal.restTemplates.PortalWMSTemplate; +import org.onap.portal.service.microservice.EpMicroserviceService; +import org.onap.portal.service.widgetCatalogParameter.EpWidgetCatalogParameterService; +import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.onap.portalsdk.core.onboarding.util.CipherUtil; +import org.onap.portalsdk.core.util.SystemProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; + +@Service +public class MicroserviceProxyService { + + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MicroserviceProxyService.class); + + private static final String BASIC_AUTH = "Basic Authentication"; + private static final String NO_AUTH = "No Authentication"; + private static final String COOKIE_AUTH = "Cookie based Authentication"; + private static final String QUESTION_MARK = "?"; + private static final String ADD_MARK = "&"; + + private RestTemplate template = new RestTemplate(); + + private final EpMicroserviceService microserviceService; + private final EpWidgetCatalogParameterService widgetParameterService; + + private final PortalWMSTemplate portalWMSTemplate; + + @Autowired + public MicroserviceProxyService(EpMicroserviceService microserviceService, + EpWidgetCatalogParameterService widgetParameterService, WidgetMService widgetMService, + PortalWMSTemplate portalWMSTemplate) { + this.microserviceService = microserviceService; + this.widgetParameterService = widgetParameterService; + this.portalWMSTemplate = portalWMSTemplate; + } + + public String proxyToDestination(long serviceId, FnUser user, HttpServletRequest request) throws Exception { + // get the microservice object by the id + Optional data = microserviceService.getById(serviceId); + // No such microservice available + // can we return a better response than null? + return data + .map(epMicroservice -> authenticateAndRespond(epMicroservice, request, composeParams(epMicroservice, user))) + .orElse(null); + } + + public String proxyToDestinationByWidgetId(long widgetId, FnUser user, HttpServletRequest request) + throws Exception { + ResponseEntity ans = portalWMSTemplate.proxyToDestinationByWidgetId(widgetId); + Long serviceId = ans.getBody(); + // get the microservice object by the id + Optional data = microserviceService.getById(serviceId); + // No such microservice available + if (!data.isPresent()) { + return null; + } + List params = composeParams(data.get(), user); + for (EpMicroserviceParameter p : params) { + EpWidgetCatalogParameter userValue = widgetParameterService.getUserParamById(widgetId, user.getId(), + p.getId()); + if (userValue != null) { + p.setParaValue(userValue.getUserValue()); + } + } + return authenticateAndRespond(data.get(), request, params); + } + + private String authenticateAndRespond(EpMicroservice data, HttpServletRequest request, + List params) throws HttpClientErrorException, IllegalArgumentException { + String response = null; + switch (data.getSecurityType()) { + case NO_AUTH: { + HttpEntity entity = new HttpEntity<>(headersForNoAuth()); + String url = microserviceUrlConverter(data, params); + logger.debug(EELFLoggerDelegate.debugLogger, + "authenticateAndRespond: Before making no authentication call: {}", url); + response = template.exchange(url, HttpMethod.GET, entity, String.class).getBody(); + logger.debug(EELFLoggerDelegate.debugLogger, + "authenticateAndRespond: No authentication call response: {}", + response); + break; + } + case BASIC_AUTH: { + // encoding the username and password + String plainCreds; + try { + plainCreds = data.getUsername() + ":" + decryptedPassword(data.getPassword()); + } catch (Exception e) { + logger.error("authenticateAndRespond failed to decrypt password", e); + throw new IllegalArgumentException("Failed to decrypt password", e); + } + byte[] plainCredsBytes = plainCreds.getBytes(); + byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes); + String base64Creds = new String(base64CredsBytes); + + HttpEntity entity = new HttpEntity<>(headersForBasicAuth(request, base64Creds)); + + String url = microserviceUrlConverter(data, params); + try { + response = template.exchange(url, HttpMethod.GET, entity, String.class).getBody(); + } catch (HttpClientErrorException e) { + logger.error("authenticateAndRespond failed for basic security url " + url, e); + throw e; + } + break; + } + case COOKIE_AUTH: { + HttpEntity entity = new HttpEntity<>(headersForCookieAuth(request)); + String url = microserviceUrlConverter(data, params); + try { + response = template.exchange(url, HttpMethod.GET, entity, String.class).getBody(); + } catch (HttpClientErrorException e) { + logger.error("authenticateAndRespond failed for cookie auth url " + url, e); + throw e; + } + break; + } + } + + return response; + } + + private String decryptedPassword(String encryptedPwd) throws Exception { + String result = ""; + if (encryptedPwd != null && encryptedPwd.length() > 0) { + try { + result = CipherUtil.decryptPKC(encryptedPwd, + SystemProperties.getProperty(SystemProperties.Decryption_Key)); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "decryptedPassword failed", e); + throw e; + } + } + + return result; + } + + private String microserviceUrlConverter(EpMicroservice data, List params) { + String url = data.getEndpointUrl(); + for (int i = 0; i < params.size(); i++) { + if (i == 0) { + url += QUESTION_MARK; + } + url += params.get(i).getParaKey() + "=" + params.get(i).getParaValue(); + if (i != (params.size() - 1)) { + url += ADD_MARK; + } + } + + return url; + } + + private HttpHeaders headersForNoAuth() { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + return headers; + } + + // TODO: why is this generically named cookie used? + private final static String Cookie = "Cookie"; + + private HttpHeaders headersForBasicAuth(HttpServletRequest request, String base64Creds) { + HttpHeaders headers = new HttpHeaders(); + headers.add("Authorization", "Basic " + base64Creds); + headers.setContentType(MediaType.APPLICATION_JSON); + String rawCookie = request.getHeader(Cookie); + if (rawCookie != null) { + headers.add(Cookie, rawCookie); + } + return headers; + } + + private HttpHeaders headersForCookieAuth(HttpServletRequest request) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + String rawCookie = request.getHeader(Cookie); + if (rawCookie != null) { + headers.add(Cookie, rawCookie); + } + return headers; + } + + private List composeParams(EpMicroservice data, FnUser user) { + List params = new ArrayList<>(data.getEpMicroserviceParameters()); + EpMicroserviceParameter userIdParam = new EpMicroserviceParameter(); + userIdParam.setParaKey("userId"); + userIdParam.setParaValue(user.getOrgUserId()); + params.add(userIdParam); + return params; + } +} diff --git a/portal-BE/src/main/java/org/onap/portal/service/microservice/EpMicroserviceService.java b/portal-BE/src/main/java/org/onap/portal/service/microservice/EpMicroserviceService.java index 1b999f85..e93ca857 100644 --- a/portal-BE/src/main/java/org/onap/portal/service/microservice/EpMicroserviceService.java +++ b/portal-BE/src/main/java/org/onap/portal/service/microservice/EpMicroserviceService.java @@ -1,5 +1,6 @@ package org.onap.portal.service.microservice; +import java.util.Optional; import org.onap.portal.domain.db.ep.EpMicroservice; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -19,4 +20,8 @@ public class EpMicroserviceService { public List saveAll(List epMicroservices) { return epMicroserviceDao.saveAll(epMicroservices); } + + public Optional getById(long serviceId) { + return epMicroserviceDao.findById(serviceId); + } }