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 1b62240a31..07adc16e1e 100644 --- a/core/src/main/java/org/acegisecurity/providers/jaas/JaasAuthenticationProvider.java +++ b/core/src/main/java/org/acegisecurity/providers/jaas/JaasAuthenticationProvider.java @@ -23,41 +23,49 @@ 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.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 @@ -66,37 +74,37 @@ import java.util.Set; * 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 @@ -107,24 +115,24 @@ import java.util.Set; * 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 @@ -135,26 +143,27 @@ import java.util.Set; * 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 { + //~ Static fields/initializers ============================================= - private static final Log log = LogFactory.getLog(JaasAuthenticationProvider.class); + protected static final Log log = LogFactory.getLog(JaasAuthenticationProvider.class); //~ Instance fields ======================================================== @@ -169,15 +178,20 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, //~ Methods ================================================================ public void setApplicationContext(ApplicationContext applicationContext) - throws BeansException { + throws BeansException { this.context = applicationContext; } + public ApplicationContext getApplicationContext() { + return context; + } + /** * Set the AuthorityGranters that should be consulted for role names to be * granted to the Authentication. * * @param authorityGranters AuthorityGranter array + * * @see JaasAuthenticationProvider */ public void setAuthorityGranters(AuthorityGranter[] authorityGranters) { @@ -190,6 +204,7 @@ 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() { @@ -202,7 +217,8 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, * * @param callbackHandlers Array of JAASAuthenticationCallbackHandlers */ - public void setCallbackHandlers(JaasAuthenticationCallbackHandler[] callbackHandlers) { + public void setCallbackHandlers( + JaasAuthenticationCallbackHandler[] callbackHandlers) { this.callbackHandlers = callbackHandlers; } @@ -211,6 +227,7 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, * none are set. * * @return the JAASAuthenticationCallbackHandlers. + * * @see #setCallbackHandlers(net.sf.acegisecurity.providers.jaas.JaasAuthenticationCallbackHandler[]) */ public JaasAuthenticationCallbackHandler[] getCallbackHandlers() { @@ -221,8 +238,9 @@ 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 @@ -249,7 +267,8 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, return loginContextName; } - public void setLoginExceptionResolver(LoginExceptionResolver loginExceptionResolver) { + public void setLoginExceptionResolver( + LoginExceptionResolver loginExceptionResolver) { this.loginExceptionResolver = loginExceptionResolver; } @@ -258,31 +277,36 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, } public void afterPropertiesSet() throws Exception { - - Assert.notNull(loginConfig, "loginConfig must be set on " - + getClass()); - Assert.hasLength(loginContextName, "loginContextName must be set on " + getClass()); + Assert.notNull(loginConfig, "loginConfig must be set on " + getClass()); + Assert.hasLength(loginContextName, + "loginContextName must be set on " + getClass()); String loginConfigStr = null; try { loginConfigStr = loginConfig.getFile().toString(); } catch (IOException e) { - log.debug("Could not resolve loginConfig [" + loginConfig + "] as a File, using URL"); + if (log.isDebugEnabled()) { + log.debug("Could not resolve loginConfig [" + loginConfig + + "] as a File, using URL"); + } + loginConfigStr = loginConfig.getURL().toString(); } - boolean allowed = "true".equalsIgnoreCase(Security.getProperty("policy.allowSystemProperty")); + boolean allowed = "true".equalsIgnoreCase(Security.getProperty( + "policy.allowSystemProperty")); if (allowed && (System.getProperty(SYSPROP) == null)) { - log.debug("Setting system property [" + SYSPROP + "] to: " + loginConfigStr); + 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()."); } /** @@ -290,17 +314,18 @@ 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; @@ -323,7 +348,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++) { @@ -339,18 +364,19 @@ 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)); + publishSuccessEvent(token); //we're done, return the token. return token; } catch (LoginException loginException) { AcegiSecurityException ase = loginExceptionResolver - .resolveException(loginException); + .resolveException(loginException); - context.publishEvent(new JaasAuthenticationFailedEvent(auth, ase)); + publishFailureEvent(token, ase); throw ase; } } @@ -362,6 +388,40 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, return UsernamePasswordAuthenticationToken.class.isAssignableFrom(aClass); } + /** + * Publishes the {@link JaasAuthenticationFailedEvent}. Can be overridden + * by subclasses for different functionality + * + * @param token The {@link UsernamePasswordAuthenticationToken} being + * processed + * @param ase The {@link AcegiSecurityException} that caused the failure + */ + protected void publishFailureEvent( + UsernamePasswordAuthenticationToken token, AcegiSecurityException ase) { + getApplicationContext().publishEvent(new JaasAuthenticationFailedEvent( + token, ase)); + } + + /** + * Publishes the {@link JaasAuthenticationSuccessEvent}. Can be overridden + * by subclasses for different functionality. + * + * @param token The {@link UsernamePasswordAuthenticationToken} being + * processed + */ + protected void publishSuccessEvent( + UsernamePasswordAuthenticationToken token) { + getApplicationContext().publishEvent(new JaasAuthenticationSuccessEvent( + token)); + } + + /** + * Loops through the login.config.url.1,login.config.url.2 properties + * looking for the login configuration. If it is not set, it will be set + * to the last available login.config.url.X property. + * + * @param loginConfigStr + */ private void setPropertyUsingLoop(String loginConfigStr) { boolean alreadySet = false; @@ -381,7 +441,8 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, if (!alreadySet) { String key = prefix + n; - log.debug("Setting security property [" + key + "] to: " + loginConfigStr); + log.debug("Setting security property [" + key + "] to: " + + loginConfigStr); Security.setProperty(key, loginConfigStr); } } @@ -399,7 +460,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 3105bf4a01..fe5e47a82c 100644 --- a/core/src/test/java/org/acegisecurity/providers/jaas/JaasAuthenticationProviderTests.java +++ b/core/src/test/java/org/acegisecurity/providers/jaas/JaasAuthenticationProviderTests.java @@ -199,6 +199,10 @@ public class JaasAuthenticationProviderTests extends TestCase { auth.getAuthorities().length == 1); } + public void testGetApplicationContext() throws Exception { + assertNotNull(jaasProvider.getApplicationContext()); + } + public void testUnsupportedAuthenticationObjectReturnsNull() { assertNull(jaasProvider.authenticate(new TestingAuthenticationToken("foo", "bar", new GrantedAuthority[]{})));