SEC-1195: Change <http> parsing behaviour to use an internal AuthenticationManager instance. Implemented "parent" AuthenticationManager in ProviderManager which is delegated to when no authentication is returned by the instances list of authentication providers. Extracted the Authentication success/failure publishing into a separate strategy.

This commit is contained in:
Luke Taylor
2009-07-15 01:28:28 +00:00
parent 1ca2e6e6fc
commit 6346e31517
9 changed files with 391 additions and 275 deletions

View File

@@ -1,32 +0,0 @@
package org.springframework.security;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEvent;
/**
* @author Luke Taylor
* @version $Id$
*/
public class MockApplicationEventPublisher implements ApplicationEventPublisher {
private Boolean expectedEvent;
private ApplicationEvent lastEvent;
public MockApplicationEventPublisher() {
}
public MockApplicationEventPublisher(boolean expectedEvent) {
this.expectedEvent = Boolean.valueOf(expectedEvent);
}
public void publishEvent(ApplicationEvent event) {
if (expectedEvent != null && !expectedEvent.booleanValue()) {
throw new IllegalStateException("The ApplicationEventPublisher did not expect to receive this event");
}
lastEvent = event;
}
public ApplicationEvent getLastEvent() {
return lastEvent;
}
}

View File

@@ -23,7 +23,7 @@ import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.springframework.security.MockApplicationEventPublisher;
import org.springframework.context.MessageSource;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.ProviderNotFoundException;
@@ -31,7 +31,6 @@ import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.concurrent.ConcurrentLoginException;
import org.springframework.security.authentication.concurrent.ConcurrentSessionController;
import org.springframework.security.authentication.concurrent.NullConcurrentSessionController;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
@@ -43,6 +42,7 @@ import org.springframework.security.core.authority.AuthorityUtils;
* @author Ben Alex
* @version $Id$
*/
@SuppressWarnings("unchecked")
public class ProviderManagerTests {
@Test(expected=ProviderNotFoundException.class)
@@ -51,46 +51,34 @@ public class ProviderManagerTests {
AuthorityUtils.createAuthorityList("ROLE_ONE", "ROLE_TWO"));
ProviderManager mgr = makeProviderManager();
mgr.setApplicationEventPublisher(new MockApplicationEventPublisher(true));
mgr.setMessageSource(mock(MessageSource.class));
mgr.authenticate(token);
}
@Test
public void authenticationSucceedsWithSupportedTokenAndReturnsExpectedObject() throws Exception {
TestingAuthenticationToken token = new TestingAuthenticationToken("Test", "Password","ROLE_ONE","ROLE_TWO");
final Authentication a = mock(Authentication.class);
ProviderManager mgr = new ProviderManager();
AuthenticationEventPublisher publisher = mock(AuthenticationEventPublisher.class);
mgr.setAuthenticationEventPublisher(publisher);
mgr.setProviders(Arrays.asList(createProviderWhichReturns(a)));
ProviderManager mgr = makeProviderManager();
mgr.setApplicationEventPublisher(new MockApplicationEventPublisher(true));
Authentication result = mgr.authenticate(token);
if (!(result instanceof TestingAuthenticationToken)) {
fail("Should have returned instance of TestingAuthenticationToken");
}
TestingAuthenticationToken castResult = (TestingAuthenticationToken) result;
assertEquals("Test", castResult.getPrincipal());
assertEquals("Password", castResult.getCredentials());
assertEquals(AuthorityUtils.createAuthorityList("ROLE_ONE","ROLE_TWO"), castResult.getAuthorities());
Authentication result = mgr.authenticate(a);
assertEquals(a, result);
verify(publisher).publishAuthenticationSuccess(result);
}
@Test
public void authenticationSuccessWhenFirstProviderReturnsNullButSecondAuthenticates() {
TestingAuthenticationToken token = new TestingAuthenticationToken("Test", "Password","ROLE_ONE","ROLE_TWO");
ProviderManager mgr = makeProviderManagerWithMockProviderWhichReturnsNullInList();
mgr.setApplicationEventPublisher(new MockApplicationEventPublisher(true));
public void authenticationSucceedsWhenFirstProviderReturnsNullButSecondAuthenticates() {
final Authentication a = mock(Authentication.class);
ProviderManager mgr = new ProviderManager();
AuthenticationEventPublisher publisher = mock(AuthenticationEventPublisher.class);
mgr.setAuthenticationEventPublisher(publisher);
mgr.setProviders(Arrays.asList(createProviderWhichReturns(null), createProviderWhichReturns(a)));
Authentication result = mgr.authenticate(token);
if (!(result instanceof TestingAuthenticationToken)) {
fail("Should have returned instance of TestingAuthenticationToken");
}
TestingAuthenticationToken castResult = (TestingAuthenticationToken) result;
assertEquals("Test", castResult.getPrincipal());
assertEquals("Password", castResult.getCredentials());
assertEquals("ROLE_ONE", castResult.getAuthorities().get(0).getAuthority());
assertEquals("ROLE_TWO", castResult.getAuthorities().get(1).getAuthority());
Authentication result = mgr.authenticate(a);
assertSame(a, result);
verify(publisher).publishAuthenticationSuccess(result);
}
@Test
@@ -99,11 +87,10 @@ public class ProviderManagerTests {
//The NullConcurrentSessionController should be the default
assertNotNull(target.getSessionController());
assertTrue(target.getSessionController() instanceof NullConcurrentSessionController);
ConcurrentSessionController csc = mock(ConcurrentSessionController.class);
target.setSessionController(csc);
assertEquals(csc, target.getSessionController());
assertSame(csc, target.getSessionController());
}
@Test(expected=IllegalArgumentException.class)
@@ -119,8 +106,6 @@ public class ProviderManagerTests {
@Test(expected=IllegalArgumentException.class)
public void getProvidersFailsIfProviderListNotSet() throws Exception {
ProviderManager mgr = new ProviderManager();
mgr.afterPropertiesSet();
mgr.getProviders();
}
@@ -137,6 +122,7 @@ public class ProviderManagerTests {
final Object resultDetails = "(Result Details)";
ProviderManager authMgr = makeProviderManager();
// A provider which sets the details object
AuthenticationProvider provider = new AuthenticationProvider() {
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
((TestingAuthenticationToken)authentication).setDetails(resultDetails);
@@ -166,25 +152,53 @@ public class ProviderManagerTests {
request.setDetails(details);
Authentication result = authMgr.authenticate(request);
assertEquals(details, result.getDetails());
assertSame(details, result.getDetails());
}
@Test
public void authenticationExceptionIsIgnoredIfLaterProviderAuthenticates() throws Exception {
ProviderManager mgr = new ProviderManager();
final Authentication authReq = mock(Authentication.class);
mgr.setProviders(Arrays.asList(createProviderWhichThrows(new BadCredentialsException("")),
createProviderWhichReturns(authReq)));
assertSame(authReq, mgr.authenticate(mock(Authentication.class)));
}
@Test
public void authenticationExceptionIsRethrownIfNoLaterProviderAuthenticates() throws Exception {
ProviderManager mgr = new ProviderManager();
mgr.setProviders(Arrays.asList(createProviderWhichThrows(new BadCredentialsException("")),
createProviderWhichReturns(null)));
try {
mgr.authenticate(mock(Authentication.class));
fail("Expected BadCredentialsException");
} catch (BadCredentialsException expected) {
}
}
// SEC-546
@Test(expected=AccountStatusException.class)
@Test
public void accountStatusExceptionPreventsCallsToSubsequentProviders() throws Exception {
ProviderManager authMgr = makeProviderManager();
AuthenticationProvider iThrowAccountStatusException = createProviderWhichThrows(new AccountStatusException(""){});
AuthenticationProvider otherProvider = mock(AuthenticationProvider.class);
authMgr.setProviders(Arrays.asList(new MockProviderWhichThrowsAccountStatusException(),
new MockProviderWhichThrowsConcurrentLoginException()) );
authMgr.setProviders(Arrays.asList(iThrowAccountStatusException, otherProvider));
authMgr.authenticate(createAuthenticationToken());
try {
authMgr.authenticate(mock(Authentication.class));
fail("Expected AccountStatusException");
} catch (AccountStatusException expected) {
}
verifyZeroInteractions(otherProvider);
}
@Test(expected=ConcurrentLoginException.class)
public void concurrentLoginExceptionPreventsCallsToSubsequentProviders() throws Exception {
ProviderManager authMgr = makeProviderManager();
// Two providers so if the second is polled it will throw an AccountStatusException
authMgr.setProviders(Arrays.asList(new MockProvider(), new MockProviderWhichThrowsAccountStatusException()) );
// Two providers so if the second is polled it will throw an BadCredentialsException
authMgr.setProviders(Arrays.asList(new MockProvider(), createProviderWhichThrows(new BadCredentialsException(""))) );
TestingAuthenticationToken request = createAuthenticationToken();
ConcurrentSessionController ctrlr = mock(ConcurrentSessionController.class);
doThrow(new ConcurrentLoginException("mocked")).when(ctrlr).checkAuthenticationAllowed(request);
@@ -193,6 +207,88 @@ public class ProviderManagerTests {
authMgr.authenticate(request);
}
@Test
public void parentAuthenticationIsUsedIfProvidersDontAuthenticate() throws Exception {
ProviderManager mgr = new ProviderManager();
mgr.setProviders(Arrays.asList(mock(AuthenticationProvider.class)));
Authentication authReq = mock(Authentication.class);
AuthenticationManager parent = mock(AuthenticationManager.class);
when(parent.authenticate(authReq)).thenReturn(authReq);
mgr.setParent(parent);
assertSame(authReq, mgr.authenticate(authReq));
}
@Test
public void parentIsNotCalledIfAccountStatusExceptionIsThrown() throws Exception {
ProviderManager mgr = new ProviderManager();
AuthenticationProvider iThrowAccountStatusException = createProviderWhichThrows(new AccountStatusException(""){});
mgr.setProviders(Arrays.asList(iThrowAccountStatusException));
AuthenticationManager parent = mock(AuthenticationManager.class);
mgr.setParent(parent);
try {
mgr.authenticate(mock(Authentication.class));
fail("Expected exception");
} catch (AccountStatusException expected) {
}
verifyZeroInteractions(parent);
}
@Test
public void providerNotFoundFromParentIsIgnored() throws Exception {
ProviderManager mgr = new ProviderManager();
final Authentication authReq = mock(Authentication.class);
AuthenticationEventPublisher publisher = mock(AuthenticationEventPublisher.class);
mgr.setAuthenticationEventPublisher(publisher);
// Set a provider that throws an exception - this is the exception we expect to be propagated
mgr.setProviders(Arrays.asList(createProviderWhichThrows(new BadCredentialsException(""))));
AuthenticationManager parent = mock(AuthenticationManager.class);
when(parent.authenticate(authReq)).thenThrow(new ProviderNotFoundException(""));
mgr.setParent(parent);
try {
mgr.authenticate(authReq);
fail("Expected exception");
} catch (BadCredentialsException expected) {
verify(publisher).publishAuthenticationFailure(expected, authReq);
}
}
@Test
public void authenticationExceptionFromParentOverridesPreviousOnes() throws Exception {
ProviderManager mgr = new ProviderManager();
final Authentication authReq = mock(Authentication.class);
AuthenticationEventPublisher publisher = mock(AuthenticationEventPublisher.class);
mgr.setAuthenticationEventPublisher(publisher);
// Set a provider that throws an exception - this is the exception we expect to be propagated
final BadCredentialsException expected = new BadCredentialsException("I'm the one from the parent");
mgr.setProviders(Arrays.asList(createProviderWhichThrows(new BadCredentialsException(""))));
AuthenticationManager parent = mock(AuthenticationManager.class);
when(parent.authenticate(authReq)).thenThrow(expected);
mgr.setParent(parent);
try {
mgr.authenticate(authReq);
fail("Expected exception");
} catch (BadCredentialsException e) {
assertSame(expected, e);
}
verify(publisher).publishAuthenticationFailure(expected, authReq);
}
private AuthenticationProvider createProviderWhichThrows(final AuthenticationException e) {
AuthenticationProvider provider = mock(AuthenticationProvider.class);
when(provider.supports(any(Class.class))).thenReturn(true);
when(provider.authenticate(any(Authentication.class))).thenThrow(e);
return provider;
}
private AuthenticationProvider createProviderWhichReturns(final Authentication a) {
AuthenticationProvider provider = mock(AuthenticationProvider.class);
when(provider.supports(any(Class.class))).thenReturn(true);
when(provider.authenticate(any(Authentication.class))).thenReturn(a);
return provider;
}
private TestingAuthenticationToken createAuthenticationToken() {
return new TestingAuthenticationToken("name", "password", new ArrayList<GrantedAuthority>(0));
}
@@ -205,21 +301,6 @@ public class ProviderManagerTests {
ProviderManager mgr = new ProviderManager();
mgr.setProviders(providers);
mgr.afterPropertiesSet();
return mgr;
}
private ProviderManager makeProviderManagerWithMockProviderWhichReturnsNullInList() {
MockProviderWhichReturnsNull provider1 = new MockProviderWhichReturnsNull();
MockProvider provider2 = new MockProvider();
List<Object> providers = new ArrayList<Object>();
providers.add(provider1);
providers.add(provider2);
ProviderManager mgr = new ProviderManager();
mgr.setProviders(providers);
return mgr;
}
@@ -242,43 +323,4 @@ public class ProviderManagerTests {
}
}
}
private class MockProviderWhichReturnsNull implements AuthenticationProvider {
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (supports(authentication.getClass())) {
return null;
} else {
throw new AuthenticationServiceException("Don't support this class");
}
}
public boolean supports(Class<? extends Object> authentication) {
if (TestingAuthenticationToken.class.isAssignableFrom(authentication)) {
return true;
} else {
return false;
}
}
}
private class MockProviderWhichThrowsAccountStatusException implements AuthenticationProvider {
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
throw new AccountStatusException("xxx") {};
}
public boolean supports(Class<? extends Object> authentication) {
return true;
}
}
private class MockProviderWhichThrowsConcurrentLoginException implements AuthenticationProvider {
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
throw new ConcurrentLoginException("xxx") {};
}
public boolean supports(Class<? extends Object> authentication) {
return true;
}
}
}

View File

@@ -0,0 +1,34 @@
package org.springframework.security.util;
import static org.junit.Assert.*;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.Test;
/**
*
* @author Luke Taylor
* @version $Id$
*/
public class MethodInvocationUtilsTests {
@Test
public void createFromClassReturnsMethodWithNoArgInfoForMethodWithNoArgs() {
MethodInvocation mi = MethodInvocationUtils.createFromClass(String.class, "length");
assertNotNull(mi);
}
@Test
public void createFromClassWithNoArgInfoReturnsNullForMethodWithArgs() {
MethodInvocation mi = MethodInvocationUtils.createFromClass(String.class, "codePointAt");
assertNull(mi);
}
@Test
public void createFromClassReturnsMethodIfGivArgInfoForMethodWithArgs() {
MethodInvocation mi = MethodInvocationUtils.createFromClass(null, String.class, "compareTo",
new Class<?>[]{String.class}, new Object[] {""});
assertNotNull(mi);
}
}