/* * Copyright 2002-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.messaging.context; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.simp.SimpMessageHeaderAccessor; import org.springframework.messaging.support.MessageBuilder; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.messaging.context.SecurityContextChannelInterceptor; import java.security.Principal; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.security.core.context.SecurityContextHolder.*; @RunWith(MockitoJUnitRunner.class) public class SecurityContextChannelInterceptorTests { @Mock MessageChannel channel; @Mock MessageHandler handler; @Mock Principal principal; MessageBuilder<String> messageBuilder; Authentication authentication; SecurityContextChannelInterceptor interceptor; AnonymousAuthenticationToken expectedAnonymous; @Before public void setup() { authentication = new TestingAuthenticationToken("user", "pass", "ROLE_USER"); messageBuilder = MessageBuilder.withPayload("payload"); expectedAnonymous = new AnonymousAuthenticationToken("key", "anonymous", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); interceptor = new SecurityContextChannelInterceptor(); } @After public void cleanup() { clearContext(); } @Test(expected = IllegalArgumentException.class) public void constructorNullHeader() { new SecurityContextChannelInterceptor(null); } @Test public void preSendCustomHeader() throws Exception { String headerName = "header"; interceptor = new SecurityContextChannelInterceptor(headerName); messageBuilder.setHeader(headerName, authentication); interceptor.preSend(messageBuilder.build(), channel); assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs( authentication); } @Test public void preSendUserSet() throws Exception { messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, authentication); interceptor.preSend(messageBuilder.build(), channel); assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs( authentication); } @Test(expected = IllegalArgumentException.class) public void setAnonymousAuthenticationNull() { interceptor.setAnonymousAuthentication(null); } @Test public void preSendUsesCustomAnonymous() throws Exception { expectedAnonymous = new AnonymousAuthenticationToken("customKey", "customAnonymous", AuthorityUtils.createAuthorityList("ROLE_CUSTOM")); interceptor.setAnonymousAuthentication(expectedAnonymous); interceptor.preSend(messageBuilder.build(), channel); assertAnonymous(); } // SEC-2845 @Test public void preSendUserNotAuthentication() throws Exception { messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, principal); interceptor.preSend(messageBuilder.build(), channel); assertAnonymous(); } // SEC-2845 @Test public void preSendUserNotSet() throws Exception { interceptor.preSend(messageBuilder.build(), channel); assertAnonymous(); } // SEC-2845 @Test public void preSendUserNotSetCustomAnonymous() throws Exception { interceptor.preSend(messageBuilder.build(), channel); assertAnonymous(); } @Test public void afterSendCompletion() throws Exception { SecurityContextHolder.getContext().setAuthentication(authentication); interceptor.afterSendCompletion(messageBuilder.build(), channel, true, null); assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); } @Test public void afterSendCompletionNullAuthentication() throws Exception { interceptor.afterSendCompletion(messageBuilder.build(), channel, true, null); assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); } @Test public void beforeHandleUserSet() throws Exception { messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, authentication); interceptor.beforeHandle(messageBuilder.build(), channel, handler); assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs( authentication); } // SEC-2845 @Test public void beforeHandleUserNotAuthentication() throws Exception { messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, principal); interceptor.beforeHandle(messageBuilder.build(), channel, handler); assertAnonymous(); } // SEC-2845 @Test public void beforeHandleUserNotSet() throws Exception { interceptor.beforeHandle(messageBuilder.build(), channel, handler); assertAnonymous(); } @Test public void afterMessageHandledUserNotSet() throws Exception { interceptor.afterMessageHandled(messageBuilder.build(), channel, handler, null); assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); } @Test public void afterMessageHandled() throws Exception { SecurityContextHolder.getContext().setAuthentication(authentication); interceptor.afterMessageHandled(messageBuilder.build(), channel, handler, null); assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); } // SEC-2829 @Test public void restoresOriginalContext() throws Exception { TestingAuthenticationToken original = new TestingAuthenticationToken("original", "original", "ROLE_USER"); SecurityContextHolder.getContext().setAuthentication(original); messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, authentication); interceptor.beforeHandle(messageBuilder.build(), channel, handler); assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs( authentication); interceptor.afterMessageHandled(messageBuilder.build(), channel, handler, null); assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs( original); } /** * If a user sends a websocket when processing another websocket * * @throws Exception */ @Test public void restoresOriginalContextNestedThreeDeep() throws Exception { AnonymousAuthenticationToken anonymous = new AnonymousAuthenticationToken("key", "anonymous", AuthorityUtils.createAuthorityList("ROLE_USER")); TestingAuthenticationToken origional = new TestingAuthenticationToken("original", "origional", "ROLE_USER"); SecurityContextHolder.getContext().setAuthentication(origional); messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, authentication); interceptor.beforeHandle(messageBuilder.build(), channel, handler); assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs( authentication); // start send websocket messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, null); interceptor.beforeHandle(messageBuilder.build(), channel, handler); assertThat(SecurityContextHolder.getContext().getAuthentication().getName()) .isEqualTo(anonymous.getName()); interceptor.afterMessageHandled(messageBuilder.build(), channel, handler, null); assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs( authentication); // end send websocket interceptor.afterMessageHandled(messageBuilder.build(), channel, handler, null); assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs( origional); } private void assertAnonymous() { Authentication currentAuthentication = SecurityContextHolder.getContext() .getAuthentication(); assertThat(currentAuthentication) .isInstanceOf(AnonymousAuthenticationToken.class); AnonymousAuthenticationToken anonymous = (AnonymousAuthenticationToken) currentAuthentication; assertThat(anonymous.getName()).isEqualTo(expectedAnonymous.getName()); assertThat(anonymous.getAuthorities()).containsOnlyElementsOf( expectedAnonymous.getAuthorities()); assertThat(anonymous.getKeyHash()).isEqualTo(expectedAnonymous.getKeyHash()); } }