From 872ace9164a6713ff8947f920fc985389fd33983 Mon Sep 17 00:00:00 2001 From: Ben Alex Date: Sun, 18 Apr 2004 12:03:07 +0000 Subject: [PATCH] Modify contract of AuthenticationProvider to allow AuthenticationProvider implementations to return null if they do not wish to process a given Authentication request, despite asserting they support it. --- .../providers/AuthenticationProvider.java | 24 +++++++-- .../providers/ProviderManager.java | 6 ++- .../providers/ProviderManagerTests.java | 54 +++++++++++++++++++ 3 files changed, 79 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/acegisecurity/providers/AuthenticationProvider.java b/core/src/main/java/org/acegisecurity/providers/AuthenticationProvider.java index 4b5684bdf6..9eec8c15e9 100644 --- a/core/src/main/java/org/acegisecurity/providers/AuthenticationProvider.java +++ b/core/src/main/java/org/acegisecurity/providers/AuthenticationProvider.java @@ -35,7 +35,12 @@ public interface AuthenticationProvider { * * @param authentication the authentication request object. * - * @return a fully authenticated object including credentials. + * @return a fully authenticated object including credentials. May return + * null if the AuthenticationProvider is + * unable to support authentication of the passed + * Authentication object. In such a case, the next + * AuthenticationProvider that supports the presented + * Authentication class will be tried. * * @throws AuthenticationException if authentication fails. */ @@ -43,8 +48,18 @@ public interface AuthenticationProvider { throws AuthenticationException; /** - * Returns true if this AuthenticationProvider supports the - * indicated Authentication object. + * Returns true if this AuthenticationProvider + * supports the indicated Authentication object. + * + *

+ * Returning true does not guarantee an + * AuthenticationProvider will be able to authenticate the + * presented instance of the Authentication class. It simply + * indicates it can support closer evaluation of it. An + * AuthenticationProvider can still return null + * from the {@link #authenticate(Authentication)} method to indicate + * another AuthenticationProvider should be tried. + *

* *

* Selection of an AuthenticationProvider capable of @@ -52,7 +67,8 @@ public interface AuthenticationProvider { * ProviderManager. *

* - * @return DOCUMENT ME! + * @return true if the implementation can more closely + * evaluate the Authentication class presented */ public boolean supports(Class authentication); } diff --git a/core/src/main/java/org/acegisecurity/providers/ProviderManager.java b/core/src/main/java/org/acegisecurity/providers/ProviderManager.java index 290fb80b33..fa21ba0d6f 100644 --- a/core/src/main/java/org/acegisecurity/providers/ProviderManager.java +++ b/core/src/main/java/org/acegisecurity/providers/ProviderManager.java @@ -123,7 +123,11 @@ public class ProviderManager implements InitializingBean, AuthenticationManager logger.debug("Authentication attempt using " + provider.getClass().getName()); - return provider.authenticate(authentication); + Authentication result = provider.authenticate(authentication); + + if (result != null) { + return result; + } } } diff --git a/core/src/test/java/org/acegisecurity/providers/ProviderManagerTests.java b/core/src/test/java/org/acegisecurity/providers/ProviderManagerTests.java index 475f40130e..a8c8a119af 100644 --- a/core/src/test/java/org/acegisecurity/providers/ProviderManagerTests.java +++ b/core/src/test/java/org/acegisecurity/providers/ProviderManagerTests.java @@ -90,6 +90,26 @@ public class ProviderManagerTests extends TestCase { assertEquals("ROLE_TWO", castResult.getAuthorities()[1].getAuthority()); } + public void testAuthenticationSuccessWhenFirstProviderReturnsNullButSecondAuthenticates() { + TestingAuthenticationToken token = new TestingAuthenticationToken("Test", + "Password", + new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl( + "ROLE_TWO")}); + + ProviderManager mgr = makeProviderManagerWithMockProviderWhichReturnsNullInList(); + 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()[0].getAuthority()); + assertEquals("ROLE_TWO", castResult.getAuthorities()[1].getAuthority()); + } + public void testStartupFailsIfProviderListDoesNotContainingProviders() throws Exception { List providers = new Vector(); @@ -146,6 +166,19 @@ public class ProviderManagerTests extends TestCase { return mgr; } + private ProviderManager makeProviderManagerWithMockProviderWhichReturnsNullInList() { + MockProviderWhichReturnsNull provider1 = new MockProviderWhichReturnsNull(); + MockProvider provider2 = new MockProvider(); + List providers = new Vector(); + providers.add(provider1); + providers.add(provider2); + + ProviderManager mgr = new ProviderManager(); + mgr.setProviders(providers); + + return mgr; + } + //~ Inner Classes ========================================================== private class MockProvider implements AuthenticationProvider { @@ -168,4 +201,25 @@ public class ProviderManagerTests extends TestCase { } } } + + 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 authentication) { + if (TestingAuthenticationToken.class.isAssignableFrom( + authentication)) { + return true; + } else { + return false; + } + } + } }