From 169449bf243adfb5724f9112063c2515a6a99125 Mon Sep 17 00:00:00 2001 From: Ray Krueger Date: Sun, 13 Mar 2005 22:26:56 +0000 Subject: [PATCH] In response to: http://forum.springframework.org/viewtopic.php?t=3874 JaasAuthenticationProvider now checks that the java.security.auth.login.config is null before attempting to use it. Also, The loginConfig resource is attempted as a file first as spaces in the path name can cause FileNotFoundExceptions for URLs --- .../jaas/JaasAuthenticationProvider.java | 182 +++++++++--------- .../jaas/JaasAuthenticationProviderTests.java | 84 ++++---- 2 files changed, 124 insertions(+), 142 deletions(-) diff --git a/core/src/main/java/org/acegisecurity/providers/jaas/JaasAuthenticationProvider.java b/core/src/main/java/org/acegisecurity/providers/jaas/JaasAuthenticationProvider.java index 6ab8e0a998..cfc6dd53d5 100644 --- a/core/src/main/java/org/acegisecurity/providers/jaas/JaasAuthenticationProvider.java +++ b/core/src/main/java/org/acegisecurity/providers/jaas/JaasAuthenticationProvider.java @@ -23,47 +23,41 @@ import net.sf.acegisecurity.providers.AuthenticationProvider; import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken; import net.sf.acegisecurity.providers.jaas.event.JaasAuthenticationFailedEvent; import net.sf.acegisecurity.providers.jaas.event.JaasAuthenticationSuccessEvent; - +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; - import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; -import org.springframework.context.ApplicationContextException; - import org.springframework.core.io.Resource; - import org.springframework.util.Assert; -import java.io.IOException; - -import java.security.Principal; -import java.security.Security; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.auth.login.Configuration; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; +import java.io.IOException; +import java.security.Principal; +import java.security.Security; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; /** * An {@link AuthenticationProvider} implementation that retrieves user details * from a JAAS login configuration. - * - *

+ *

+ *

* This AuthenticationProvider is capable of validating {@link * net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken} * requests contain the correct username and password. *

- * - *

+ *

+ *

* This implementation is backed by a JAAS * configuration. The loginConfig property must be set to a given JAAS @@ -72,37 +66,37 @@ import javax.security.auth.login.LoginException; * configuration file containing an index matching the {@link * #setLoginContextName(java.lang.String) loginContextName} property. *

- * - *

+ *

+ *

* For example: If this JaasAuthenticationProvider were configured in a Spring * WebApplicationContext the xml to set the loginConfiguration could be as * follows... *

-<property name="loginConfig">
- <value>/WEB-INF/login.conf</value>
-</property>
-
+ * <property name="loginConfig"> + * <value>/WEB-INF/login.conf</value> + * </property> + * *

- * - *

+ *

+ *

* The loginContextName should coincide with a given index in the loginConfig * specifed. The loginConfig file used in the JUnit tests appears as the * following... *

-JAASTest {
-  net.sf.acegisecurity.providers.jaas.TestLoginModule required;
-};
-
+ * JAASTest { + * net.sf.acegisecurity.providers.jaas.TestLoginModule required; + * }; + * * Using the example login configuration above, the loginContextName property * would be set as JAASTest... *
-<property name="loginContextName">
- <value>JAASTest</value>
-</property>
-
+ * <property name="loginContextName"> + * <value>JAASTest</value> + * </property> + * *

- * - *

+ *

+ *

* When using JAAS login modules as the authentication source, sometimes the LoginContext * will require CallbackHandlers. The JaasAuthenticationProvider uses @@ -113,24 +107,24 @@ JAASTest { * CallbackHandler, control is passed to each {@link * JaasAuthenticationCallbackHandler} for each Callback passed. *

- * - *

+ *

+ *

* {{@link JaasAuthenticationCallbackHandler}s are passed to the * JaasAuthenticationProvider through the {@link * #setCallbackHandlers(net.sf.acegisecurity.providers.jaas.JaasAuthenticationCallbackHandler[]) * callbackHandlers} property. } *

-  <property name="callbackHandlers">
-      <list>
-          <bean class="net.sf.acegisecurity.providers.jaas.TestCallbackHandler"/>
-          <bean class="{@link JaasNameCallbackHandler net.sf.acegisecurity.providers.jaas.JaasNameCallbackHandler}"/>
-          <bean class="{@link JaasPasswordCallbackHandler net.sf.acegisecurity.providers.jaas.JaasPasswordCallbackHandler}"/>
-      </list>
-  </property>
-
+ * <property name="callbackHandlers"> + * <list> + * <bean class="net.sf.acegisecurity.providers.jaas.TestCallbackHandler"/> + * <bean class="{@link JaasNameCallbackHandler net.sf.acegisecurity.providers.jaas.JaasNameCallbackHandler}"/> + * <bean class="{@link JaasPasswordCallbackHandler net.sf.acegisecurity.providers.jaas.JaasPasswordCallbackHandler}"/> + * </list> + * </property> + * *

- * - *

+ *

+ *

* After calling LoginContext.login(), the JaasAuthenticationProvider will * retrieve the returned Principals from the Subject * (LoginContext.getSubject().getPrincipals). Each returned principal is then @@ -141,24 +135,27 @@ JAASTest { * method. The returned role will be applied to the Authorization object as a * {@link GrantedAuthority}. *

- * - *

+ *

+ *

* AuthorityGranters are configured in spring xml as follows... *

-<property name="authorityGranters">
-  <list>
-      <bean class="net.sf.acegisecurity.providers.jaas.TestAuthorityGranter"/>
-  </list>
-</property>
-

-

+ * <property name="authorityGranters"> + * <list> + * <bean class="net.sf.acegisecurity.providers.jaas.TestAuthorityGranter"/> + * </list> + * </property> + *

+ * *

* * @author Ray Krueger * @version $Id$ */ public class JaasAuthenticationProvider implements AuthenticationProvider, - InitializingBean, ApplicationContextAware { + InitializingBean, ApplicationContextAware { + + private static final Log log = LogFactory.getLog(JaasAuthenticationProvider.class); + //~ Instance fields ======================================================== private ApplicationContext context; @@ -172,7 +169,7 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, //~ Methods ================================================================ public void setApplicationContext(ApplicationContext applicationContext) - throws BeansException { + throws BeansException { this.context = applicationContext; } @@ -181,7 +178,6 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, * granted to the Authentication. * * @param authorityGranters AuthorityGranter array - * * @see JaasAuthenticationProvider */ public void setAuthorityGranters(AuthorityGranter[] authorityGranters) { @@ -194,7 +190,6 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, * were ever set. * * @return The AuthorityGranter array, or null - * * @see #setAuthorityGranters(net.sf.acegisecurity.providers.jaas.AuthorityGranter[]) */ public AuthorityGranter[] getAuthorityGranters() { @@ -207,8 +202,7 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, * * @param callbackHandlers Array of JAASAuthenticationCallbackHandlers */ - public void setCallbackHandlers( - JaasAuthenticationCallbackHandler[] callbackHandlers) { + public void setCallbackHandlers(JaasAuthenticationCallbackHandler[] callbackHandlers) { this.callbackHandlers = callbackHandlers; } @@ -217,7 +211,6 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, * none are set. * * @return the JAASAuthenticationCallbackHandlers. - * * @see #setCallbackHandlers(net.sf.acegisecurity.providers.jaas.JaasAuthenticationCallbackHandler[]) */ public JaasAuthenticationCallbackHandler[] getCallbackHandlers() { @@ -228,9 +221,8 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, * Set the JAAS login configuration file. * * @param loginConfig Spring - * Resource - * + * href="http://www.springframework.org/docs/api/org/springframework/core/io/Resource.html">Spring + * Resource * @see JAAS * Reference @@ -257,8 +249,7 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, return loginContextName; } - public void setLoginExceptionResolver( - LoginExceptionResolver loginExceptionResolver) { + public void setLoginExceptionResolver(LoginExceptionResolver loginExceptionResolver) { this.loginExceptionResolver = loginExceptionResolver; } @@ -267,29 +258,36 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, } public void afterPropertiesSet() throws Exception { + if (loginConfig == null) { - throw new ApplicationContextException("loginConfig must be set on " - + getClass()); + throw new IllegalArgumentException("loginConfig must be set on " + + getClass()); } if ((loginContextName == null) || "".equals(loginContextName)) { - throw new ApplicationContextException( - "loginContextName must be set on " + getClass()); + throw new IllegalArgumentException("loginContextName must be set on " + getClass()); } - String loginConfigStr = loginConfig.getURL().toString(); + String loginConfigStr = null; - boolean allowed = "true".equalsIgnoreCase(Security.getProperty( - "policy.allowSystemProperty")); + try { + loginConfigStr = loginConfig.getFile().toString(); + } catch (IOException e) { + log.debug("Could not resolve loginConfig [" + loginConfig + "] as a File, using URL"); + loginConfigStr = loginConfig.getURL().toString(); + } - if (allowed) { + boolean allowed = "true".equalsIgnoreCase(Security.getProperty("policy.allowSystemProperty")); + + if (allowed && (System.getProperty(SYSPROP) == null)) { + log.debug("Setting system property [" + SYSPROP + "] to: " + loginConfigStr); System.setProperty(SYSPROP, loginConfigStr); } else { setPropertyUsingLoop(loginConfigStr); } Assert.notNull(Configuration.getConfiguration(), - "As per http://java.sun.com/j2se/1.5.0/docs/api/javax/security/auth/login/Configuration.html \"If a Configuration object was set via the Configuration.setConfiguration method, then that object is returned. Otherwise, a default Configuration object is returned\". Your JRE returned null to Configuration.getConfiguration()."); + "As per http://java.sun.com/j2se/1.5.0/docs/api/javax/security/auth/login/Configuration.html \"If a Configuration object was set via the Configuration.setConfiguration method, then that object is returned. Otherwise, a default Configuration object is returned\". Your JRE returned null to Configuration.getConfiguration()."); } /** @@ -297,18 +295,17 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, * and credential * * @param auth The Authentication object to be authenticated. - * * @return The authenticated Authentication object, with it's * grantedAuthorities set. - * * @throws AuthenticationException This implementation does not handle - * 'locked' or 'disabled' accounts. This method only throws a - * AuthenticationServiceException, with the message of the - * LoginException that will be thrown, should the - * loginContext.login() method fail. + * 'locked' or 'disabled' accounts. This method only throws a + * AuthenticationServiceException, with the message of the + * LoginException that will be thrown, should the + * loginContext.login() method fail. */ public Authentication authenticate(Authentication auth) - throws AuthenticationException { + throws AuthenticationException { + if (auth instanceof UsernamePasswordAuthenticationToken) { UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) auth; @@ -331,7 +328,7 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, Set principals = lc.getSubject().getPrincipals(); for (Iterator iterator = principals.iterator(); - iterator.hasNext();) { + iterator.hasNext();) { Principal principal = (Principal) iterator.next(); for (int i = 0; i < authorityGranters.length; i++) { @@ -347,8 +344,7 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, } //Convert the authorities set back to an array and apply it to the token. - token.setAuthorities((GrantedAuthority[]) authorities.toArray( - new GrantedAuthority[authorities.size()])); + token.setAuthorities((GrantedAuthority[]) authorities.toArray(new GrantedAuthority[authorities.size()])); //Publish the success event context.publishEvent(new JaasAuthenticationSuccessEvent(token)); @@ -357,7 +353,7 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, return token; } catch (LoginException loginException) { AcegiSecurityException ase = loginExceptionResolver - .resolveException(loginException); + .resolveException(loginException); context.publishEvent(new JaasAuthenticationFailedEvent(auth, ase)); throw ase; @@ -389,7 +385,9 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, } if (!alreadySet) { - Security.setProperty(prefix + n, loginConfigStr); + String key = prefix + n; + log.debug("Setting security property [" + key + "] to: " + loginConfigStr); + Security.setProperty(key, loginConfigStr); } } @@ -406,7 +404,7 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, } public void handle(Callback[] callbacks) - throws IOException, UnsupportedCallbackException { + throws IOException, UnsupportedCallbackException { for (int i = 0; i < callbackHandlers.length; i++) { JaasAuthenticationCallbackHandler handler = callbackHandlers[i]; diff --git a/core/src/test/java/org/acegisecurity/providers/jaas/JaasAuthenticationProviderTests.java b/core/src/test/java/org/acegisecurity/providers/jaas/JaasAuthenticationProviderTests.java index 063cef8b8d..3105bf4a01 100644 --- a/core/src/test/java/org/acegisecurity/providers/jaas/JaasAuthenticationProviderTests.java +++ b/core/src/test/java/org/acegisecurity/providers/jaas/JaasAuthenticationProviderTests.java @@ -16,29 +16,18 @@ package net.sf.acegisecurity.providers.jaas; import junit.framework.TestCase; - -import net.sf.acegisecurity.AcegiSecurityException; -import net.sf.acegisecurity.Authentication; -import net.sf.acegisecurity.AuthenticationException; -import net.sf.acegisecurity.GrantedAuthority; -import net.sf.acegisecurity.GrantedAuthorityImpl; -import net.sf.acegisecurity.LockedException; +import net.sf.acegisecurity.*; import net.sf.acegisecurity.providers.TestingAuthenticationToken; import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken; - import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextException; import org.springframework.context.support.ClassPathXmlApplicationContext; +import javax.security.auth.login.LoginException; import java.net.URL; - import java.security.Security; - import java.util.Arrays; import java.util.List; -import javax.security.auth.login.LoginException; - /** * Tests for the JaasAuthenticationProvider @@ -57,27 +46,27 @@ public class JaasAuthenticationProviderTests extends TestCase { public void testBadPassword() { try { - jaasProvider.authenticate(new UsernamePasswordAuthenticationToken( - "user", "asdf")); + jaasProvider.authenticate(new UsernamePasswordAuthenticationToken("user", "asdf")); fail("LoginException should have been thrown for the bad password"); - } catch (AuthenticationException e) {} + } catch (AuthenticationException e) { + } assertNotNull("Failure event not fired", eventCheck.failedEvent); assertNotNull("Failure event exception was null", - eventCheck.failedEvent.getException()); + eventCheck.failedEvent.getException()); assertNull("Success event was fired", eventCheck.successEvent); } public void testBadUser() { try { - jaasProvider.authenticate(new UsernamePasswordAuthenticationToken( - "asdf", "password")); + jaasProvider.authenticate(new UsernamePasswordAuthenticationToken("asdf", "password")); fail("LoginException should have been thrown for the bad user"); - } catch (AuthenticationException e) {} + } catch (AuthenticationException e) { + } assertNotNull("Failure event not fired", eventCheck.failedEvent); assertNotNull("Failure event exception was null", - eventCheck.failedEvent.getException()); + eventCheck.failedEvent.getException()); assertNull("Success event was fired", eventCheck.successEvent); } @@ -102,7 +91,7 @@ public class JaasAuthenticationProviderTests extends TestCase { try { myJaasProvider.afterPropertiesSet(); fail("Should have thrown ApplicationContextException"); - } catch (ApplicationContextException expected) { + } catch (IllegalArgumentException expected) { assertTrue(expected.getMessage().startsWith("loginConfig must be set on")); } } @@ -117,8 +106,8 @@ public class JaasAuthenticationProviderTests extends TestCase { try { myJaasProvider.afterPropertiesSet(); - fail("Should have thrown ApplicationContextException"); - } catch (ApplicationContextException expected) { + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException expected) { assertTrue(expected.getMessage().startsWith("loginContextName must be set on")); } @@ -126,8 +115,8 @@ public class JaasAuthenticationProviderTests extends TestCase { try { myJaasProvider.afterPropertiesSet(); - fail("Should have thrown ApplicationContextException"); - } catch (ApplicationContextException expected) { + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException expected) { assertTrue(expected.getMessage().startsWith("loginContextName must be set on")); } } @@ -136,13 +125,12 @@ public class JaasAuthenticationProviderTests extends TestCase { GrantedAuthorityImpl role1 = new GrantedAuthorityImpl("ROLE_1"); GrantedAuthorityImpl role2 = new GrantedAuthorityImpl("ROLE_2"); - GrantedAuthority[] defaultAuths = new GrantedAuthority[] {role1, role2,}; + GrantedAuthority[] defaultAuths = new GrantedAuthority[]{role1, role2, }; UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("user", "password", defaultAuths); - assertTrue(jaasProvider.supports( - UsernamePasswordAuthenticationToken.class)); + assertTrue(jaasProvider.supports(UsernamePasswordAuthenticationToken.class)); Authentication auth = jaasProvider.authenticate(token); @@ -154,13 +142,13 @@ public class JaasAuthenticationProviderTests extends TestCase { List list = Arrays.asList(auth.getAuthorities()); assertTrue("GrantedAuthorities does not contain ROLE_TEST", - list.contains(new GrantedAuthorityImpl("ROLE_TEST"))); + list.contains(new GrantedAuthorityImpl("ROLE_TEST"))); assertTrue("GrantedAuthorities does not contain ROLE_1", - list.contains(role1)); + list.contains(role1)); assertTrue("GrantedAuthorities does not contain ROLE_2", - list.contains(role2)); + list.contains(role2)); boolean foundit = false; @@ -170,7 +158,7 @@ public class JaasAuthenticationProviderTests extends TestCase { if (obj instanceof JaasGrantedAuthority) { JaasGrantedAuthority grant = (JaasGrantedAuthority) obj; assertNotNull("Principal was null on JaasGrantedAuthority", - grant.getPrincipal()); + grant.getPrincipal()); foundit = true; } } @@ -179,7 +167,7 @@ public class JaasAuthenticationProviderTests extends TestCase { assertNotNull("Success event not fired", eventCheck.successEvent); assertEquals("Auth objects are not equal", auth, - eventCheck.successEvent.getAuthentication()); + eventCheck.successEvent.getAuthentication()); assertNull("Failure event was fired", eventCheck.failedEvent); } @@ -187,16 +175,15 @@ public class JaasAuthenticationProviderTests extends TestCase { public void testLoginExceptionResolver() { assertNotNull(jaasProvider.getLoginExceptionResolver()); jaasProvider.setLoginExceptionResolver(new LoginExceptionResolver() { - public AcegiSecurityException resolveException(LoginException e) { - return new LockedException("This is just a test!"); - } - }); + public AcegiSecurityException resolveException(LoginException e) { + return new LockedException("This is just a test!"); + } + }); try { - jaasProvider.authenticate(new UsernamePasswordAuthenticationToken( - "user", "password")); - } catch (LockedException e) {} - catch (Exception e) { + jaasProvider.authenticate(new UsernamePasswordAuthenticationToken("user", "password")); + } catch (LockedException e) { + } catch (Exception e) { fail("LockedException should have been thrown and caught"); } } @@ -205,25 +192,22 @@ public class JaasAuthenticationProviderTests extends TestCase { UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("user", "password", null); - assertTrue(jaasProvider.supports( - UsernamePasswordAuthenticationToken.class)); + assertTrue(jaasProvider.supports(UsernamePasswordAuthenticationToken.class)); Authentication auth = jaasProvider.authenticate(token); assertTrue("Only ROLE_TEST should have been returned", - auth.getAuthorities().length == 1); + auth.getAuthorities().length == 1); } public void testUnsupportedAuthenticationObjectReturnsNull() { - assertNull(jaasProvider.authenticate( - new TestingAuthenticationToken("foo", "bar", - new GrantedAuthority[] {}))); + assertNull(jaasProvider.authenticate(new TestingAuthenticationToken("foo", "bar", + new GrantedAuthority[]{}))); } protected void setUp() throws Exception { String resName = "/" + getClass().getName().replace('.', '/') + ".xml"; context = new ClassPathXmlApplicationContext(resName); eventCheck = (JaasEventCheck) context.getBean("eventCheck"); - jaasProvider = (JaasAuthenticationProvider) context.getBean( - "jaasAuthenticationProvider"); + jaasProvider = (JaasAuthenticationProvider) context.getBean("jaasAuthenticationProvider"); } }