SEC-1750: Make sure RunAs replacement is constrained to the SecurityContext of the current thread.

This commit is contained in:
Luke Taylor 2011-05-20 20:42:54 +01:00
parent 22b7c9b905
commit 76dc21469e
4 changed files with 34 additions and 18 deletions

View File

@ -26,8 +26,10 @@ import org.springframework.security.ConfigAttribute;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.RunAsManager;
import org.springframework.security.context.SecurityContext;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.context.SecurityContextImpl;
import org.springframework.security.event.authorization.AuthenticationCredentialsNotFoundEvent;
import org.springframework.security.event.authorization.AuthorizationFailureEvent;
import org.springframework.security.event.authorization.AuthorizedEvent;
@ -110,7 +112,7 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean, A
protected static final Log logger = LogFactory.getLog(AbstractSecurityInterceptor.class);
//~ Instance fields ================================================================================================
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private ApplicationEventPublisher eventPublisher;
private AccessDecisionManager accessDecisionManager;
@ -140,21 +142,22 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean, A
if (token.isContextHolderRefreshRequired()) {
if (logger.isDebugEnabled()) {
logger.debug("Reverting to original Authentication: " + token.getAuthentication().toString());
logger.debug("Reverting to original Authentication: " + token.getSecurityContext().getAuthentication());
}
SecurityContextHolder.getContext().setAuthentication(token.getAuthentication());
SecurityContextHolder.setContext(token.getSecurityContext());
}
if (afterInvocationManager != null) {
// Attempt after invocation handling
try {
returnedObject = afterInvocationManager.decide(token.getAuthentication(), token.getSecureObject(),
returnedObject = afterInvocationManager.decide(token.getSecurityContext().getAuthentication(),
token.getSecureObject(),
token.getAttr(), returnedObject);
}
catch (AccessDeniedException accessDeniedException) {
AuthorizationFailureEvent event = new AuthorizationFailureEvent(token.getSecureObject(), token
.getAttr(), token.getAuthentication(), accessDeniedException);
.getAttr(), token.getSecurityContext().getAuthentication(), accessDeniedException);
publishEvent(event);
throw accessDeniedException;
@ -285,16 +288,20 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean, A
}
// no further work post-invocation
return new InterceptorStatusToken(authenticated, false, attr, object);
return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attr, object);
} else {
if (logger.isDebugEnabled()) {
logger.debug("Switching to RunAs Authentication: " + runAs);
}
SecurityContextHolder.getContext().setAuthentication(runAs);
SecurityContext originalContext = SecurityContextHolder.getContext();
SecurityContext runAsContext = new SecurityContextImpl();
runAsContext.setAuthentication(runAs);
// revert to token.Authenticated post-invocation
return new InterceptorStatusToken(authenticated, true, attr, object);
SecurityContextHolder.setContext(runAsContext);
// revert to original context post-invocation
return new InterceptorStatusToken(originalContext, true, attr, object);
}
}

View File

@ -17,6 +17,7 @@ package org.springframework.security.intercept;
import org.springframework.security.Authentication;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.context.SecurityContext;
/**
@ -32,16 +33,16 @@ import org.springframework.security.ConfigAttributeDefinition;
public class InterceptorStatusToken {
//~ Instance fields ================================================================================================
private Authentication authentication;
private SecurityContext context;
private ConfigAttributeDefinition attr;
private Object secureObject;
private boolean contextHolderRefreshRequired;
//~ Constructors ===================================================================================================
public InterceptorStatusToken(Authentication authentication, boolean contextHolderRefreshRequired,
public InterceptorStatusToken(SecurityContext context, boolean contextHolderRefreshRequired,
ConfigAttributeDefinition attr, Object secureObject) {
this.authentication = authentication;
this.context = context;
this.contextHolderRefreshRequired = contextHolderRefreshRequired;
this.attr = attr;
this.secureObject = secureObject;
@ -53,8 +54,8 @@ public class InterceptorStatusToken {
return attr;
}
public Authentication getAuthentication() {
return authentication;
public SecurityContext getSecurityContext() {
return context;
}
public Object getSecureObject() {

View File

@ -18,6 +18,8 @@ package org.springframework.security.intercept;
import junit.framework.TestCase;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.context.SecurityContext;
import org.springframework.security.context.SecurityContextImpl;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.security.util.SimpleMethodInvocation;
@ -58,12 +60,13 @@ public class InterceptorStatusTokenTests extends TestCase {
ConfigAttributeDefinition attr = new ConfigAttributeDefinition("FOO");
MethodInvocation mi = new SimpleMethodInvocation();
InterceptorStatusToken token = new InterceptorStatusToken(new UsernamePasswordAuthenticationToken("rod",
"koala"), true, attr, mi);
SecurityContext ctx = new SecurityContextImpl();
InterceptorStatusToken token = new InterceptorStatusToken(ctx, true, attr, mi);
assertTrue(token.isContextHolderRefreshRequired());
assertEquals(attr, token.getAttr());
assertEquals(mi, token.getSecureObject());
assertEquals("rod", token.getAuthentication().getPrincipal());
assertSame(ctx, token.getSecurityContext());
}
}

View File

@ -34,6 +34,7 @@ import org.springframework.security.MockAuthenticationManager;
import org.springframework.security.MockRunAsManager;
import org.springframework.security.RunAsManager;
import org.springframework.security.context.SecurityContext;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.intercept.method.MethodDefinitionSource;
@ -166,11 +167,15 @@ public class MethodSecurityInterceptorTests extends TestCase {
public void testMethodCallWithRunAsReplacement() throws Exception {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test", "Password",
new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_UPPER")});
SecurityContextHolder.getContext().setAuthentication(token);
SecurityContext ctx = SecurityContextHolder.getContext();
ctx.setAuthentication(token);
ITargetObject target = makeInterceptedTarget();
String result = target.makeUpperCase("hello");
assertEquals("HELLO org.springframework.security.MockRunAsAuthenticationToken true", result);
// Check reset afterwards
assertSame(ctx, SecurityContextHolder.getContext());
assertSame(token, SecurityContextHolder.getContext().getAuthentication());
}
public void testMethodCallWithoutRunAsReplacement()