/* * Copyright (c) 2012-2013 EMC Corporation * All Rights Reserved */ package com.emc.storageos.security.authentication; import java.io.IOException; import java.net.URI; import java.net.URLEncoder; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.HttpMethod; import javax.ws.rs.core.HttpHeaders; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import com.emc.storageos.services.util.SecurityUtils; import com.emc.storageos.svcs.errorhandling.resources.APIException; import com.emc.storageos.svcs.errorhandling.resources.InternalException; import com.google.common.net.InetAddresses; /** * Client side Authentication filter for the Bourne Token authentication mechanism */ public class TokenBasedAuthenticationFilter extends AbstractAuthenticationFilter { private final Logger _logger = LoggerFactory.getLogger(TokenBasedAuthenticationFilter.class); private final String REQUESTING_COOKIES = RequestProcessingUtils.REQUESTING_COOKIES; @Autowired private AuthSvcEndPointLocator _endpointLocator; private boolean _usingFormLogin; /** * Set usingFormLogin to redirect the service to form login page * * @param value true or false */ public void setUsingFormLogin(String value) { _usingFormLogin = Boolean.parseBoolean(value); } // Overriding doFilter even though it is in the base class. // Need a different logic to forward to authsvc when authenticate fails. @Override public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { final HttpServletResponse response = (HttpServletResponse) servletResponse; final HttpServletRequest request = (HttpServletRequest) servletRequest; AbstractRequestWrapper reqWrapper = null; try { reqWrapper = authenticate(servletRequest); } catch (APIException e) { _logger.debug("unauthorized request: serviceUrl = " + request.getRequestURI(), e); response.setStatus(toHTTPStatus(e)); response.getOutputStream().print(toServiceErrorXml(e)); response.setHeader("Content-Type", "application/xml"); return; } catch (final InternalException e) { response.setStatus(toHTTPStatus(e)); response.getOutputStream().print(toServiceErrorXml(e)); response.setHeader("Content-Type", "application/xml"); return; } if (reqWrapper != null) { // we are done, forward it to resource service forwardToService(servletRequest, servletResponse, reqWrapper); } else { // We need to go get a token from authsvc. forwardToAuthService(request, response); } } @Override protected AbstractRequestWrapper authenticate(ServletRequest servletRequest) { final StorageOSUser user = getStorageOSUserFromRequest(servletRequest, true); if (user != null) { // Token found and validated. Proceed to the rest of the filter chain. return new AbstractRequestWrapper((HttpServletRequest) servletRequest, user); } else { _logger.debug("No token found in request."); return null; } } /** * Forward to Bourne authsvc * * @param req * @param servletResponse * @throws java.io.IOException * @throws javax.servlet.ServletException */ protected void forwardToAuthService(final HttpServletRequest req, final HttpServletResponse servletResponse) throws IOException, ServletException { boolean formLoginRequested = _usingFormLogin || RequestProcessingUtils.isRequestingQueryParam(req, RequestProcessingUtils.REQUESTING_FORMLOGIN); boolean cookiesRequested = RequestProcessingUtils.isRequestingQueryParam(req, RequestProcessingUtils.REQUESTING_COOKIES); boolean isRequestFromLB = RequestProcessingUtils.isRequestFromLoadBalancer(req); URI endpoint = null; if (isRequestFromLB) { endpoint = URI.create(req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort()); } else { try { endpoint = _endpointLocator.getAnEndpoint(); } catch (InternalException e) { servletResponse.sendError(toHTTPStatus(e), toServiceErrorXml(e)); } } StringBuilder redirectURL = new StringBuilder(endpoint.toString()); if (cookiesRequested || formLoginRequested || !InetAddresses.isInetAddress(endpoint.getHost())) { // ok, then, keep them on the same node redirectURL = RequestProcessingUtils.getOnNodeAuthsvcRedirectURL(req, endpoint); } if (formLoginRequested) { redirectURL.append("/formlogin?"); } else { redirectURL.append("/login?"); } StringBuilder serviceURL = new StringBuilder(SecurityUtils.stripXSS(req.getRequestURL().toString())); String queryString = SecurityUtils.stripXSS(RequestProcessingUtils.removeFromQueryString(req.getQueryString(), REQUESTING_COOKIES)); if (queryString != null && !queryString.isEmpty()) { serviceURL.append("?" + queryString); } redirectURL.append("service="); redirectURL.append(URLEncoder.encode(serviceURL.toString(), "UTF-8")); // adding requesting cookies if needed if (cookiesRequested) { redirectURL.append(String.format("&%s=true", REQUESTING_COOKIES)); } // CHECK - if we are already back from redirect or we have a non-GET request // we don't redirect in these cases boolean redirectLoop = (req.getQueryString() != null && req.getQueryString().contains(RequestProcessingUtils.REDIRECT_FROM_AUTHSVC)); if (redirectLoop || !req.getMethod().equals(HttpMethod.GET)) { servletResponse.setHeader(HttpHeaders.LOCATION, redirectURL.toString()); _logger.debug("sending unauthorized status code (401), Location={}" + redirectURL); servletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, (redirectLoop) ? "using cookies? retry using \"using-cookies\" query parameter" : "Non GET Unauthenticated request: authenticate using " + redirectURL); } else { _logger.info("redirecting request for authentication: url: {}", redirectURL.toString()); servletResponse.sendRedirect(redirectURL.toString()); } } }