From 9fef9615bd5889eacbe8ddad454b7ff4b4c195c0 Mon Sep 17 00:00:00 2001 From: "Lott, Christopher (cl778h)" Date: Thu, 3 Oct 2019 15:58:33 -0400 Subject: [PATCH] Serve login page without using redirect This preserves original scheme, either HTTP or HTTPS. Correct URL of Dashboard REST URL in config-deploy doc. Bump version to 1.2.3. Change-Id: Ia7da1e2a7a0f189c95072ddda51ac59a738d9247 Signed-off-by: Lott, Christopher (cl778h) --- a1-med-client/pom.xml | 2 +- anr-xapp-client/pom.xml | 2 +- app-mgr-client/pom.xml | 2 +- docs/config-deploy.rst | 4 +- docs/release-notes.rst | 5 + e2-mgr-client/pom.xml | 2 +- pom.xml | 4 +- webapp-backend/pom.xml | 2 +- .../ric/portal/dashboard/DashboardConstants.java | 1 - .../oransc/ric/portal/dashboard/LoginServlet.java | 116 --------------------- .../dashboard/config/WebSecurityConfiguration.java | 22 +--- .../dashboard/controller/AdminController.java | 1 + .../dashboard/controller/E2ManagerController.java | 4 +- .../dashboard/controller/Html5PathsController.java | 18 +++- .../dashboard/portalapi/DashboardUserManager.java | 57 +++++++++- .../portalapi/PortalAuthenticationFilter.java | 70 +++++++++---- .../src/main/resources/application.properties | 2 +- .../config/WebSecurityMockConfiguration.java | 21 +--- .../controller/PortalRestCentralServiceTest.java | 12 +-- webapp-frontend/pom.xml | 2 +- 20 files changed, 148 insertions(+), 201 deletions(-) delete mode 100644 webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/LoginServlet.java diff --git a/a1-med-client/pom.xml b/a1-med-client/pom.xml index d1dbf5c8..8cfc29d0 100644 --- a/a1-med-client/pom.xml +++ b/a1-med-client/pom.xml @@ -25,7 +25,7 @@ limitations under the License. org.o-ran-sc.portal.ric-dashboard ric-dash-parent - 1.2.2-SNAPSHOT + 1.2.3-SNAPSHOT org.o-ran-sc.ric.plt.a1med.client diff --git a/anr-xapp-client/pom.xml b/anr-xapp-client/pom.xml index 05cddf9a..d7dcaff5 100644 --- a/anr-xapp-client/pom.xml +++ b/anr-xapp-client/pom.xml @@ -25,7 +25,7 @@ limitations under the License. org.o-ran-sc.portal.ric-dashboard ric-dash-parent - 1.2.2-SNAPSHOT + 1.2.3-SNAPSHOT org.o-ran-sc.ric.xapp.anr.client diff --git a/app-mgr-client/pom.xml b/app-mgr-client/pom.xml index d27f10c9..b6ea408e 100644 --- a/app-mgr-client/pom.xml +++ b/app-mgr-client/pom.xml @@ -25,7 +25,7 @@ limitations under the License. org.o-ran-sc.portal.ric-dashboard ric-dash-parent - 1.2.2-SNAPSHOT + 1.2.3-SNAPSHOT org.o-ran-sc.ric.plt.appmgr.client diff --git a/docs/config-deploy.rst b/docs/config-deploy.rst index 5f62be40..c6d5086a 100644 --- a/docs/config-deploy.rst +++ b/docs/config-deploy.rst @@ -210,8 +210,8 @@ must supply the following information about the deployed instance: ``http://dashboard.simpledemo.onap.org:8080`` - Dashboard REST URL that is reachable by the Portal back-end server. This can be a host name or an IP address, because it does not use - cookie-based authentication. This should be a value like - ``http://192.168.1.1:8080/auxapi/v3`` + cookie-based authentication. This must be a URL with suffix "/api/v3" + for example ``http://192.168.1.1:8080/api/v3``. The Dashboard server only listens on a single port, so the examples above both use the same port number. Different port numbers might be diff --git a/docs/release-notes.rst b/docs/release-notes.rst index 2453d603..2ab746fa 100644 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -19,6 +19,11 @@ RIC Dashboard Release Notes =========================== +Version 1.2.3, 4 Oct 2019 +------------------------- +* Serve unauthenticated user a login-at-portal page without using redirect +* Upgrade to Spring-Boot 2.1.9.RELEASE + Version 1.2.2, 27 Sep 2019 -------------------------- * Support Portal security using EPSDK-FW cookie and user management diff --git a/e2-mgr-client/pom.xml b/e2-mgr-client/pom.xml index 723b4ab3..4d4f8976 100644 --- a/e2-mgr-client/pom.xml +++ b/e2-mgr-client/pom.xml @@ -25,7 +25,7 @@ limitations under the License. org.o-ran-sc.portal.ric-dashboard ric-dash-parent - 1.2.2-SNAPSHOT + 1.2.3-SNAPSHOT org.o-ran-sc.ric.plt.e2mgr.client diff --git a/pom.xml b/pom.xml index 4109bdc6..8c394b35 100644 --- a/pom.xml +++ b/pom.xml @@ -26,14 +26,14 @@ limitations under the License. org.springframework.boot spring-boot-starter-parent - 2.1.6.RELEASE + 2.1.9.RELEASE org.o-ran-sc.portal.ric-dashboard ric-dash-parent RIC Dashboard project pom - 1.2.2-SNAPSHOT + 1.2.3-SNAPSHOT 11 diff --git a/webapp-backend/pom.xml b/webapp-backend/pom.xml index e008f8bb..6976b00e 100644 --- a/webapp-backend/pom.xml +++ b/webapp-backend/pom.xml @@ -25,7 +25,7 @@ limitations under the License. org.o-ran-sc.portal.ric-dashboard ric-dash-parent - 1.2.2-SNAPSHOT + 1.2.3-SNAPSHOT ric-dash-be RIC Dashboard Webapp backend diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardConstants.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardConstants.java index c441c17e..f3352f69 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardConstants.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardConstants.java @@ -26,7 +26,6 @@ public abstract class DashboardConstants { } public static final String ENDPOINT_PREFIX = "/api"; - public static final String LOGIN_PAGE = "/login.html"; // Factor out method names used in multiple controllers public static final String VERSION_METHOD = "version"; public static final String APP_NAME_AC = "AC"; diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/LoginServlet.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/LoginServlet.java deleted file mode 100644 index fe58e933..00000000 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/LoginServlet.java +++ /dev/null @@ -1,116 +0,0 @@ -/*- - * ========================LICENSE_START================================= - * O-RAN-SC - * %% - * Copyright (C) 2019 AT&T Intellectual Property and Nokia - * %% - * 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.oransc.ric.portal.dashboard; - -import java.io.IOException; -import java.lang.invoke.MethodHandles; -import java.net.URLEncoder; - -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.onap.portalsdk.core.onboarding.util.PortalApiConstants; -import org.onap.portalsdk.core.onboarding.util.PortalApiProperties; -import org.oransc.ric.portal.dashboard.portalapi.PortalAuthenticationFilter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.MediaType; - -/** - * Serves a login page that contains a link from configuration to ONAP Portal. - * This avoids the immediate redirect to Portal that is confusing to users and - * infuriating to developers. - * - * Basically this is do-it-yourself JSP :) - */ -public class LoginServlet extends HttpServlet { - - private static final long serialVersionUID = 1191385178190976568L; - - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - @Override - public void init(ServletConfig servletConfig) throws ServletException { - logger.debug("init"); - super.init(servletConfig); - final String portalURL = PortalApiProperties.getProperty(PortalApiConstants.ECOMP_REDIRECT_URL); - if (portalURL == null || portalURL.length() == 0) - throw new ServletException("Failed to get property " + PortalApiConstants.ECOMP_REDIRECT_URL); - } - - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) - throws IOException, ServletException { - logger.debug("doGet {}", request.getRequestURI()); - // The original page URL should arrive as a query parameter - String appUrl = request.getParameter(PortalAuthenticationFilter.REDIRECT_URL_KEY); - // If a user bookmarks the login page, then nothing arrives; - // use the original URL without the login suffix. - if (appUrl == null || appUrl.isEmpty()) { - String loginUrl = request.getRequestURL().toString(); - int indexOfLogin = loginUrl.indexOf(DashboardConstants.LOGIN_PAGE); - appUrl = loginUrl.substring(0, indexOfLogin); - } - String encodedAppUrl = URLEncoder.encode(appUrl, "UTF-8"); - String portalBaseUrl = PortalApiProperties.getProperty(PortalApiConstants.ECOMP_REDIRECT_URL); - String redirectUrl = portalBaseUrl + "?" + PortalAuthenticationFilter.REDIRECT_URL_KEY + "=" + encodedAppUrl; - String aHref = ""; - // If only Java had "here" documents. - String body = String.join(// - System.getProperty("line.separator"), // - "", // - "", // - "RIC Dashboard", // - "", // - "", // - "", // - "

RIC Dashboard

", // - "

Please log in.

", // - "

", // - aHref, "Click here to authenticate at the ONAP Portal", // - "

", // - "", // - ""); - writeAndFlush(response, MediaType.TEXT_HTML_VALUE, body); - } - - /** - * Sets the content type and writes the response. - * - * @param response - * @param contentType - * @param responseBody - * @throws IOException - */ - private void writeAndFlush(HttpServletResponse response, String contentType, String responseBody) - throws IOException { - response.setContentType(contentType); - response.getWriter().print(responseBody); - response.getWriter().flush(); - } - -} diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/WebSecurityConfiguration.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/WebSecurityConfiguration.java index edb80c8e..20a15023 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/WebSecurityConfiguration.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/WebSecurityConfiguration.java @@ -24,8 +24,6 @@ import java.lang.invoke.MethodHandles; import java.lang.reflect.InvocationTargetException; import org.onap.portalsdk.core.onboarding.util.PortalApiConstants; -import org.oransc.ric.portal.dashboard.DashboardConstants; -import org.oransc.ric.portal.dashboard.LoginServlet; import org.oransc.ric.portal.dashboard.controller.A1MediatorController; import org.oransc.ric.portal.dashboard.controller.AdminController; import org.oransc.ric.portal.dashboard.controller.AnrXappController; @@ -38,7 +36,6 @@ import org.oransc.ric.portal.dashboard.portalapi.PortalAuthenticationFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; @@ -105,8 +102,7 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { AppManagerController.CONTROLLER_PATH + "/" + AppManagerController.VERSION_METHOD, // E2ManagerController.CONTROLLER_PATH + "/" + E2ManagerController.HEALTH_METHOD, // E2ManagerController.CONTROLLER_PATH + "/" + E2ManagerController.VERSION_METHOD, // - SimpleErrorController.ERROR_PATH, // - DashboardConstants.LOGIN_PAGE // + SimpleErrorController.ERROR_PATH }; @Override @@ -144,20 +140,4 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { return portalAuthenticationFilter; } - /** - * Instantiates a trivial login servlet that serves a basic page with a link to - * authenticate at Portal. The login filter redirects to this page instead of - * Portal. - * - * @return Servlet registration bean for the Dashboard login servlet. - */ - @Bean - public ServletRegistrationBean loginServletBean() { - LoginServlet servlet = new LoginServlet(); - final ServletRegistrationBean servletBean = new ServletRegistrationBean<>(servlet, - DashboardConstants.LOGIN_PAGE); - servletBean.setName("LoginServlet"); - return servletBean; - } - } diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AdminController.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AdminController.java index 58dab296..7ad509ad 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AdminController.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AdminController.java @@ -67,6 +67,7 @@ public class AdminController { @Value("${metrics.url.mc}") private String mcAppMetricsUrl; + public AdminController() { // Mock data users = new DashboardUser[] { // diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/E2ManagerController.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/E2ManagerController.java index a5e3b52a..f164734e 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/E2ManagerController.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/E2ManagerController.java @@ -74,7 +74,7 @@ public class E2ManagerController { public static final String HEALTH_METHOD = "health"; public static final String VERSION_METHOD = DashboardConstants.VERSION_METHOD; // Keep these consistent with the E2M implementation - /*package*/ static final String NODEB_PREFIX = "/nodeb"; + /* package */ static final String NODEB_PREFIX = "/nodeb"; public static final String RAN_METHOD = NODEB_PREFIX + "/ran"; public static final String NODEB_SHUTDOWN_METHOD = NODEB_PREFIX + "/shutdown"; public static final String NODEB_LIST_METHOD = NODEB_PREFIX + "/ids"; @@ -181,7 +181,7 @@ public class E2ManagerController { } @ApiOperation(value = "Abort any other ongoing procedures over X2 between the RIC and the RAN.") - @PutMapping(NODEB_PREFIX + "/{" + PP_RANNAME + "}"+ RESET_METHOD) + @PutMapping(NODEB_PREFIX + "/{" + PP_RANNAME + "}" + RESET_METHOD) @Secured({ DashboardConstants.ROLE_ADMIN }) public void reset(@PathVariable(PP_RANNAME) String ranName, @RequestBody ResetRequest resetRequest, HttpServletResponse response) { diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/Html5PathsController.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/Html5PathsController.java index abe3b16f..ac64c875 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/Html5PathsController.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/Html5PathsController.java @@ -19,9 +19,12 @@ */ package org.oransc.ric.portal.dashboard.controller; +import java.io.IOException; import java.lang.invoke.MethodHandles; +import java.net.URL; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,14 +49,19 @@ public class Html5PathsController { * https://stackoverflow.com/questions/44692781/configure-spring-boot-to-redirect-404-to-a-single-page-app * * @param request - * HttpServletRequest - * @return Forward directive to index.html + * HttpServletRequest + * @param response + * HttpServletResponse + * @throws IOException + * On error */ @RequestMapping(method = { RequestMethod.OPTIONS, RequestMethod.GET }, // path = { "/catalog", "/control", "/stats", "/user" }) - public String forwardAngularRoutes(HttpServletRequest request) { - logger.debug("forwardAngularRoutes: {}", request.getRequestURI()); - return "forward:/index.html"; + public void forwardAngularRoutes(HttpServletRequest request, HttpServletResponse response) throws IOException { + URL url = new URL(request.getScheme(), request.getServerName(), request.getServerPort(), "/index.html"); + if (logger.isDebugEnabled()) + logger.debug("forwardAngularRoutes: {} redirected to {}", request.getRequestURI(), url); + response.sendRedirect(url.toString()); } } diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/DashboardUserManager.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/DashboardUserManager.java index b02d0261..6b92a30b 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/DashboardUserManager.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/DashboardUserManager.java @@ -23,10 +23,14 @@ import java.io.File; import java.io.IOException; import java.lang.invoke.MethodHandles; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.onap.portalsdk.core.onboarding.exception.PortalAPIException; +import org.onap.portalsdk.core.restful.domain.EcompRole; import org.onap.portalsdk.core.restful.domain.EcompUser; +import org.oransc.ric.portal.dashboard.DashboardConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,16 +42,47 @@ import com.fasterxml.jackson.databind.ObjectMapper; /** * Provides user-management services. * - * This first implementation serializes user details to a file. TODO: migrate to - * a database. + * This first implementation serializes user details to a file. + * + * TODO: migrate to a database. */ public class DashboardUserManager { private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + public static final String USER_FILE_PATH = "/tmp/dashboard-users.json"; + private final File userFile; private final List users; + /** + * convenience constructor that uses default file path + * + * @param clear + * If true, start empty and remove any existing file. + * + * @throws IOException + * On file error + */ + public DashboardUserManager(boolean clear) throws IOException { + this(USER_FILE_PATH); + if (clear) { + logger.debug("ctor: removing file {}", userFile.getAbsolutePath()); + File f = new File(DashboardUserManager.USER_FILE_PATH); + if (f.exists()) + f.delete(); + users.clear(); + } + } + + /** + * Uses specified file path + * + * @param userFilePath + * File path + * @throws IOException + * If file cannot be read + */ public DashboardUserManager(final String userFilePath) throws IOException { logger.debug("ctor: userfile {}", userFilePath); if (userFilePath == null) @@ -119,4 +154,22 @@ public class DashboardUserManager { } } + // Test infrastructure + public static void main(String[] args) throws Exception { + DashboardUserManager dum = new DashboardUserManager(false); + EcompUser user = new EcompUser(); + user.setActive(true); + user.setLoginId("demo"); + user.setFirstName("First"); + user.setLastName("Last"); + EcompRole role = new EcompRole(); + role.setId(1L); + role.setName(DashboardConstants.ROLE_NAME_ADMIN); + Set roles = new HashSet<>(); + roles.add(role); + user.setRoles(roles); + dum.createUser(user); + logger.debug("Created user {}", user); + } + } diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthenticationFilter.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthenticationFilter.java index 18668f3f..50f5b894 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthenticationFilter.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthenticationFilter.java @@ -44,6 +44,7 @@ import org.oransc.ric.portal.dashboard.DashboardConstants; import org.oransc.ric.portal.dashboard.model.EcompUserDetails; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.http.MediaType; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; @@ -147,7 +148,10 @@ public class PortalAuthenticationFilter implements Filter { throws IOException, ServletException { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth == null || auth.getAuthorities().isEmpty()) { - logger.debug("doFilter adding auth to request {}", req); + if (logger.isDebugEnabled()) { + logger.debug("doFilter adding auth to request URI {}", + (req instanceof HttpServletRequest) ? ((HttpServletRequest) req).getRequestURL() : req); + } EcompRole admin = new EcompRole(); admin.setId(1L); admin.setName(DashboardConstants.ROLE_ADMIN); @@ -173,16 +177,21 @@ public class PortalAuthenticationFilter implements Filter { */ private void doFilterEPSDKFW(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { - logger.debug("doFilter {}", req); HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; + if (logger.isTraceEnabled()) + logger.trace("doFilter: req {}", request.getRequestURI()); // Need to authenticate the request final String userId = authManager.valdiateEcompSso(request); final EcompUser ecompUser = (userId == null ? null : userManager.getUser(userId)); if (userId == null || ecompUser == null) { - String redirectURL = buildLoginPageUrl(request); - logger.trace("doFilter: unauthorized, redirecting to {}", redirectURL); - response.sendRedirect(redirectURL); + logger.debug("doFilter: unauthorized user requests URI {}, serving login page", request.getRequestURI()); + StringBuffer sb = request.getRequestURL(); + sb.append(request.getQueryString() == null ? "" : "?" + request.getQueryString()); + String body = generateLoginRedirectPage(sb.toString()); + response.setContentType(MediaType.TEXT_HTML_VALUE); + response.getWriter().print(body); + response.getWriter().flush(); } else { EcompUserDetails userDetails = new EcompUserDetails(ecompUser); // Using portal session as credentials is a hack @@ -194,19 +203,44 @@ public class PortalAuthenticationFilter implements Filter { } } - private String buildLoginPageUrl(HttpServletRequest request) { - logger.trace("buildLoginPageUrl"); - // Why so much work to recover the original request? - final StringBuffer sb = request.getRequestURL(); - sb.append(request.getQueryString() == null ? "" : "?" + request.getQueryString()); - final String requestedUrl = sb.toString(); - String encodedUrl = null; - try { - encodedUrl = URLEncoder.encode(requestedUrl, "UTF-8"); - } catch (UnsupportedEncodingException ex) { - logger.error("buildLoginPageUrl: Failed to encode {}", requestedUrl); - } - return DashboardConstants.LOGIN_PAGE + "?" + REDIRECT_URL_KEY + "=" + encodedUrl; + /** + * Generates a page with text only, absolutely no references to any webapp + * resources, so this can be served to an unauthenticated user without + * triggering a new authentication attempt. The page has a link to the Portal + * URL from configuration, with a return URL that is the original request. + * + * @param appUrl + * Original requested URL + * @return HTML + * @throws UnsupportedEncodingException + * On error + */ + private static String generateLoginRedirectPage(String appUrl) throws UnsupportedEncodingException { + String encodedAppUrl = URLEncoder.encode(appUrl, "UTF-8"); + String portalBaseUrl = PortalApiProperties.getProperty(PortalApiConstants.ECOMP_REDIRECT_URL); + String redirectUrl = portalBaseUrl + "?" + PortalAuthenticationFilter.REDIRECT_URL_KEY + "=" + encodedAppUrl; + String aHref = ""; + // If only Java had "here" documents. + String body = String.join(// + System.getProperty("line.separator"), // + "", // + "", // + "RIC Dashboard", // + "", // + "", // + "", // + "

RIC Dashboard

", // + "

Please log in.

", // + "

", // + aHref, "Click here to authenticate at the ONAP Portal", // + "

", // + "", // + ""); + return body; } /** diff --git a/webapp-backend/src/main/resources/application.properties b/webapp-backend/src/main/resources/application.properties index 45eb6695..bcc6cbc9 100644 --- a/webapp-backend/src/main/resources/application.properties +++ b/webapp-backend/src/main/resources/application.properties @@ -29,7 +29,7 @@ server.port = 8080 userfile = users.json # boolean flag whether to enforce Portal user and roles on requests -portalapi.security = false +portalapi.security = true # class that decrypts ciphertext from Portal portalapi.decryptor = org.oransc.ric.portal.dashboard.portalapi.PortalSdkDecryptorAes # name of request cookie with user ID diff --git a/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/WebSecurityMockConfiguration.java b/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/WebSecurityMockConfiguration.java index 8d066934..de2ee55d 100644 --- a/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/WebSecurityMockConfiguration.java +++ b/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/WebSecurityMockConfiguration.java @@ -19,7 +19,6 @@ */ package org.oransc.ric.portal.dashboard.config; -import java.io.File; import java.io.IOException; import java.lang.invoke.MethodHandles; import java.util.HashSet; @@ -29,12 +28,10 @@ import org.onap.portalsdk.core.onboarding.exception.PortalAPIException; import org.onap.portalsdk.core.restful.domain.EcompRole; import org.onap.portalsdk.core.restful.domain.EcompUser; import org.oransc.ric.portal.dashboard.DashboardConstants; -import org.oransc.ric.portal.dashboard.LoginServlet; import org.oransc.ric.portal.dashboard.portalapi.DashboardUserManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; @@ -92,22 +89,10 @@ public class WebSecurityMockConfiguration extends WebSecurityConfigurerAdapter { web.ignoring().antMatchers("/", "/csrf"); // allow swagger-ui to load } - @Bean - public ServletRegistrationBean loginServlet() { - LoginServlet servlet = new LoginServlet(); - final ServletRegistrationBean servletBean = new ServletRegistrationBean<>(servlet, - DashboardConstants.LOGIN_PAGE); - servletBean.setName("LoginServlet"); - return servletBean; - } - // This implementation is so light it can be used during tests. @Bean public DashboardUserManager dashboardUserManager() throws IOException, PortalAPIException { - File f = new File("/tmp/users.json"); - if (f.exists()) - f.delete(); - DashboardUserManager um = new DashboardUserManager(f.getAbsolutePath()); + DashboardUserManager dum = new DashboardUserManager(true); // Mock user for convenience in testing EcompUser demo = new EcompUser(); demo.setLoginId("demo"); @@ -119,8 +104,8 @@ public class WebSecurityMockConfiguration extends WebSecurityConfigurerAdapter { Set roles = new HashSet<>(); roles.add(role); demo.setRoles(roles); - um.createUser(demo); - return um; + dum.createUser(demo); + return dum; } } diff --git a/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/PortalRestCentralServiceTest.java b/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/PortalRestCentralServiceTest.java index bb310282..4f241010 100644 --- a/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/PortalRestCentralServiceTest.java +++ b/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/PortalRestCentralServiceTest.java @@ -26,7 +26,6 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.onap.portalsdk.core.onboarding.util.PortalApiConstants; import org.onap.portalsdk.core.restful.domain.EcompUser; -import org.oransc.ric.portal.dashboard.DashboardConstants; import org.oransc.ric.portal.dashboard.config.PortalApIMockConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,8 +38,6 @@ public class PortalRestCentralServiceTest extends AbstractControllerTest { private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - // paths are hardcoded here exactly like the EPSDK-FW library :( - @Test public void getAnalyticsTest() { // paths are hardcoded here exactly like the EPSDK-FW library :( @@ -52,12 +49,13 @@ public class PortalRestCentralServiceTest extends AbstractControllerTest { } @Test - public void getLoginPageTest() { - URI uri = buildUri(null, DashboardConstants.LOGIN_PAGE); + public void getErrorPageTest() { + // Send unauthorized request + URI uri = buildUri(null, "/favicon.ico"); logger.info("Invoking {}", uri); ResponseEntity response = restTemplate.exchange(uri, HttpMethod.GET, null, String.class); - Assertions.assertTrue(response.getStatusCode().is2xxSuccessful()); - Assertions.assertTrue(response.getBody().contains("Please log in")); + Assertions.assertTrue(response.getStatusCode().is4xxClientError()); + Assertions.assertTrue(response.getBody().contains("Static error page")); } private HttpEntity getEntityWithHeaders(Object body) { diff --git a/webapp-frontend/pom.xml b/webapp-frontend/pom.xml index 8080a0f3..dfd36b03 100644 --- a/webapp-frontend/pom.xml +++ b/webapp-frontend/pom.xml @@ -25,7 +25,7 @@ limitations under the License. org.o-ran-sc.portal.ric-dashboard ric-dash-parent - 1.2.2-SNAPSHOT + 1.2.3-SNAPSHOT ric-dash-fe RIC Dashboard Webapp frontend -- 2.16.6