Browse Source

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

2.0.x
Luke Taylor 15 years ago
parent
commit
76dc21469e
  1. 25
      core/src/main/java/org/springframework/security/intercept/AbstractSecurityInterceptor.java
  2. 11
      core/src/main/java/org/springframework/security/intercept/InterceptorStatusToken.java
  3. 9
      core/src/test/java/org/springframework/security/intercept/InterceptorStatusTokenTests.java
  4. 7
      core/src/test/java/org/springframework/security/intercept/method/aopalliance/MethodSecurityInterceptorTests.java

25
core/src/main/java/org/springframework/security/intercept/AbstractSecurityInterceptor.java

@ -26,8 +26,10 @@ import org.springframework.security.ConfigAttribute; @@ -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 @@ -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 @@ -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 @@ -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);
SecurityContextHolder.setContext(runAsContext);
// revert to token.Authenticated post-invocation
return new InterceptorStatusToken(authenticated, true, attr, object);
// revert to original context post-invocation
return new InterceptorStatusToken(originalContext, true, attr, object);
}
}

11
core/src/main/java/org/springframework/security/intercept/InterceptorStatusToken.java

@ -17,6 +17,7 @@ package org.springframework.security.intercept; @@ -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; @@ -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 { @@ -53,8 +54,8 @@ public class InterceptorStatusToken {
return attr;
}
public Authentication getAuthentication() {
return authentication;
public SecurityContext getSecurityContext() {
return context;
}
public Object getSecureObject() {

9
core/src/test/java/org/springframework/security/intercept/InterceptorStatusTokenTests.java

@ -18,6 +18,8 @@ package org.springframework.security.intercept; @@ -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 { @@ -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());
}
}

7
core/src/test/java/org/springframework/security/intercept/method/aopalliance/MethodSecurityInterceptorTests.java

@ -34,6 +34,7 @@ import org.springframework.security.MockAuthenticationManager; @@ -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 { @@ -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()

Loading…
Cancel
Save