package org.molgenis.security;
import org.molgenis.security.core.runas.SystemSecurityToken;
import org.molgenis.security.core.utils.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.Assert;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Collection;
/**
* Based on org.springframework.security.web.authentication.AnonymousAuthenticationFilter:
* <p>
* Detects if there is no {@code Authentication} object in the {@code SecurityContextHolder}, and populates it with one
* if needed.
*/
public class MolgenisAnonymousAuthenticationFilter extends GenericFilterBean implements InitializingBean
{
private static final Logger LOG = LoggerFactory.getLogger(MolgenisAnonymousAuthenticationFilter.class);
// ~ Instance fields
// ================================================================================================
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
private final String key;
private final Object principal;
private final UserDetailsService userDetailsService;
/**
* Creates a filter with a principal named "anonymousUser" and the single authority "ROLE_ANONYMOUS".
*
* @param key the key to identify tokens created by this filter
*/
public MolgenisAnonymousAuthenticationFilter(String key, Object principal, UserDetailsService userDetailsService)
{
this.key = key;
this.principal = principal;
this.userDetailsService = userDetailsService;
}
// ~ Methods
// ========================================================================================================
@Override
public void afterPropertiesSet()
{
Assert.hasLength(key);
Assert.notNull(principal, "Anonymous authentication principal must be set");
Assert.notNull(userDetailsService, "User details service must be set");
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException
{
if (SecurityContextHolder.getContext().getAuthentication() == null)
{
SecurityContextHolder.getContext().setAuthentication(createAuthentication((HttpServletRequest) req));
if (LOG.isDebugEnabled())
{
LOG.debug("Populated SecurityContextHolder with anonymous token: '" + SecurityContextHolder.getContext()
.getAuthentication() + "'");
}
}
else
{
if (LOG.isTraceEnabled())
{
LOG.trace("SecurityContextHolder not populated with anonymous token, as it already contained: '{}'",
SecurityContextHolder.getContext().getAuthentication());
}
}
chain.doFilter(req, res);
}
protected Authentication createAuthentication(HttpServletRequest request)
{
AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(key, principal, getAuthorities());
auth.setDetails(authenticationDetailsSource.buildDetails(request));
return auth;
}
public void setAuthenticationDetailsSource(
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource)
{
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
this.authenticationDetailsSource = authenticationDetailsSource;
}
public Object getPrincipal()
{
return principal;
}
public Collection<? extends GrantedAuthority> getAuthorities()
{
// Remember the original context
SecurityContext origCtx = SecurityContextHolder.getContext();
try
{
// Set a SystemSecurityToken
SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
SecurityContextHolder.getContext().setAuthentication(new SystemSecurityToken());
UserDetails user = userDetailsService.loadUserByUsername(SecurityUtils.ANONYMOUS_USERNAME);
if (user == null)
{
throw new RuntimeException("user with name '" + SecurityUtils.ANONYMOUS_USERNAME + "' does not exist");
}
return user.getAuthorities();
}
finally
{
// Set the original context back when method is finished
SecurityContextHolder.setContext(origCtx);
}
}
}