diff options
Diffstat (limited to 'ecomp-portal-BE-common/src/main/java/org/onap/portalapp/portal/interceptor/PortalResourceInterceptor.java')
-rw-r--r-- | ecomp-portal-BE-common/src/main/java/org/onap/portalapp/portal/interceptor/PortalResourceInterceptor.java | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/ecomp-portal-BE-common/src/main/java/org/onap/portalapp/portal/interceptor/PortalResourceInterceptor.java b/ecomp-portal-BE-common/src/main/java/org/onap/portalapp/portal/interceptor/PortalResourceInterceptor.java new file mode 100644 index 00000000..5d2f399b --- /dev/null +++ b/ecomp-portal-BE-common/src/main/java/org/onap/portalapp/portal/interceptor/PortalResourceInterceptor.java @@ -0,0 +1,369 @@ +/*- + * ============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============================================ + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.portalapp.portal.interceptor; + +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.codec.binary.Hex; +import org.onap.portalapp.controller.sessionmgt.SessionCommunicationController; +import org.onap.portalapp.portal.controller.BasicAuthenticationController; +import org.onap.portalapp.portal.controller.ExternalAppsRestfulController; +import org.onap.portalapp.portal.controller.SharedContextRestController; +import org.onap.portalapp.portal.controller.WebAnalyticsExtAppController; +import org.onap.portalapp.portal.domain.BasicAuthCredentials; +import org.onap.portalapp.portal.domain.EPEndpoint; +import org.onap.portalapp.portal.domain.EPRole; +import org.onap.portalapp.portal.domain.EPUser; +import org.onap.portalapp.portal.logging.aop.EPEELFLoggerAdvice; +import org.onap.portalapp.portal.logging.format.EPAppMessagesEnum; +import org.onap.portalapp.portal.logging.logic.EPLogUtil; +import org.onap.portalapp.portal.service.BasicAuthenticationCredentialService; +import org.onap.portalapp.portal.utils.EcompPortalUtils; +import org.onap.portalapp.service.RemoteWebServiceCallService; +import org.onap.portalapp.service.sessionmgt.ManageService; +import org.onap.portalapp.util.EPUserUtils; +import org.onap.portalsdk.core.controller.FusionBaseController; +import org.onap.portalsdk.core.exception.UrlAccessRestrictedException; +import org.onap.portalsdk.core.interceptor.ResourceInterceptor; +import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.onap.portalsdk.core.onboarding.listener.PortalTimeoutHandler; +import org.onap.portalsdk.core.onboarding.util.CipherUtil; +import org.onap.portalsdk.core.util.SystemProperties; +import org.onap.portalsdk.core.util.SystemProperties.SecurityEventTypeEnum; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.method.HandlerMethod; + +public class PortalResourceInterceptor extends ResourceInterceptor { + private static final String APP_KEY = "uebkey"; + + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(PortalResourceInterceptor.class); + + @Autowired + private RemoteWebServiceCallService remoteWebServiceCallService; + + @Autowired + private ManageService manageService; + + @Autowired + private EPEELFLoggerAdvice epAdvice; + + @Autowired + private BasicAuthenticationCredentialService basicAuthService; + + @SuppressWarnings("unchecked") + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) + throws Exception { + + if (handler instanceof HandlerMethod) { + HandlerMethod method = (HandlerMethod) handler; + + /** + * These classes provide REST endpoints used by other application + * servers, NOT by an end user's browser. + */ + if (method.getBean() instanceof FusionBaseController) { + FusionBaseController controller = (FusionBaseController) method.getBean(); + if (!controller.isAccessible()) { + + // authorize portalApi requests by user role + String requestURI = request.getRequestURI(); + if (requestURI != null) { + String[] uriArray = requestURI.split("/portalApi/"); + if (uriArray.length > 1) { + String portalApiPath = uriArray[1]; + + Set<? extends String> roleFunctions = (Set<? extends String>) request.getSession() + .getAttribute(SystemProperties + .getProperty(SystemProperties.ROLE_FUNCTIONS_ATTRIBUTE_NAME)); + Set<? extends String> allRoleFunctions = (Set<? extends String>) request.getSession() + .getAttribute(EPUserUtils.ALL_ROLE_FUNCTIONS); + // Defend against code error to avoid throwing NPE + if (roleFunctions == null || allRoleFunctions == null) { + logger.error(EELFLoggerDelegate.errorLogger, + "preHandle: failed to get role functions attribute(s) from session!!"); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeInitializationError); + return false; + } + // check to see if roleFunctions of the user is in + // the + // list of all role functions + // if not, ignore to prevent restricting every + // trivial + // call; otherwise, if it is, then check for the + // access + if (matchRoleFunctions(portalApiPath, allRoleFunctions) + && !matchRoleFunctions(portalApiPath, roleFunctions)) { + EPUser user = (EPUser) request.getSession().getAttribute( + SystemProperties.getProperty(SystemProperties.USER_ATTRIBUTE_NAME)); + logger.error(EELFLoggerDelegate.errorLogger, + "preHandle: User {} not authorized for path {} ", user.getOrgUserId(), + portalApiPath); + EcompPortalUtils.setBadPermissions(user, response, portalApiPath); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeRestApiAuthenticationError); + return false; + } // failed to match + + } // is portalApi + + } // requestURI + } // instance check + } // not accessible + else if (method.getBean() instanceof BasicAuthenticationController) { + return checkBasicAuth(request, response); + } + Object controllerObj = method.getBean(); + if (controllerObj instanceof SessionCommunicationController + || controllerObj instanceof SharedContextRestController + || controllerObj instanceof ExternalAppsRestfulController) { + // check user authentication for RESTful calls + String secretKey = null; + try { + epAdvice.loadServletRequestBasedDefaults(request, SecurityEventTypeEnum.INCOMING_REST_MESSAGE); + if (!remoteWebServiceCallService.verifyRESTCredential(secretKey, request.getHeader(APP_KEY), + request.getHeader("username"), request.getHeader("password"))) { + throw new UrlAccessRestrictedException(); + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "preHandle: failed to authenticate RESTful service", + e); + EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeRestApiAuthenticationError, e); + throw new UrlAccessRestrictedException(); + } + } + + if (controllerObj instanceof WebAnalyticsExtAppController) { + if (!remoteWebServiceCallService.verifyAppKeyCredential(request.getHeader(APP_KEY))) { + logger.error(EELFLoggerDelegate.errorLogger, + "preHandle: failed to verify app key for web analytics call"); + throw new UrlAccessRestrictedException(); + } + } + } + + handleSessionUpdates(request); + return true; + } + + /** + * Sets the status code and sends a response. Factors code out of many + * methods. + * + * @param response + * HttpServletResponse + * @param statusCode + * HTTP status code like 404 + * @param message + * Message to send in a JSON error object + */ + private void sendErrorResponse(HttpServletResponse response, final int statusCode, final String message) + throws Exception { + response.setStatus(statusCode); + response.setContentType("application/json"); + response.getWriter().write("{\"error\":\"" + message + "\"}"); + response.getWriter().flush(); + } + + /** + * Gets HTTP basic authentication information from the request and checks + * whether those credentials are authorized for the request path. + * + * @param request + * HttpServletRequest + * @param response + * HttpServletResponse + * @return True if the request is authorized, else false + * @throws Exception + */ + private boolean checkBasicAuth(HttpServletRequest request, HttpServletResponse response) throws Exception { + String uri = request.getRequestURI().toString(); + uri = uri.substring(uri.indexOf("/", 1)); + + final String authHeader = request.getHeader("Authorization"); + + // Unauthorized access due to missing HTTP Authorization request header + if (authHeader == null) { + final String msg = "no authorization found"; + logger.debug(EELFLoggerDelegate.debugLogger, "checkBasicAuth: {}", msg); + sendErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, msg); + return false; + } + + String[] accountNamePassword = getUserNamePassword(authHeader); + if (accountNamePassword == null || accountNamePassword.length != 2) { + final String msg = "failed to get username and password from Atuhorization header"; + logger.debug(EELFLoggerDelegate.debugLogger, "checkBasicAuth: {}", msg); + sendErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, msg); + return false; + } + + BasicAuthCredentials creds; + try { + creds = basicAuthService.getBasicAuthCredentialByUsernameAndPassword(accountNamePassword[0], + encrypted(accountNamePassword[1])); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "checkBasicAuth failed to get credentials", e); + final String msg = "Failed while getting basic authentication credential: "; + sendErrorResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg); + throw e; + } + + // Unauthorized access due to invalid credentials (username and + // password) + if (creds == null || !creds.getUsername().equals(accountNamePassword[0])) { + final String msg = "Unauthorized: Access denied"; + logger.debug(EELFLoggerDelegate.debugLogger, "checkBasicAuth: {}", msg); + sendErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, msg); + return false; + } + + // Unauthorized access due to inactive account + if (creds.getIsActive().equals("N")) { + final String msg = "Unauthorized: The account is inactive"; + logger.debug(EELFLoggerDelegate.debugLogger, "checkBasicAuth: {}", msg); + sendErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, msg); + return false; + } + boolean isAllowedEp = false; + for (EPEndpoint ep : creds.getEndpoints()) { + if (ep.getName().equals(uri)) { + isAllowedEp = true; + break; + } + } + + // If user doesn't specify any endpoint, allow all endpoints for that + // account + if (creds.getEndpoints().size() == 0) + isAllowedEp = true; + + // Unauthorized access due to the invalid endpoints + if (!isAllowedEp) { + final String msg = "Unauthorized: Endpoint access denied"; + logger.debug(EELFLoggerDelegate.debugLogger, "checkBasicAuth: {}", msg); + sendErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, msg); + return false; + } + + // Made it to the end! + return true; + } + + private String[] getUserNamePassword(String authValue) { + String base64Credentials = authValue.substring("Basic".length()).trim(); + String credentials = new String(Base64.getDecoder().decode(base64Credentials), Charset.forName("UTF-8")); + final String[] values = credentials.split(":", 2); + return values; + } + + @SuppressWarnings("unused") + private String decrypted(String encrypted) throws Exception { + String result = ""; + if (encrypted != null & encrypted.length() > 0) { + try { + result = CipherUtil.decryptPKC(encrypted, SystemProperties.getProperty(SystemProperties.Decryption_Key)); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "decryptedPassword failed", e); + throw e; + } + } + return result; + } + + private String encrypted(String decryptedPwd) throws Exception { + String result = ""; + if (decryptedPwd != null & decryptedPwd.length() > 0) { + try { + result = CipherUtil.encryptPKC(decryptedPwd, + SystemProperties.getProperty(SystemProperties.Decryption_Key)); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, "encryptedPassword() failed", e); + throw e; + } + } + return result; + } + + private Boolean matchRoleFunctions(String portalApiPath, Set<? extends String> roleFunctions) { + String[] path = portalApiPath.split("/"); + List<String> roleFunList = new ArrayList<>(); + if (path.length > 1) { + roleFunList = roleFunctions.stream().filter(item -> item.startsWith(path[0])).collect(Collectors.toList()); + if (roleFunList.size() >= 1) { + for (String roleFunction : roleFunList) { + String[] roleFunctionArray = roleFunction.split("/"); + boolean b = true; + if (roleFunctionArray.length == path.length) { + for (int i = 0; i < roleFunctionArray.length; i++) { + if (b) { + if (!roleFunctionArray[i].equals("*")) { + Pattern p = Pattern.compile(Pattern.quote(path[i]), Pattern.CASE_INSENSITIVE); + Matcher m = p.matcher(roleFunctionArray[i]); + b = m.matches(); + + } + } + } + if (b) + return b; + } + } + } + } else { + for (String roleFunction : roleFunctions) { + if (portalApiPath.matches(roleFunction)) + return true; + } + } + return false; + } + + protected void handleSessionUpdates(HttpServletRequest request) { + PortalTimeoutHandler.handleSessionUpdatesNative(request, null, null, null, null, manageService); + } +} |