mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-25 13:32:30 +00:00
SEC-1750: Make sure RunAs replacement is constrained to the SecurityContext of the current thread.
This commit is contained in:
parent
22b7c9b905
commit
76dc21469e
@ -26,8 +26,10 @@ import org.springframework.security.ConfigAttribute;
|
|||||||
import org.springframework.security.ConfigAttributeDefinition;
|
import org.springframework.security.ConfigAttributeDefinition;
|
||||||
import org.springframework.security.RunAsManager;
|
import org.springframework.security.RunAsManager;
|
||||||
|
|
||||||
|
import org.springframework.security.context.SecurityContext;
|
||||||
import org.springframework.security.context.SecurityContextHolder;
|
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.AuthenticationCredentialsNotFoundEvent;
|
||||||
import org.springframework.security.event.authorization.AuthorizationFailureEvent;
|
import org.springframework.security.event.authorization.AuthorizationFailureEvent;
|
||||||
import org.springframework.security.event.authorization.AuthorizedEvent;
|
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);
|
protected static final Log logger = LogFactory.getLog(AbstractSecurityInterceptor.class);
|
||||||
|
|
||||||
//~ Instance fields ================================================================================================
|
//~ Instance fields ================================================================================================
|
||||||
|
|
||||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||||
private ApplicationEventPublisher eventPublisher;
|
private ApplicationEventPublisher eventPublisher;
|
||||||
private AccessDecisionManager accessDecisionManager;
|
private AccessDecisionManager accessDecisionManager;
|
||||||
@ -140,21 +142,22 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean, A
|
|||||||
|
|
||||||
if (token.isContextHolderRefreshRequired()) {
|
if (token.isContextHolderRefreshRequired()) {
|
||||||
if (logger.isDebugEnabled()) {
|
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) {
|
if (afterInvocationManager != null) {
|
||||||
// Attempt after invocation handling
|
// Attempt after invocation handling
|
||||||
try {
|
try {
|
||||||
returnedObject = afterInvocationManager.decide(token.getAuthentication(), token.getSecureObject(),
|
returnedObject = afterInvocationManager.decide(token.getSecurityContext().getAuthentication(),
|
||||||
|
token.getSecureObject(),
|
||||||
token.getAttr(), returnedObject);
|
token.getAttr(), returnedObject);
|
||||||
}
|
}
|
||||||
catch (AccessDeniedException accessDeniedException) {
|
catch (AccessDeniedException accessDeniedException) {
|
||||||
AuthorizationFailureEvent event = new AuthorizationFailureEvent(token.getSecureObject(), token
|
AuthorizationFailureEvent event = new AuthorizationFailureEvent(token.getSecureObject(), token
|
||||||
.getAttr(), token.getAuthentication(), accessDeniedException);
|
.getAttr(), token.getSecurityContext().getAuthentication(), accessDeniedException);
|
||||||
publishEvent(event);
|
publishEvent(event);
|
||||||
|
|
||||||
throw accessDeniedException;
|
throw accessDeniedException;
|
||||||
@ -285,16 +288,20 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean, A
|
|||||||
}
|
}
|
||||||
|
|
||||||
// no further work post-invocation
|
// no further work post-invocation
|
||||||
return new InterceptorStatusToken(authenticated, false, attr, object);
|
return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attr, object);
|
||||||
} else {
|
} else {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Switching to RunAs Authentication: " + runAs);
|
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
|
SecurityContextHolder.setContext(runAsContext);
|
||||||
return new InterceptorStatusToken(authenticated, true, attr, object);
|
|
||||||
|
// revert to original context post-invocation
|
||||||
|
return new InterceptorStatusToken(originalContext, true, attr, object);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ package org.springframework.security.intercept;
|
|||||||
|
|
||||||
import org.springframework.security.Authentication;
|
import org.springframework.security.Authentication;
|
||||||
import org.springframework.security.ConfigAttributeDefinition;
|
import org.springframework.security.ConfigAttributeDefinition;
|
||||||
|
import org.springframework.security.context.SecurityContext;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,16 +33,16 @@ import org.springframework.security.ConfigAttributeDefinition;
|
|||||||
public class InterceptorStatusToken {
|
public class InterceptorStatusToken {
|
||||||
//~ Instance fields ================================================================================================
|
//~ Instance fields ================================================================================================
|
||||||
|
|
||||||
private Authentication authentication;
|
private SecurityContext context;
|
||||||
private ConfigAttributeDefinition attr;
|
private ConfigAttributeDefinition attr;
|
||||||
private Object secureObject;
|
private Object secureObject;
|
||||||
private boolean contextHolderRefreshRequired;
|
private boolean contextHolderRefreshRequired;
|
||||||
|
|
||||||
//~ Constructors ===================================================================================================
|
//~ Constructors ===================================================================================================
|
||||||
|
|
||||||
public InterceptorStatusToken(Authentication authentication, boolean contextHolderRefreshRequired,
|
public InterceptorStatusToken(SecurityContext context, boolean contextHolderRefreshRequired,
|
||||||
ConfigAttributeDefinition attr, Object secureObject) {
|
ConfigAttributeDefinition attr, Object secureObject) {
|
||||||
this.authentication = authentication;
|
this.context = context;
|
||||||
this.contextHolderRefreshRequired = contextHolderRefreshRequired;
|
this.contextHolderRefreshRequired = contextHolderRefreshRequired;
|
||||||
this.attr = attr;
|
this.attr = attr;
|
||||||
this.secureObject = secureObject;
|
this.secureObject = secureObject;
|
||||||
@ -53,8 +54,8 @@ public class InterceptorStatusToken {
|
|||||||
return attr;
|
return attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Authentication getAuthentication() {
|
public SecurityContext getSecurityContext() {
|
||||||
return authentication;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object getSecureObject() {
|
public Object getSecureObject() {
|
||||||
|
@ -18,6 +18,8 @@ package org.springframework.security.intercept;
|
|||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
import org.springframework.security.ConfigAttributeDefinition;
|
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.providers.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.util.SimpleMethodInvocation;
|
import org.springframework.security.util.SimpleMethodInvocation;
|
||||||
|
|
||||||
@ -58,12 +60,13 @@ public class InterceptorStatusTokenTests extends TestCase {
|
|||||||
ConfigAttributeDefinition attr = new ConfigAttributeDefinition("FOO");
|
ConfigAttributeDefinition attr = new ConfigAttributeDefinition("FOO");
|
||||||
MethodInvocation mi = new SimpleMethodInvocation();
|
MethodInvocation mi = new SimpleMethodInvocation();
|
||||||
|
|
||||||
InterceptorStatusToken token = new InterceptorStatusToken(new UsernamePasswordAuthenticationToken("rod",
|
SecurityContext ctx = new SecurityContextImpl();
|
||||||
"koala"), true, attr, mi);
|
|
||||||
|
InterceptorStatusToken token = new InterceptorStatusToken(ctx, true, attr, mi);
|
||||||
|
|
||||||
assertTrue(token.isContextHolderRefreshRequired());
|
assertTrue(token.isContextHolderRefreshRequired());
|
||||||
assertEquals(attr, token.getAttr());
|
assertEquals(attr, token.getAttr());
|
||||||
assertEquals(mi, token.getSecureObject());
|
assertEquals(mi, token.getSecureObject());
|
||||||
assertEquals("rod", token.getAuthentication().getPrincipal());
|
assertSame(ctx, token.getSecurityContext());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ import org.springframework.security.MockAuthenticationManager;
|
|||||||
import org.springframework.security.MockRunAsManager;
|
import org.springframework.security.MockRunAsManager;
|
||||||
import org.springframework.security.RunAsManager;
|
import org.springframework.security.RunAsManager;
|
||||||
|
|
||||||
|
import org.springframework.security.context.SecurityContext;
|
||||||
import org.springframework.security.context.SecurityContextHolder;
|
import org.springframework.security.context.SecurityContextHolder;
|
||||||
|
|
||||||
import org.springframework.security.intercept.method.MethodDefinitionSource;
|
import org.springframework.security.intercept.method.MethodDefinitionSource;
|
||||||
@ -166,11 +167,15 @@ public class MethodSecurityInterceptorTests extends TestCase {
|
|||||||
public void testMethodCallWithRunAsReplacement() throws Exception {
|
public void testMethodCallWithRunAsReplacement() throws Exception {
|
||||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test", "Password",
|
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test", "Password",
|
||||||
new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_UPPER")});
|
new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_UPPER")});
|
||||||
SecurityContextHolder.getContext().setAuthentication(token);
|
SecurityContext ctx = SecurityContextHolder.getContext();
|
||||||
|
ctx.setAuthentication(token);
|
||||||
|
|
||||||
ITargetObject target = makeInterceptedTarget();
|
ITargetObject target = makeInterceptedTarget();
|
||||||
String result = target.makeUpperCase("hello");
|
String result = target.makeUpperCase("hello");
|
||||||
assertEquals("HELLO org.springframework.security.MockRunAsAuthenticationToken true", result);
|
assertEquals("HELLO org.springframework.security.MockRunAsAuthenticationToken true", result);
|
||||||
|
// Check reset afterwards
|
||||||
|
assertSame(ctx, SecurityContextHolder.getContext());
|
||||||
|
assertSame(token, SecurityContextHolder.getContext().getAuthentication());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMethodCallWithoutRunAsReplacement()
|
public void testMethodCallWithoutRunAsReplacement()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user