* ========================LICENSE_START=================================
* O-RAN-SC
* %%
- * Copyright (C) 2019 AT&T Intellectual Property and Nokia
+ * Copyright (C) 2019 AT&T Intellectual Property
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.onap.portalsdk.core.onboarding.util.KeyProperties;
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.DashboardUserManager;
import org.oransc.ric.portal.dashboard.model.EcompUserDetails;
+import org.owasp.esapi.reference.DefaultSecurityConfiguration;
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;
* created and EPService cookie is set.
* </UL>
*
- * TODO: What about sessions? Will this be stateless?
+ * Open question: what about sessions? Will this be stateless?
*
* This filter uses no annotations to avoid Spring's automatic registration,
* which add this filter in the chain in the wrong order.
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+ // Unfortunately not all file names are defined as constants
+ private static final String[] securityPropertyFiles = { KeyProperties.PROPERTY_FILE_NAME,
+ PortalApiProperties.PROPERTY_FILE_NAME, DefaultSecurityConfiguration.DEFAULT_RESOURCE_FILE,
+ "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 SecurityException(msg);
+ } else {
+ try {
+ in.close();
+ } catch (IOException ex) {
+ logger.warn("Failed to close stream", ex);
+ }
+ }
+ }
+ }
}
@Override
// No resources to release
}
+ /**
+ * 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 {
+ if (enforcePortalSecurity)
+ doFilterEPSDKFW(req, res, chain);
+ else
+ doFilterMockUserAdminRole(req, res, chain);
+ }
+
/*
* Populates security context with a mock user in the admin role.
*
- * TODO: AUTH
*/
- @Override
- public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
+ private void doFilterMockUserAdminRole(ServletRequest req, ServletResponse res, FilterChain chain)
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);
PreAuthenticatedAuthenticationToken authToken = new PreAuthenticatedAuthenticationToken(userDetails,
"fakeCredentials", userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authToken);
- }
- else {
+ } 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. Requests for pages ignored in the web security config do
- * not hit this filter.
- *
- * TODO: AUTH
+ * to Portal otherwise.
*/
- public void doFilter_EPSDKFW(ServletRequest req, ServletResponse res, FilterChain chain)
+ 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 String userId = authManager.validateEcompSso(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
}
}
- 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.
+ return 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>");
}
/**