SEC-1564: JAAS Configuration can now be injected into DefaultJaasAuthenticationProvider

This commit is contained in:
rwinch
2010-09-10 20:17:22 -05:00
parent 8bf1b8420a
commit 58d9903ebc
19 changed files with 1521 additions and 332 deletions

View File

@@ -0,0 +1,273 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.authentication.jaas;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import java.util.Collections;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import org.apache.commons.logging.Log;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.jaas.DefaultJaasAuthenticationProvider;
import org.springframework.security.authentication.jaas.event.JaasAuthenticationFailedEvent;
import org.springframework.security.authentication.jaas.event.JaasAuthenticationSuccessEvent;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.session.SessionDestroyedEvent;
import org.springframework.test.util.ReflectionTestUtils;
public class DefaultJaasAuthenticationProviderTests {
private DefaultJaasAuthenticationProvider provider;
private UsernamePasswordAuthenticationToken token;
private ApplicationEventPublisher publisher;
private Log log;
@Before
public void setUp() throws Exception {
Configuration configuration = mock(Configuration.class);
publisher = mock(ApplicationEventPublisher.class);
log = mock(Log.class);
provider = new DefaultJaasAuthenticationProvider();
provider.setConfiguration(configuration);
provider.setApplicationEventPublisher(publisher);
provider.setAuthorityGranters(new AuthorityGranter[] { new TestAuthorityGranter() });
provider.afterPropertiesSet();
AppConfigurationEntry[] aces = new AppConfigurationEntry[] { new AppConfigurationEntry(
TestLoginModule.class.getName(), LoginModuleControlFlag.REQUIRED,
Collections.<String, Object> emptyMap()) };
when(configuration.getAppConfigurationEntry(provider.getLoginContextName())).thenReturn(aces);
token = new UsernamePasswordAuthenticationToken("user", "password");
ReflectionTestUtils.setField(provider, "log", log);
}
@Test(expected = IllegalArgumentException.class)
public void afterPropertiesSetNullConfiguration() throws Exception {
provider.setConfiguration(null);
provider.afterPropertiesSet();
}
@Test(expected = IllegalArgumentException.class)
public void afterPropertiesSetNullAuthorityGranters() throws Exception {
provider.setAuthorityGranters(null);
provider.afterPropertiesSet();
}
@Test
public void authenticateUnsupportedAuthentication() {
assertEquals(null, provider.authenticate(new TestingAuthenticationToken("user", "password")));
}
@Test
public void authenticateSuccess() throws Exception {
Authentication auth = provider.authenticate(token);
assertEquals(token.getPrincipal(), auth.getPrincipal());
assertEquals(token.getCredentials(), auth.getCredentials());
assertEquals(true, auth.isAuthenticated());
assertEquals(false, auth.getAuthorities().isEmpty());
verify(publisher).publishEvent(isA(JaasAuthenticationSuccessEvent.class));
verifyNoMoreInteractions(publisher);
}
@Test
public void authenticateBadPassword() {
try {
provider.authenticate(new UsernamePasswordAuthenticationToken("user", "asdf"));
fail("LoginException should have been thrown for the bad password");
} catch (AuthenticationException success) {
}
verifyFailedLogin();
}
@Test
public void authenticateBadUser() {
try {
provider.authenticate(new UsernamePasswordAuthenticationToken("asdf", "password"));
fail("LoginException should have been thrown for the bad user");
} catch (AuthenticationException success) {
}
verifyFailedLogin();
}
@Test
public void logout() throws Exception {
SessionDestroyedEvent event = mock(SessionDestroyedEvent.class);
SecurityContext securityContext = mock(SecurityContext.class);
JaasAuthenticationToken token = mock(JaasAuthenticationToken.class);
LoginContext context = mock(LoginContext.class);
when(event.getSecurityContext()).thenReturn(securityContext);
when(securityContext.getAuthentication()).thenReturn(token);
when(token.getLoginContext()).thenReturn(context);
provider.onApplicationEvent(event);
verify(event).getSecurityContext();
verify(securityContext).getAuthentication();
verify(token).getLoginContext();
verify(context).logout();
verifyNoMoreInteractions(event, securityContext, token, context);
}
@Test
public void logoutNullSession() {
SessionDestroyedEvent event = mock(SessionDestroyedEvent.class);
provider.handleLogout(event);
verify(event).getSecurityContext();
verify(log).debug(anyString());
verifyNoMoreInteractions(event);
}
@Test
public void logoutNullAuthentication() {
SessionDestroyedEvent event = mock(SessionDestroyedEvent.class);
SecurityContext securityContext = mock(SecurityContext.class);
when(event.getSecurityContext()).thenReturn(securityContext);
provider.handleLogout(event);
verify(event).getSecurityContext();
verify(event).getSecurityContext();
verify(securityContext).getAuthentication();
verifyNoMoreInteractions(event, securityContext);
}
@Test
public void logoutNonJaasAuthentication() {
SessionDestroyedEvent event = mock(SessionDestroyedEvent.class);
SecurityContext securityContext = mock(SecurityContext.class);
when(event.getSecurityContext()).thenReturn(securityContext);
when(securityContext.getAuthentication()).thenReturn(token);
provider.handleLogout(event);
verify(event).getSecurityContext();
verify(event).getSecurityContext();
verify(securityContext).getAuthentication();
verifyNoMoreInteractions(event, securityContext);
}
@Test
public void logoutNullLoginContext() throws Exception {
SessionDestroyedEvent event = mock(SessionDestroyedEvent.class);
SecurityContext securityContext = mock(SecurityContext.class);
JaasAuthenticationToken token = mock(JaasAuthenticationToken.class);
when(event.getSecurityContext()).thenReturn(securityContext);
when(securityContext.getAuthentication()).thenReturn(token);
provider.onApplicationEvent(event);
verify(event).getSecurityContext();
verify(securityContext).getAuthentication();
verify(token).getLoginContext();
verifyNoMoreInteractions(event, securityContext, token);
}
@Test
public void logoutLoginException() throws Exception {
SessionDestroyedEvent event = mock(SessionDestroyedEvent.class);
SecurityContext securityContext = mock(SecurityContext.class);
JaasAuthenticationToken token = mock(JaasAuthenticationToken.class);
LoginContext context = mock(LoginContext.class);
LoginException loginException = new LoginException("Failed Login");
when(event.getSecurityContext()).thenReturn(securityContext);
when(securityContext.getAuthentication()).thenReturn(token);
when(token.getLoginContext()).thenReturn(context);
doThrow(loginException).when(context).logout();
provider.onApplicationEvent(event);
verify(event).getSecurityContext();
verify(securityContext).getAuthentication();
verify(token).getLoginContext();
verify(context).logout();
verify(log).warn(anyString(), eq(loginException));
verifyNoMoreInteractions(event, securityContext, token, context);
}
@Test
public void publishNullPublisher() {
provider.setApplicationEventPublisher(null);
AuthenticationException ae = new BadCredentialsException("Failed to login", token);
provider.publishFailureEvent(token, ae);
provider.publishSuccessEvent(token);
}
@Test
public void javadocExample() {
String resName = "/" + getClass().getName().replace('.', '/') + ".xml";
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(resName);
context.registerShutdownHook();
try {
provider = context.getBean(DefaultJaasAuthenticationProvider.class);
Authentication auth = provider.authenticate(token);
assertEquals(true, auth.isAuthenticated());
assertEquals(token.getPrincipal(), auth.getPrincipal());
} finally {
context.close();
}
}
private void verifyFailedLogin() {
verify(publisher).publishEvent(argThat(new BaseMatcher<JaasAuthenticationFailedEvent>() {
public void describeTo(Description desc) {
desc.appendText("isA(org.springframework.security.authentication.jaas.event.JaasAuthenticationFailedEvent)");
desc.appendText(" && event.getException() != null");
}
public boolean matches(Object arg) {
JaasAuthenticationFailedEvent e = (JaasAuthenticationFailedEvent) arg;
return e.getException() != null;
}
}));
verifyNoMoreInteractions(publisher);
}
}

View File

@@ -0,0 +1,98 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.authentication.jaas.memory;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Map;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
import org.junit.Before;
import org.junit.Test;
import org.springframework.security.authentication.jaas.TestLoginModule;
/**
* Tests {@link InMemoryConfiguration}.
*
* @author Rob Winch
*/
public class InMemoryConfigurationTests {
private AppConfigurationEntry[] defaultEntries;
private Map<String, AppConfigurationEntry[]> mappedEntries;
@Before
public void setUp() {
defaultEntries = new AppConfigurationEntry[] { new AppConfigurationEntry(TestLoginModule.class.getName(),
LoginModuleControlFlag.REQUIRED, Collections.<String, Object> emptyMap()) };
mappedEntries = Collections.<String, AppConfigurationEntry[]> singletonMap("name",
new AppConfigurationEntry[] { new AppConfigurationEntry(TestLoginModule.class.getName(),
LoginModuleControlFlag.OPTIONAL, Collections.<String, Object> emptyMap()) });
}
@Test
public void constructorNullDefault() {
assertNull(new InMemoryConfiguration((AppConfigurationEntry[]) null).getAppConfigurationEntry("name"));
}
@Test(expected = IllegalArgumentException.class)
public void constructorNullMapped() {
new InMemoryConfiguration((Map<String, AppConfigurationEntry[]>) null);
}
@Test
public void constructorEmptyMap() {
assertNull(new InMemoryConfiguration(Collections.<String, AppConfigurationEntry[]> emptyMap())
.getAppConfigurationEntry("name"));
}
@Test
public void constructorEmptyMapNullDefault() {
assertNull(new InMemoryConfiguration(Collections.<String, AppConfigurationEntry[]> emptyMap(), null)
.getAppConfigurationEntry("name"));
}
@Test(expected = IllegalArgumentException.class)
public void constructorNullMapNullDefault() {
new InMemoryConfiguration(null, null);
}
@Test
public void nonnullDefault() {
InMemoryConfiguration configuration = new InMemoryConfiguration(defaultEntries);
assertArrayEquals(defaultEntries, configuration.getAppConfigurationEntry("name"));
}
@Test
public void mappedNonnullDefault() {
InMemoryConfiguration configuration = new InMemoryConfiguration(mappedEntries, defaultEntries);
assertArrayEquals(defaultEntries, configuration.getAppConfigurationEntry("missing"));
assertArrayEquals(mappedEntries.get("name"), configuration.getAppConfigurationEntry("name"));
}
@Test
public void jdk5Compatable() throws Exception {
Method method = InMemoryConfiguration.class.getDeclaredMethod("refresh");
assertEquals(InMemoryConfiguration.class, method.getDeclaringClass());
}
}

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<bean id="jaasAuthProvider"
class="org.springframework.security.authentication.jaas.DefaultJaasAuthenticationProvider">
<property name="configuration">
<bean
class="org.springframework.security.authentication.jaas.memory.InMemoryConfiguration">
<constructor-arg>
<map>
<entry key="SPRINGSECURITY">
<array>
<bean
class="javax.security.auth.login.AppConfigurationEntry">
<constructor-arg
value="org.springframework.security.authentication.jaas.TestLoginModule" />
<constructor-arg>
<util:constant
static-field="javax.security.auth.login.AppConfigurationEntry$LoginModuleControlFlag.REQUIRED" />
</constructor-arg>
<constructor-arg>
<map></map>
</constructor-arg>
</bean>
</array>
</entry>
</map>
</constructor-arg>
</bean>
</property>
<property name="authorityGranters">
<list>
<bean
class="org.springframework.security.authentication.jaas.TestAuthorityGranter" />
</list>
</property>
</bean>
</beans>