/* * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited * * 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.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; import java.io.File; import java.io.FileOutputStream; import java.io.PrintWriter; import java.net.URL; import java.security.Security; import java.util.*; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import org.junit.Before; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.io.FileSystemResource; import org.springframework.security.authentication.LockedException; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.session.SessionDestroyedEvent; /** * Tests for the JaasAuthenticationProvider * * @author Ray Krueger */ public class JaasAuthenticationProviderTests { // ~ Instance fields // ================================================================================================ private ApplicationContext context; private JaasAuthenticationProvider jaasProvider; private JaasEventCheck eventCheck; // ~ Methods // ======================================================================================================== @Before public void setUp() throws Exception { String resName = "/" + getClass().getName().replace('.', '/') + ".xml"; context = new ClassPathXmlApplicationContext(resName); eventCheck = (JaasEventCheck) context.getBean("eventCheck"); jaasProvider = (JaasAuthenticationProvider) context .getBean("jaasAuthenticationProvider"); } @Test public void testBadPassword() { try { jaasProvider.authenticate(new UsernamePasswordAuthenticationToken("user", "asdf")); fail("LoginException should have been thrown for the bad password"); } catch (AuthenticationException e) { } assertThat(eventCheck.failedEvent).as("Failure event not fired").isNotNull(); assertThat(eventCheck.failedEvent.getException()).withFailMessage("Failure event exception was null").isNotNull(); assertThat(eventCheck.successEvent).as("Success event was fired").isNull(); } @Test public void testBadUser() { try { jaasProvider.authenticate(new UsernamePasswordAuthenticationToken("asdf", "password")); fail("LoginException should have been thrown for the bad user"); } catch (AuthenticationException e) { } assertThat(eventCheck.failedEvent).as("Failure event not fired").isNotNull(); assertThat(eventCheck.failedEvent.getException()).withFailMessage("Failure event exception was null").isNotNull(); assertThat(eventCheck.successEvent).as("Success event was fired").isNull(); } @Test public void testConfigurationLoop() throws Exception { String resName = "/" + getClass().getName().replace('.', '/') + ".conf"; URL url = getClass().getResource(resName); Security.setProperty("login.config.url.1", url.toString()); setUp(); testFull(); } @Test public void detectsMissingLoginConfig() throws Exception { JaasAuthenticationProvider myJaasProvider = new JaasAuthenticationProvider(); myJaasProvider.setApplicationEventPublisher(context); myJaasProvider.setAuthorityGranters(jaasProvider.getAuthorityGranters()); myJaasProvider.setCallbackHandlers(jaasProvider.getCallbackHandlers()); myJaasProvider.setLoginContextName(jaasProvider.getLoginContextName()); try { myJaasProvider.afterPropertiesSet(); fail("Should have thrown ApplicationContextException"); } catch (IllegalArgumentException expected) { assertThat(expected.getMessage().startsWith("loginConfig must be set on")).isTrue(); } } // SEC-1239 @Test public void spacesInLoginConfigPathAreAccepted() throws Exception { File configFile; // Create temp directory with a space in the name File configDir = new File(System.getProperty("java.io.tmpdir") + File.separator + "jaas test"); configDir.deleteOnExit(); if (configDir.exists()) { configDir.delete(); } configDir.mkdir(); configFile = File.createTempFile("login", "conf", configDir); configFile.deleteOnExit(); FileOutputStream fos = new FileOutputStream(configFile); PrintWriter pw = new PrintWriter(fos); pw.append("JAASTestBlah {" + "org.springframework.security.authentication.jaas.TestLoginModule required;" + "};"); pw.flush(); pw.close(); JaasAuthenticationProvider myJaasProvider = new JaasAuthenticationProvider(); myJaasProvider.setApplicationEventPublisher(context); myJaasProvider.setLoginConfig(new FileSystemResource(configFile)); myJaasProvider.setAuthorityGranters(jaasProvider.getAuthorityGranters()); myJaasProvider.setCallbackHandlers(jaasProvider.getCallbackHandlers()); myJaasProvider.setLoginContextName(jaasProvider.getLoginContextName()); myJaasProvider.afterPropertiesSet(); } @Test public void detectsMissingLoginContextName() throws Exception { JaasAuthenticationProvider myJaasProvider = new JaasAuthenticationProvider(); myJaasProvider.setApplicationEventPublisher(context); myJaasProvider.setAuthorityGranters(jaasProvider.getAuthorityGranters()); myJaasProvider.setCallbackHandlers(jaasProvider.getCallbackHandlers()); myJaasProvider.setLoginConfig(jaasProvider.getLoginConfig()); myJaasProvider.setLoginContextName(null); try { myJaasProvider.afterPropertiesSet(); fail("Should have thrown IllegalArgumentException"); } catch (IllegalArgumentException expected) { assertThat(expected.getMessage()).startsWith("loginContextName must be set on"); } myJaasProvider.setLoginContextName(""); try { myJaasProvider.afterPropertiesSet(); fail("Should have thrown IllegalArgumentException"); } catch (IllegalArgumentException expected) { assertThat(expected.getMessage().startsWith("loginContextName must be set on")); } } @Test public void testFull() throws Exception { UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( "user", "password", AuthorityUtils.createAuthorityList("ROLE_ONE")); assertThat(jaasProvider.supports(UsernamePasswordAuthenticationToken.class)).isTrue(); Authentication auth = jaasProvider.authenticate(token); assertThat(jaasProvider.getAuthorityGranters()).isNotNull(); assertThat(jaasProvider.getCallbackHandlers()).isNotNull(); assertThat(jaasProvider.getLoginConfig()).isNotNull(); assertThat(jaasProvider.getLoginContextName()).isNotNull(); Collection<? extends GrantedAuthority> list = auth.getAuthorities(); Set<String> set = AuthorityUtils.authorityListToSet(list); assertThat(set.contains("ROLE_ONE")).withFailMessage("GrantedAuthorities should not contain ROLE_ONE").isFalse(); assertThat(set.contains("ROLE_TEST1")).withFailMessage("GrantedAuthorities should contain ROLE_TEST1").isTrue(); assertThat(set.contains("ROLE_TEST2")).withFailMessage("GrantedAuthorities should contain ROLE_TEST2").isTrue(); boolean foundit = false; for (GrantedAuthority a : list) { if (a instanceof JaasGrantedAuthority) { JaasGrantedAuthority grant = (JaasGrantedAuthority) a; assertThat(grant.getPrincipal()).withFailMessage("Principal was null on JaasGrantedAuthority").isNotNull(); foundit = true; } } assertThat(foundit).as("Could not find a JaasGrantedAuthority").isTrue(); assertThat(eventCheck.successEvent).as("Success event should be fired").isNotNull(); assertThat(eventCheck.successEvent.getAuthentication()).withFailMessage("Auth objects should be equal").isEqualTo(auth); assertThat(eventCheck.failedEvent).as("Failure event should not be fired").isNull(); } @Test public void testGetApplicationEventPublisher() throws Exception { assertThat(jaasProvider.getApplicationEventPublisher()).isNotNull(); } @Test public void testLoginExceptionResolver() { assertThat(jaasProvider.getLoginExceptionResolver()).isNotNull(); jaasProvider.setLoginExceptionResolver(new LoginExceptionResolver() { public AuthenticationException resolveException(LoginException e) { return new LockedException("This is just a test!"); } }); try { jaasProvider.authenticate(new UsernamePasswordAuthenticationToken("user", "password")); } catch (LockedException e) { } catch (Exception e) { fail("LockedException should have been thrown and caught"); } } @Test public void testLogout() throws Exception { MockLoginContext loginContext = new MockLoginContext( jaasProvider.getLoginContextName()); JaasAuthenticationToken token = new JaasAuthenticationToken(null, null, loginContext); SecurityContext context = SecurityContextHolder.createEmptyContext(); context.setAuthentication(token); SessionDestroyedEvent event = mock(SessionDestroyedEvent.class); when(event.getSecurityContexts()).thenReturn(Arrays.asList(context)); jaasProvider.handleLogout(event); assertThat(loginContext.loggedOut).isTrue(); } @Test public void testNullDefaultAuthorities() { UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( "user", "password"); assertThat(jaasProvider.supports(UsernamePasswordAuthenticationToken.class)).isTrue(); Authentication auth = jaasProvider.authenticate(token); assertThat(auth .getAuthorities()).withFailMessage("Only ROLE_TEST1 and ROLE_TEST2 should have been returned").hasSize(2); } @Test public void testUnsupportedAuthenticationObjectReturnsNull() { assertThat(jaasProvider.authenticate(new TestingAuthenticationToken("foo", "bar", AuthorityUtils.NO_AUTHORITIES))).isNull(); } // ~ Inner Classes // ================================================================================================== private static class MockLoginContext extends LoginContext { boolean loggedOut = false; public MockLoginContext(String loginModule) throws LoginException { super(loginModule); } public void logout() throws LoginException { this.loggedOut = true; } } }