Serve login page without using redirect
[portal/ric-dashboard.git] / webapp-backend / src / main / java / org / oransc / ric / portal / dashboard / portalapi / PortalAuthenticationFilter.java
index 2ec5938..50f5b89 100644 (file)
 package org.oransc.ric.portal.dashboard.portalapi;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
 import java.lang.invoke.MethodHandles;
 import java.net.URLEncoder;
+import java.util.HashSet;
 
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
@@ -36,11 +38,14 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.onap.portalsdk.core.onboarding.util.PortalApiConstants;
 import org.onap.portalsdk.core.onboarding.util.PortalApiProperties;
+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.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;
 
@@ -73,15 +78,39 @@ public class PortalAuthenticationFilter implements Filter {
 
        private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
+       // Unfortunately these names are not available as constants
+       private static final String[] securityPropertyFiles = { "ESAPI.properties", "key.properties", "portal.properties",
+                       "validation.properties" };
+
        public static final String REDIRECT_URL_KEY = "redirectUrl";
 
+       private final boolean enforcePortalSecurity;
        private final PortalAuthManager authManager;
 
        private final DashboardUserManager userManager;
 
-       public PortalAuthenticationFilter(PortalAuthManager authManager, DashboardUserManager userManager) {
+       public PortalAuthenticationFilter(boolean portalSecurity, PortalAuthManager authManager,
+                       DashboardUserManager userManager) {
+               this.enforcePortalSecurity = portalSecurity;
                this.authManager = authManager;
                this.userManager = userManager;
+               if (portalSecurity) {
+                       // Throw if security is requested and prerequisites are not met
+                       for (String pf : securityPropertyFiles) {
+                               InputStream in = MethodHandles.lookup().lookupClass().getClassLoader().getResourceAsStream(pf);
+                               if (in == null) {
+                                       String msg = "Failed to find property file on classpath: " + pf;
+                                       logger.error(msg);
+                                       throw new RuntimeException(msg);
+                               } else {
+                                       try {
+                                               in.close();
+                                       } catch (IOException ex) {
+                                               logger.warn("Failed to close stream", ex);
+                                       }
+                               }
+                       }
+               }
        }
 
        @Override
@@ -100,23 +129,69 @@ public class PortalAuthenticationFilter implements Filter {
        }
 
        /**
-        * Checks for valid cookies and allows request to be served if found; redirects
-        * to Portal otherwise. Requests for pages ignored in the web security config do
-        * not hit this filter.
+        * Requests for pages ignored in the web security config do not hit this filter.
         */
        @Override
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
                        throws IOException, ServletException {
-               logger.debug("doFilter {}", req);
+               if (enforcePortalSecurity)
+                       doFilterEPSDKFW(req, res, chain);
+               else
+                       doFilterMockUserAdminRole(req, res, chain);
+       }
+
+       /*
+        * Populates security context with a mock user in the admin role.
+        * 
+        */
+       private void doFilterMockUserAdminRole(ServletRequest req, ServletResponse res, FilterChain chain)
+                       throws IOException, ServletException {
+               Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+               if (auth == null || auth.getAuthorities().isEmpty()) {
+                       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);
+                       HashSet<EcompRole> roles = new HashSet<>();
+                       roles.add(admin);
+                       EcompUser user = new EcompUser();
+                       user.setLoginId("fakeLoginId");
+                       user.setRoles(roles);
+                       user.setActive(true);
+                       EcompUserDetails userDetails = new EcompUserDetails(user);
+                       PreAuthenticatedAuthenticationToken authToken = new PreAuthenticatedAuthenticationToken(userDetails,
+                                       "fakeCredentials", userDetails.getAuthorities());
+                       SecurityContextHolder.getContext().setAuthentication(authToken);
+               } else {
+                       logger.debug("doFilter: authorities {}", auth.getAuthorities());
+               }
+               chain.doFilter(req, res);
+       }
+
+       /*
+        * Checks for valid cookies and allows request to be served if found; redirects
+        * to Portal otherwise.
+        */
+       private void doFilterEPSDKFW(ServletRequest req, ServletResponse res, FilterChain chain)
+                       throws IOException, ServletException {
                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
@@ -128,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 = "<a href=\"" + redirectUrl + "\">";
+               // If only Java had "here" documents.
+               String body = String.join(//
+                               System.getProperty("line.separator"), //
+                               "<html>", //
+                               "<head>", //
+                               "<title>RIC Dashboard</title>", //
+                               "<style>", //
+                               "html, body { ", //
+                               "  font-family: Helvetica, Arial, sans-serif;", //
+                               "}", //
+                               "</style>", //
+                               "</head>", //
+                               "<body>", //
+                               "<h2>RIC Dashboard</h2>", //
+                               "<h4>Please log in.</h4>", //
+                               "<p>", //
+                               aHref, "Click here to authenticate at the ONAP Portal</a>", //
+                               "</p>", //
+                               "</body>", //
+                               "</html>");
+               return body;
        }
 
        /**