/* * Copyright 2010-2016 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.web.jaasapi; import static org.assertj.core.api.Assertions.assertThat; import java.io.IOException; import java.security.AccessController; import java.security.Principal; import java.util.HashMap; import javax.security.auth.Subject; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.TextInputCallback; import javax.security.auth.callback.UnsupportedCallbackException; 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.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.mock.web.MockFilterChain; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.authentication.jaas.JaasAuthenticationToken; import org.springframework.security.authentication.jaas.TestLoginModule; import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.context.SecurityContextHolder; /** * Tests the JaasApiIntegrationFilter. * * @author Rob Winch */ public class JaasApiIntegrationFilterTests { // ~ Instance fields // ================================================================================================ private JaasApiIntegrationFilter filter; private MockHttpServletRequest request; private MockHttpServletResponse response; private Authentication token; private Subject authenticatedSubject; private Configuration testConfiguration; private CallbackHandler callbackHandler; // ~ Methods // ======================================================================================================== @Before public void onBeforeTests() throws Exception { this.filter = new JaasApiIntegrationFilter(); this.request = new MockHttpServletRequest(); this.response = new MockHttpServletResponse(); authenticatedSubject = new Subject(); authenticatedSubject.getPrincipals().add(new Principal() { public String getName() { return "principal"; } }); authenticatedSubject.getPrivateCredentials().add("password"); authenticatedSubject.getPublicCredentials().add("username"); callbackHandler = new CallbackHandler() { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (Callback callback : callbacks) { if (callback instanceof NameCallback) { ((NameCallback) callback).setName("user"); } else if (callback instanceof PasswordCallback) { ((PasswordCallback) callback).setPassword( "password".toCharArray()); } else if (callback instanceof TextInputCallback) { // ignore } else { throw new UnsupportedCallbackException(callback, "Unrecognized Callback " + callback); } } } }; testConfiguration = new Configuration() { public void refresh() { } public AppConfigurationEntry[] getAppConfigurationEntry(String name) { return new AppConfigurationEntry[] { new AppConfigurationEntry( TestLoginModule.class.getName(), LoginModuleControlFlag.REQUIRED, new HashMap<String, String>()) }; } }; LoginContext ctx = new LoginContext("SubjectDoAsFilterTest", authenticatedSubject, callbackHandler, testConfiguration); ctx.login(); token = new JaasAuthenticationToken("username", "password", AuthorityUtils.createAuthorityList("ROLE_ADMIN"), ctx); // just in case someone forgot to clear the context SecurityContextHolder.clearContext(); } @After public void onAfterTests() { SecurityContextHolder.clearContext(); } /** * Ensure a Subject was not setup in some other manner. */ @Test public void currentSubjectNull() { assertThat(Subject.getSubject(AccessController.getContext())).isNull(); } @Test public void obtainSubjectNullAuthentication() { assertNullSubject(filter.obtainSubject(request)); } @Test public void obtainSubjectNonJaasAuthentication() { Authentication authentication = new TestingAuthenticationToken("un", "pwd"); authentication.setAuthenticated(true); SecurityContextHolder.getContext().setAuthentication(authentication); assertNullSubject(filter.obtainSubject(request)); } @Test public void obtainSubjectNullLoginContext() { token = new JaasAuthenticationToken("un", "pwd", AuthorityUtils.createAuthorityList("ROLE_ADMIN"), null); SecurityContextHolder.getContext().setAuthentication(token); assertNullSubject(filter.obtainSubject(request)); } @Test public void obtainSubjectNullSubject() throws Exception { LoginContext ctx = new LoginContext("obtainSubjectNullSubject", null, callbackHandler, testConfiguration); assertThat(ctx.getSubject()).isNull(); token = new JaasAuthenticationToken("un", "pwd", AuthorityUtils.createAuthorityList("ROLE_ADMIN"), ctx); SecurityContextHolder.getContext().setAuthentication(token); assertNullSubject(filter.obtainSubject(request)); } @Test public void obtainSubject() throws Exception { SecurityContextHolder.getContext().setAuthentication(token); assertThat(filter.obtainSubject(request)).isEqualTo(authenticatedSubject); } @Test public void doFilterCurrentSubjectPopulated() throws Exception { SecurityContextHolder.getContext().setAuthentication(token); assertJaasSubjectEquals(authenticatedSubject); } @Test public void doFilterAuthenticationNotAuthenticated() throws Exception { // Authentication is null, so no Subject is populated. token.setAuthenticated(false); SecurityContextHolder.getContext().setAuthentication(token); assertJaasSubjectEquals(null); filter.setCreateEmptySubject(true); assertJaasSubjectEquals(new Subject()); } @Test public void doFilterAuthenticationNull() throws Exception { assertJaasSubjectEquals(null); filter.setCreateEmptySubject(true); assertJaasSubjectEquals(new Subject()); } // ~ Helper Methods // ==================================================================================================== private void assertJaasSubjectEquals(final Subject expectedValue) throws Exception { MockFilterChain chain = new MockFilterChain() { public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { // See if the subject was updated Subject currentSubject = Subject.getSubject( AccessController.getContext()); assertThat(currentSubject).isEqualTo(expectedValue); // run so we know the chain was executed super.doFilter(request, response); } }; filter.doFilter(request, response, chain); // ensure that the chain was actually invoked assertThat(chain.getRequest()).isNotNull(); } private void assertNullSubject(Subject subject) { assertThat(subject).withFailMessage( "Subject is expected to be null, but is not. Got " + subject).isNull(); } }