Commit d83e4689 authored by Matija Obreza's avatar Matija Obreza

ATiC: Set-Cookie based on Authorization header

parent 56db106a
......@@ -16,8 +16,14 @@
package org.genesys2.server.servlet.filter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
......@@ -25,41 +31,81 @@ import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.filter.OncePerRequestFilter;
/**
* Converts the "access_token" cookie to the "Authorization" HTTP request header.
*
* @author Maxym Borodenko
* @author Matija Obreza
*/
public class AccessTokenInCookieFilter extends OncePerRequestFilter {
public static final Logger LOG = LoggerFactory.getLogger(AccessTokenInCookieFilter.class);
private static final String ACCESS_TOKEN_COOKIE_NAME = "APITOKEN";
@Value("${host.name}") // we're using the API host name for cookie domain here
private String cookieDomain;
@Value("${base.cookie-secure}")
private boolean cookieSecure;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = null;
if (request.getHeader("Authorization") == null && request.getCookies() != null) {
for (Cookie cookie : request.getCookies()) {
if (cookie.getName().equalsIgnoreCase("access_token")) {
token = cookie.getValue();
String authorizationHeader = request.getHeader("Authorization");
Cookie[] cookies = request.getCookies();
String accessToken = null;
if (authorizationHeader == null && cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equalsIgnoreCase(ACCESS_TOKEN_COOKIE_NAME)) {
accessToken = cookie.getValue();
break;
}
}
}
if (StringUtils.isNotBlank(token)) {
if (authorizationHeader == null && StringUtils.isNotBlank(accessToken)) {
LOG.debug("Using access token from cookie!");
// Wrap the request
CustomHeadersRequest customHeadersRequest = new CustomHeadersRequest(request);
customHeadersRequest.addHeader("Authorization", "Bearer " + token);
customHeadersRequest.addHeader("Authorization", "Bearer " + accessToken);
filterChain.doFilter(customHeadersRequest, response);
return;
}
// Register the "access_token" cookie if Authorization is provided, but cookie is missing or not matching
if (request.getHeader("Authorization") != null) {
Optional<Cookie> tokenCookie = Optional.empty();
if (cookies != null) {
tokenCookie = Arrays.stream(cookies).filter(cookie -> cookie.getName().equals(ACCESS_TOKEN_COOKIE_NAME)).findFirst();
}
accessToken = authorizationHeader.substring(7); // Remove "Bearer "
if (!tokenCookie.isPresent() || tokenCookie.get().getValue().equals(accessToken)) {
Cookie cookie = new Cookie(ACCESS_TOKEN_COOKIE_NAME, accessToken);
cookie.setComment("Genesys API access token");
cookie.setHttpOnly(true);
cookie.setSecure(cookieSecure);
if (StringUtils.isNotBlank(cookieDomain)) {
cookie.setDomain(cookieDomain);
}
// Only set cookie for /api
cookie.setPath("/api");
LOG.info("Registering API cookie '{}' on {}{}", cookie.getName(), cookie.getDomain(), cookie.getPath());
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
......
......@@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit;
import org.genesys.blocks.oauth.service.OAuthClientDetailsService;
import org.genesys.blocks.oauth.service.OAuthServiceImpl;
import org.genesys.blocks.security.component.OAuthClientOriginCheckFilter;
import org.genesys2.server.servlet.filter.AccessTokenInCookieFilter;
import org.genesys2.server.servlet.filter.ApiAccessLoggerFilter;
import org.genesys2.spring.CachedInMemoryAuthorizationCodeServices;
import org.slf4j.Logger;
......@@ -150,7 +151,12 @@ public class OAuth2ServerConfig {
public ApiAccessLoggerFilter apiAccessLoggerFilter() {
return new ApiAccessLoggerFilter();
}
@Bean
public AccessTokenInCookieFilter accessTokenInCookieFilter() {
return new AccessTokenInCookieFilter();
}
@Override
public void configure(final ResourceServerSecurityConfigurer resources) {
final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
......@@ -201,10 +207,10 @@ public class OAuth2ServerConfig {
;
/*@formatter:on*/
http.addFilterBefore(accessTokenInCookieFilter(), AbstractPreAuthenticatedProcessingFilter.class);
http.addFilterAfter(clientOriginCheckFilter(), AbstractPreAuthenticatedProcessingFilter.class);
http.addFilterAfter(apiAccessLoggerFilter(), SwitchUserFilter.class);
}
}
@Configuration
......@@ -315,7 +321,7 @@ public class OAuth2ServerConfig {
return result;
}
};
config.setAllowCredentials(false);
config.setAllowCredentials(true);
config.setAllowedMethods(Collections.singletonList(HttpMethod.POST.name()));
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
......
......@@ -26,13 +26,11 @@ import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.SessionTrackingMode;
import org.genesys2.server.servlet.filter.AccessTokenInCookieFilter;
import org.genesys2.server.servlet.filter.LocaleURLFilter;
import org.genesys2.server.servlet.filter.SuppressRequestRejectedExceptionFilter;
import org.sitemesh.builder.SiteMeshFilterBuilder;
import org.sitemesh.config.ConfigurableSiteMeshFilter;
import org.sitemesh.webapp.contentfilter.BasicSelector;
import org.springframework.context.annotation.Bean;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.CommonsRequestLoggingFilter;
......@@ -93,8 +91,6 @@ public class WebInitializer extends AbstractAnnotationConfigDispatcherServletIni
servletContext.addListener(new SessionListener());
}
@Bean
public CommonsRequestLoggingFilter requestLoggingFilter() {
CommonsRequestLoggingFilter loggingFilter = new CommonsRequestLoggingFilter();
loggingFilter.setIncludeClientInfo(true);
......@@ -137,10 +133,6 @@ public class WebInitializer extends AbstractAnnotationConfigDispatcherServletIni
localeURLFilter.setInitParameter("excludePaths", "/html,/login-attempt");
localeURLFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST, DispatcherType.INCLUDE, DispatcherType.FORWARD), false, "/*");
// Convert the cookie to the Authorization HTTP request header
final FilterRegistration.Dynamic accessTokenInCookieFilter = servletContext.addFilter("accessTokenInCookieFilter", AccessTokenInCookieFilter.class);
accessTokenInCookieFilter.addMappingForUrlPatterns(null, false, "/api/*");
// Then the spring security
final DelegatingFilterProxy filter = new DelegatingFilterProxy("springSecurityFilterChain");
filter.setContextAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment