GH-3897: Deprecate ChannelSecurityInterceptor (#3915)

* GH-3897: Deprecate `ChannelSecurityInterceptor`

Fixes https://github.com/spring-projects/spring-integration/issues/3897

Spring Security has deprecated `AccessDecisionManager` and all its infrastructure
in favor of `AuthorizationManager`

* Deprecate and AOP `ChannelSecurityInterceptor` and all its infrastructure,
including `@SecuredChannel` and respective XML configuration.
The `AuthorizationChannelInterceptor` added to respective channels for security
or configured as a global channel interceptor fully covers the previous AOP configuration
* Fix deprecation warnings in other tests with security

* Fix language in docs

Co-authored-by: Gary Russell <grussell@vmware.com>

* * Remove `forRemoval` attr from `@Deprecated` markers for Security classes:
looks like to mark `@Deprecated` and even `@SuppressWarnings("deprecation")`
don't silence warnings on compilation

Co-authored-by: Gary Russell <grussell@vmware.com>
This commit is contained in:
Artem Bilan
2022-10-17 18:32:17 -04:00
committed by GitHub
parent ff076b6a19
commit 64f0e738a9
26 changed files with 255 additions and 688 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 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.
@@ -26,7 +26,11 @@ import org.springframework.security.access.ConfigAttribute;
*
* @author Oleg Zhurakousky
* @since 2.0
*
* @deprecated since 6.0 in favor of literally
* {@code new AuthorizationChannelInterceptor(AuthorityAuthorizationManager.hasAnyRole())}
*/
@Deprecated(since = "6.0")
public interface ChannelAccessPolicy {
Collection<ConfigAttribute> getConfigAttributesForSend();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 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.
@@ -29,7 +29,11 @@ import org.springframework.util.Assert;
* is a <em>send</em> operation, the {@link Message} is also available.
*
* @author Mark Fisher
*
* @deprecated since 6.0 in favor of literally
* {@code new AuthorizationChannelInterceptor(AuthorityAuthorizationManager.hasAnyRole())}
*/
@Deprecated(since = "6.0")
public class ChannelInvocation {
private final MessageChannel channel;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 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.
@@ -31,8 +31,15 @@ import org.springframework.util.Assert;
*
* @author Mark Fisher
* @author Oleg Zhurakousky
*
* @see SecuredChannel
*
* @deprecated since 6.0 in favor of literally
* {@code new AuthorizationChannelInterceptor(AuthorityAuthorizationManager.hasAnyRole())}.
* However, the {@link org.springframework.security.messaging.access.intercept.AuthorizationChannelInterceptor}
* can be configured with any {@link org.springframework.security.authorization.AuthorizationManager} implementation.
*/
@Deprecated(since = "6.0")
public final class ChannelSecurityInterceptor extends AbstractSecurityInterceptor implements MethodInterceptor {
private final ChannelSecurityMetadataSource securityMetadataSource;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 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.
@@ -36,7 +36,11 @@ import org.springframework.util.Assert;
*
* @author Mark Fisher
* @author Oleg Zhurakousky
*
* @deprecated since 6.0 in favor of literally
* {@code new AuthorizationChannelInterceptor(AuthorityAuthorizationManager.hasAnyRole())}
*/
@Deprecated(since = "6.0")
public class ChannelSecurityMetadataSource implements SecurityMetadataSource {
private final Map<Pattern, ChannelAccessPolicy> patternMappings;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 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.
@@ -33,7 +33,11 @@ import org.springframework.util.StringUtils;
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Artem Bilan
*
* @deprecated since 6.0 in favor of literally
* {@code new AuthorizationChannelInterceptor(AuthorityAuthorizationManager.hasAnyRole())}
*/
@Deprecated(since = "6.0")
public class DefaultChannelAccessPolicy implements ChannelAccessPolicy {
private final Collection<ConfigAttribute> configAttributeDefinitionForSend;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2019 the original author or authors.
* Copyright 2015-2022 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.
@@ -33,7 +33,11 @@ import java.lang.annotation.Target;
*
* @author Artem Bilan
* @since 4.2
*
* @deprecated since 6.0 in favor of literally
* {@code new AuthorizationChannelInterceptor(AuthorityAuthorizationManager.hasAnyRole())}
*/
@Deprecated(since = "6.0")
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 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.
@@ -42,7 +42,11 @@ import org.springframework.messaging.MessageChannel;
* @author Oleg Zhurakousky
* @author Artem Bilan
* @author Gary Russell
* @deprecated since 6.0 in favor of literally
* {@code new AuthorizationChannelInterceptor(AuthorityAuthorizationManager.hasAnyRole())}
*/
@Deprecated(since = "6.0")
@SuppressWarnings("serial")
public class ChannelSecurityInterceptorBeanPostProcessor extends AbstractAutoProxyCreator {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 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.
@@ -22,7 +22,11 @@ import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHa
* Namespace handler for the security namespace.
*
* @author Jonas Partner
*
* @deprecated since 6.0 in favor of literally
* {@code new AuthorizationChannelInterceptor(AuthorityAuthorizationManager.hasAnyRole())}
*/
@Deprecated(since = "6.0", forRemoval = true)
public class IntegrationSecurityNamespaceHandler extends AbstractIntegrationNamespaceHandler {
public void init() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 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.
@@ -41,7 +41,11 @@ import org.springframework.util.xml.DomUtils;
* @author Jonas Partner
* @author Mark Fisher
* @author Artem Bilan
*
* @deprecated since 6.0 in favor of literally
* {@code new AuthorizationChannelInterceptor(AuthorityAuthorizationManager.hasAnyRole())}
*/
@Deprecated(since = "6.0")
public class SecuredChannelsParser extends AbstractSingleBeanDefinitionParser {
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2019 the original author or authors.
* Copyright 2014-2022 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.
@@ -43,7 +43,11 @@ import org.springframework.integration.security.channel.SecuredChannel;
* @author Artem Bilan
*
* @since 4.0
*
* @deprecated since 6.0 in favor of literally
* {@code new AuthorizationChannelInterceptor(AuthorityAuthorizationManager.hasAnyRole())}
*/
@Deprecated(since = "6.0", forRemoval = true)
public class SecurityIntegrationConfigurationInitializer implements IntegrationConfigurationInitializer {
private static final String CHANNEL_SECURITY_INTERCEPTOR_BPP_BEAN_NAME =

View File

@@ -1,43 +0,0 @@
/*
* Copyright 2002-2019 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
*
* https://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.integration.security;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
/**
* @author Oleg Zhurakousky
* @author Mark Fisher
* @since 2.0
*/
public class MockAuthenticationManager implements AuthenticationManager {
private final boolean grantAccess;
public MockAuthenticationManager(boolean grantAccess) {
this.grantAccess = grantAccess;
}
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (this.grantAccess) {
authentication.setAuthenticated(true);
}
return authentication;
}
}

View File

@@ -1,22 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:si-security="http://www.springframework.org/schema/integration/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
https://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/security
https://www.springframework.org/schema/integration/security/spring-integration-security.xsd
http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/task https://www.springframework.org/schema/task/spring-task.xsd">
<beans:import resource="classpath:org/springframework/integration/security/config/commonSecurityConfiguration.xml"/>
<security:user-service id="userDetailsService">
<security:user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN"/>
<security:user name="bob" password="bobspassword" authorities="ROLE_USER"/>
</security:user-service>
<si-security:secured-channels>
<si-security:access-policy pattern="securedChannel.*" send-access="ROLE_ADMIN, ROLE_PRESIDENT"/>
</si-security:secured-channels>
<channel-interceptor pattern="securedChannel*">
<beans:bean
class="org.springframework.security.messaging.access.intercept.AuthorizationChannelInterceptor">
<beans:constructor-arg>
<beans:bean class="org.springframework.security.authorization.AuthorityAuthorizationManager"
factory-method="hasAnyRole">
<beans:constructor-arg>
<beans:array>
<beans:value>ADMIN</beans:value>
<beans:value>PRESIDENT</beans:value>
</beans:array>
</beans:constructor-arg>
</beans:bean>
</beans:constructor-arg>
</beans:bean>
</channel-interceptor>
<beans:bean id="testHandler" class="org.springframework.integration.security.TestHandler"/>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 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.
@@ -17,10 +17,10 @@
package org.springframework.integration.security.channel;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -28,25 +28,22 @@ import org.springframework.integration.security.SecurityTestUtils;
import org.springframework.integration.security.TestHandler;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandlingException;
import org.springframework.messaging.MessageDeliveryException;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
/**
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Artem Bilan
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@SpringJUnitConfig
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class ChannelAdapterSecurityIntegrationTests {
@@ -78,59 +75,67 @@ public class ChannelAdapterSecurityIntegrationTests {
TestHandler testConsumer;
@After
@AfterEach
public void tearDown() {
SecurityContextHolder.clearContext();
}
@Test(expected = AccessDeniedException.class)
@Test
public void testSecuredWithNotEnoughPermission() {
login("bob", "bobspassword", "ROLE_ADMINA");
securedChannelAdapter.send(new GenericMessage<String>("test"));
assertThatExceptionOfType(MessageDeliveryException.class)
.isThrownBy(() -> this.securedChannelAdapter.send(new GenericMessage<>("test")))
.withRootCauseExactlyInstanceOf(AccessDeniedException.class);
}
@Test
public void testSecuredWithPermission() {
login("bob", "bobspassword", "ROLE_ADMIN", "ROLE_PRESIDENT");
securedChannelAdapter.send(new GenericMessage<String>("test"));
securedChannelAdapter2.send(new GenericMessage<String>("test"));
securedChannelAdapter.send(new GenericMessage<>("test"));
securedChannelAdapter2.send(new GenericMessage<>("test"));
assertThat(testConsumer.sentMessages.size()).as("Wrong size of message list in target").isEqualTo(2);
}
@Test
public void testSecurityContextPropagation() {
login("bob", "bobspassword", "ROLE_ADMIN", "ROLE_PRESIDENT");
this.queueChannel.send(new GenericMessage<String>("test"));
this.queueChannel.send(new GenericMessage<>("test"));
Message<?> receive = this.securedChannelQueue.receive(10000);
assertThat(receive).isNotNull();
SecurityContextHolder.clearContext();
this.queueChannel.send(new GenericMessage<String>("test"));
this.queueChannel.send(new GenericMessage<>("test"));
Message<?> errorMessage = this.errorChannel.receive(10000);
assertThat(errorMessage).isNotNull();
Object payload = errorMessage.getPayload();
assertThat(payload).isInstanceOf(MessageHandlingException.class);
assertThat(((MessageHandlingException) payload).getCause())
assertThat(payload).isInstanceOf(MessageDeliveryException.class);
assertThat(((MessageDeliveryException) payload).getCause())
.isInstanceOf(AuthenticationCredentialsNotFoundException.class);
}
@Test(expected = AccessDeniedException.class)
@Test
public void testSecuredWithoutPermission() {
login("bob", "bobspassword", "ROLE_USER");
securedChannelAdapter.send(new GenericMessage<String>("test"));
assertThatExceptionOfType(MessageDeliveryException.class)
.isThrownBy(() -> this.securedChannelAdapter.send(new GenericMessage<>("test")))
.withRootCauseExactlyInstanceOf(AccessDeniedException.class);
}
@Test(expected = AccessDeniedException.class)
@Test
public void testSecured2WithoutPermission() {
login("bob", "bobspassword", "ROLE_USER");
securedChannelAdapter2.send(new GenericMessage<String>("test"));
assertThatExceptionOfType(MessageDeliveryException.class)
.isThrownBy(() -> this.securedChannelAdapter2.send(new GenericMessage<>("test")))
.withRootCauseExactlyInstanceOf(AccessDeniedException.class);
}
@Test(expected = AuthenticationException.class)
@Test
public void testSecuredWithoutAuthenticating() {
securedChannelAdapter.send(new GenericMessage<String>("test"));
assertThatExceptionOfType(MessageDeliveryException.class)
.isThrownBy(() -> this.securedChannelAdapter.send(new GenericMessage<>("test")))
.withRootCauseExactlyInstanceOf(AuthenticationCredentialsNotFoundException.class);
}
@Test

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 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.
@@ -16,80 +16,67 @@
package org.springframework.integration.security.channel;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import org.junit.After;
import org.junit.Test;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.security.MockAuthenticationManager;
import org.springframework.integration.security.SecurityTestUtils;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageDeliveryException;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authorization.AuthorityAuthorizationManager;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.messaging.access.intercept.AuthorizationChannelInterceptor;
/**
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Artem Bilan
*/
public class ChannelSecurityInterceptorTests {
@After
@AfterEach
public void clearSecurityContext() {
SecurityContextHolder.clearContext();
}
@Test(expected = AuthenticationException.class)
public void securedSendWithoutAuthentication() throws Exception {
MessageChannel channel = getSecuredChannel("ROLE_ADMIN");
channel.send(new GenericMessage<String>("test"));
}
@Test(expected = AccessDeniedException.class)
public void securedSendWithoutRole() throws Exception {
MessageChannel channel = getSecuredChannel("ROLE_ADMIN");
SecurityContext context = SecurityTestUtils.createContext("test", "pwd", "ROLE_USER");
SecurityContextHolder.setContext(context);
channel.send(new GenericMessage<String>("test"));
@Test
public void securedSendWithoutAuthentication() {
MessageChannel channel = getSecuredChannel();
assertThatExceptionOfType(MessageDeliveryException.class)
.isThrownBy(() -> channel.send(new GenericMessage<>("test")))
.withRootCauseExactlyInstanceOf(AuthenticationCredentialsNotFoundException.class);
}
@Test
public void securedSendWithRole() throws Exception {
MessageChannel channel = getSecuredChannel("ROLE_ADMIN");
public void securedSendWithoutRole() {
MessageChannel channel = getSecuredChannel();
SecurityContext context = SecurityTestUtils.createContext("test", "pwd", "ROLE_USER");
SecurityContextHolder.setContext(context);
assertThatExceptionOfType(MessageDeliveryException.class)
.isThrownBy(() -> channel.send(new GenericMessage<>("test")))
.withRootCauseExactlyInstanceOf(AccessDeniedException.class);
}
@Test
public void securedSendWithRole() {
MessageChannel channel = getSecuredChannel();
SecurityContext context = SecurityTestUtils.createContext("test", "pwd", "ROLE_ADMIN");
SecurityContextHolder.setContext(context);
channel.send(new GenericMessage<String>("test"));
channel.send(new GenericMessage<>("test"));
}
private static MessageChannel getSecuredChannel(String role) throws Exception {
private static MessageChannel getSecuredChannel() {
QueueChannel channel = new QueueChannel();
channel.setBeanName("securedChannel");
ProxyFactory proxyFactory = new ProxyFactory(channel);
proxyFactory.addAdvice(createInterceptor(role));
return (MessageChannel) proxyFactory.getProxy();
}
@SuppressWarnings("rawtypes")
private static ChannelSecurityInterceptor createInterceptor(String role) throws Exception {
ChannelSecurityMetadataSource securityMetadataSource = new ChannelSecurityMetadataSource();
securityMetadataSource.addPatternMapping(Pattern.compile("secured.*"), new DefaultChannelAccessPolicy(role, null));
ChannelSecurityInterceptor interceptor = new ChannelSecurityInterceptor(securityMetadataSource);
AffirmativeBased accessDecisionManager = AffirmativeBased.class.getConstructor(List.class)
.newInstance(Collections.singletonList(new RoleVoter()));
accessDecisionManager.afterPropertiesSet();
interceptor.setAccessDecisionManager(accessDecisionManager);
interceptor.setAuthenticationManager(new MockAuthenticationManager(true));
interceptor.afterPropertiesSet();
return interceptor;
channel.addInterceptor(new AuthorizationChannelInterceptor(AuthorityAuthorizationManager.hasRole("ADMIN")));
return channel;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2019 the original author or authors.
* Copyright 2014-2022 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.
@@ -17,20 +17,19 @@
package org.springframework.integration.security.config;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
@@ -50,13 +49,11 @@ import org.springframework.integration.config.GlobalChannelInterceptor;
import org.springframework.integration.handler.BridgeHandler;
import org.springframework.integration.security.SecurityTestUtils;
import org.springframework.integration.security.TestHandler;
import org.springframework.integration.security.channel.ChannelSecurityInterceptor;
import org.springframework.integration.security.channel.SecuredChannel;
import org.springframework.integration.security.channel.SecurityContextPropagationChannelInterceptor;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageDeliveryException;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessageHandlingException;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.SubscribableChannel;
@@ -64,27 +61,27 @@ import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.authorization.AuthorityAuthorizationManager;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.messaging.access.intercept.AuthorizationChannelInterceptor;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.task.DelegatingSecurityContextAsyncTaskExecutor;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
/**
* @author Artem Bilan
*
* @since 4.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@SpringJUnitConfig
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class ChannelSecurityInterceptorSecuredChannelAnnotationTests {
public class ChannelSecurityAnnotationTests {
@Autowired
MessageChannel securedChannel;
@@ -125,96 +122,104 @@ public class ChannelSecurityInterceptorSecuredChannelAnnotationTests {
@Autowired
TestGateway testGateway;
@After
@AfterEach
public void tearDown() {
SecurityContextHolder.clearContext();
}
@Test(expected = AccessDeniedException.class)
@Test
public void testSecuredWithNotEnoughPermission() {
login("bob", "bobspassword", "ROLE_ADMINA");
securedChannel.send(new GenericMessage<String>("test"));
assertThatExceptionOfType(MessageDeliveryException.class)
.isThrownBy(() -> this.securedChannel.send(new GenericMessage<>("test")))
.withRootCauseExactlyInstanceOf(AccessDeniedException.class);
}
@Test
public void testSecuredWithPermission() {
login("bob", "bobspassword", "ROLE_ADMIN", "ROLE_PRESIDENT");
securedChannel.send(new GenericMessage<String>("test"));
securedChannel2.send(new GenericMessage<String>("test"));
securedChannel.send(new GenericMessage<>("test"));
securedChannel2.send(new GenericMessage<>("test"));
assertThat(testConsumer.sentMessages.size()).as("Wrong size of message list in target").isEqualTo(2);
}
@Test(expected = AccessDeniedException.class)
@Test
public void testSecuredWithoutPermision() {
login("bob", "bobspassword", "ROLE_USER");
securedChannel.send(new GenericMessage<String>("test"));
assertThatExceptionOfType(MessageDeliveryException.class)
.isThrownBy(() -> this.securedChannel.send(new GenericMessage<>("test")))
.withRootCauseExactlyInstanceOf(AccessDeniedException.class);
}
@Test(expected = AccessDeniedException.class)
@Test
public void testSecured2WithoutPermision() {
login("bob", "bobspassword", "ROLE_USER");
securedChannel2.send(new GenericMessage<String>("test"));
assertThatExceptionOfType(MessageDeliveryException.class)
.isThrownBy(() -> this.securedChannel2.send(new GenericMessage<>("test")))
.withRootCauseExactlyInstanceOf(AccessDeniedException.class);
}
@Test(expected = AuthenticationException.class)
@Test
public void testSecuredWithoutAuthenticating() {
securedChannel.send(new GenericMessage<String>("test"));
assertThatExceptionOfType(MessageDeliveryException.class)
.isThrownBy(() -> this.securedChannel2.send(new GenericMessage<>("test")))
.withRootCauseExactlyInstanceOf(AuthenticationCredentialsNotFoundException.class);
}
@Test
public void testUnsecuredAsAdmin() {
login("bob", "bobspassword", "ROLE_ADMIN");
unsecuredChannel.send(new GenericMessage<String>("test"));
unsecuredChannel.send(new GenericMessage<>("test"));
assertThat(testConsumer.sentMessages.size()).as("Wrong size of message list in target").isEqualTo(1);
}
@Test
public void testUnsecuredAsUser() {
login("bob", "bobspassword", "ROLE_USER");
unsecuredChannel.send(new GenericMessage<String>("test"));
unsecuredChannel.send(new GenericMessage<>("test"));
assertThat(testConsumer.sentMessages.size()).as("Wrong size of message list in target").isEqualTo(1);
}
@Test
public void testUnsecuredWithoutAuthenticating() {
unsecuredChannel.send(new GenericMessage<String>("test"));
unsecuredChannel.send(new GenericMessage<>("test"));
assertThat(testConsumer.sentMessages.size()).as("Wrong size of message list in target").isEqualTo(1);
}
@Test
public void testSecurityContextPropagationQueueChannel() {
login("bob", "bobspassword", "ROLE_ADMIN", "ROLE_PRESIDENT");
this.queueChannel.send(new GenericMessage<String>("test"));
this.queueChannel.send(new GenericMessage<>("test"));
Message<?> receive = this.securedChannelQueue.receive(10000);
assertThat(receive).isNotNull();
SecurityContextHolder.clearContext();
this.queueChannel.send(new GenericMessage<String>("test"));
this.queueChannel.send(new GenericMessage<>("test"));
Message<?> errorMessage = this.errorChannel.receive(10000);
assertThat(errorMessage).isNotNull();
Object payload = errorMessage.getPayload();
assertThat(payload).isInstanceOf(MessageHandlingException.class);
assertThat(((MessageHandlingException) payload).getCause())
assertThat(payload).isInstanceOf(MessageDeliveryException.class);
assertThat(((MessageDeliveryException) payload).getCause())
.isInstanceOf(AuthenticationCredentialsNotFoundException.class);
}
@Test
public void testSecurityContextPropagationExecutorChannel() {
login("bob", "bobspassword", "ROLE_ADMIN", "ROLE_PRESIDENT");
this.executorChannel.send(new GenericMessage<String>("test"));
this.executorChannel.send(new GenericMessage<>("test"));
Message<?> receive = this.securedChannelQueue.receive(10000);
assertThat(receive).isNotNull();
SecurityContextHolder.clearContext();
this.executorChannel.send(new GenericMessage<String>("test"));
this.executorChannel.send(new GenericMessage<>("test"));
Message<?> errorMessage = this.errorChannel.receive(10000);
assertThat(errorMessage).isNotNull();
Object payload = errorMessage.getPayload();
assertThat(payload).isInstanceOf(MessageHandlingException.class);
assertThat(((MessageHandlingException) payload).getCause())
assertThat(payload).isInstanceOf(MessageDeliveryException.class);
assertThat(((MessageDeliveryException) payload).getCause())
.isInstanceOf(AuthenticationCredentialsNotFoundException.class);
}
@@ -222,7 +227,7 @@ public class ChannelSecurityInterceptorSecuredChannelAnnotationTests {
public void testSecurityContextPropagationPublishSubscribeChannel() {
login("bob", "bobspassword", "ROLE_ADMIN", "ROLE_PRESIDENT");
this.publishSubscribeChannel.send(new GenericMessage<String>("test"));
this.publishSubscribeChannel.send(new GenericMessage<>("test"));
Message<?> receive = this.securedChannelQueue.receive(10000);
assertThat(receive).isNotNull();
@@ -236,7 +241,7 @@ public class ChannelSecurityInterceptorSecuredChannelAnnotationTests {
this.publishSubscribeChannel.setApplySequence(true);
this.publishSubscribeChannel.send(new GenericMessage<String>("test"));
this.publishSubscribeChannel.send(new GenericMessage<>("test"));
receive = this.securedChannelQueue.receive(10000);
assertThat(receive).isNotNull();
@@ -252,12 +257,12 @@ public class ChannelSecurityInterceptorSecuredChannelAnnotationTests {
SecurityContextHolder.clearContext();
this.publishSubscribeChannel.send(new GenericMessage<String>("test"));
this.publishSubscribeChannel.send(new GenericMessage<>("test"));
Message<?> errorMessage = this.errorChannel.receive(10000);
assertThat(errorMessage).isNotNull();
Object payload = errorMessage.getPayload();
assertThat(payload).isInstanceOf(MessageHandlingException.class);
assertThat(((MessageHandlingException) payload).getCause())
assertThat(payload).isInstanceOf(MessageDeliveryException.class);
assertThat(((MessageDeliveryException) payload).getCause())
.isInstanceOf(AuthenticationCredentialsNotFoundException.class);
}
@@ -285,22 +290,38 @@ public class ChannelSecurityInterceptorSecuredChannelAnnotationTests {
@Configuration
@EnableIntegration
@IntegrationComponentScan
@ImportResource("classpath:org/springframework/integration/security/config/commonSecurityConfiguration.xml")
public static class ContextConfiguration {
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager(
User.withUsername("jimi")
.password("jimispassword")
.authorities("ROLE_USER", "ROLE_ADMIN")
.build(),
User.withUsername("bob")
.password("bobspassword")
.authorities("ROLE_USER")
.build());
}
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
@SecuredChannel(interceptor = "channelSecurityInterceptor", sendAccess = {"ROLE_ADMIN", "ROLE_PRESIDENT"})
@GlobalChannelInterceptor(patterns = "secured*")
AuthorizationChannelInterceptor authorizationChannelInterceptor() {
return new AuthorizationChannelInterceptor(AuthorityAuthorizationManager.hasAnyRole("ADMIN", "PRESIDENT"));
}
@Bean
public SubscribableChannel securedChannel() {
return new DirectChannel();
}
@Bean
@SecuredChannel(interceptor = "channelSecurityInterceptor", sendAccess = {"ROLE_ADMIN", "ROLE_PRESIDENT"})
public SubscribableChannel securedChannel2() {
return new DirectChannel();
}
@@ -326,7 +347,6 @@ public class ChannelSecurityInterceptorSecuredChannelAnnotationTests {
}
@Bean
@SecuredChannel(interceptor = "channelSecurityInterceptor", sendAccess = {"ROLE_ADMIN", "ROLE_PRESIDENT"})
public PollableChannel securedChannelQueue() {
return new QueueChannel();
}
@@ -353,7 +373,6 @@ public class ChannelSecurityInterceptorSecuredChannelAnnotationTests {
}
@Bean
@SecuredChannel(interceptor = "channelSecurityInterceptor", sendAccess = {"ROLE_ADMIN", "ROLE_PRESIDENT"})
public PollableChannel securedChannelQueue2() {
return new QueueChannel();
}
@@ -386,15 +405,6 @@ public class ChannelSecurityInterceptorSecuredChannelAnnotationTests {
return testHandler;
}
@Bean
public ChannelSecurityInterceptor channelSecurityInterceptor(AuthenticationManager authenticationManager,
AccessDecisionManager accessDecisionManager) {
ChannelSecurityInterceptor channelSecurityInterceptor = new ChannelSecurityInterceptor();
channelSecurityInterceptor.setAuthenticationManager(authenticationManager);
channelSecurityInterceptor.setAccessDecisionManager(accessDecisionManager);
return channelSecurityInterceptor;
}
@Bean
public AsyncTaskExecutor securityContextExecutor() {
return new DelegatingSecurityContextAsyncTaskExecutor(new SimpleAsyncTaskExecutor());

View File

@@ -1,16 +0,0 @@
<?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:security="http://www.springframework.org/schema/integration/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/security
https://www.springframework.org/schema/integration/security/spring-integration-security.xsd">
<import resource="classpath:org/springframework/integration/security/config/commonSecurityConfiguration.xml"/>
<security:secured-channels>
<security:access-policy pattern="test" send-access="ROLE_ADMIN"/>
</security:secured-channels>
</beans>

View File

@@ -1,81 +0,0 @@
/*
* Copyright 2002-2019 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
*
* https://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.integration.security.config;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.integration.channel.MessagePublishingErrorHandler;
import org.springframework.integration.channel.NullChannel;
import org.springframework.integration.channel.PublishSubscribeChannel;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.MessageChannel;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.ErrorHandler;
/**
* @author Mark Fisher
* @author Artem Bilan
* @author Gary Russell
* @since 1.0.3
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class DefaultConfigurationTests {
@Autowired
private ApplicationContext context;
@Test
public void verifyErrorChannel() {
Object errorChannel = context.getBean("errorChannel");
assertThat(errorChannel).isNotNull();
assertThat(errorChannel.getClass()).isEqualTo(PublishSubscribeChannel.class);
}
@Test
public void verifyNullChannel() {
Object nullChannel = context.getBean("nullChannel");
assertThat(nullChannel).isNotNull();
assertThat(nullChannel.getClass()).isEqualTo(NullChannel.class);
}
@Test
public void verifyTaskScheduler() {
Object taskScheduler = context.getBean(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME);
assertThat(taskScheduler.getClass()).isEqualTo(ThreadPoolTaskScheduler.class);
ErrorHandler errorHandler = TestUtils.getPropertyValue(taskScheduler, "errorHandler", ErrorHandler.class);
assertThat(errorHandler.getClass()).isEqualTo(MessagePublishingErrorHandler.class);
MessageChannel defaultErrorChannel = TestUtils.getPropertyValue(errorHandler,
"messagingTemplate.defaultDestination", MessageChannel.class);
assertThat(defaultErrorChannel).isNull();
errorHandler.handleError(new Throwable());
defaultErrorChannel = TestUtils.getPropertyValue(errorHandler, "messagingTemplate.defaultDestination",
MessageChannel.class);
assertThat(defaultErrorChannel).isNotNull();
assertThat(defaultErrorChannel).isEqualTo(context.getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME));
}
}

View File

@@ -1,29 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:si-security="http://www.springframework.org/schema/integration/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/integration
https://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/security
https://www.springframework.org/schema/integration/security/spring-integration-security.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<beans:import resource="classpath:org/springframework/integration/security/config/commonSecurityConfiguration.xml"/>
<si-security:secured-channels>
<si-security:access-policy pattern="adminRequiredForSend" send-access="ROLE_ADMIN"/>
<si-security:access-policy pattern="adminOrUserRequiredForSend" send-access="ROLE_ADMIN, ROLE_USER"/>
<si-security:access-policy pattern="adminRequiredForReceive" receive-access="ROLE_ADMIN"/>
<si-security:access-policy pattern="adminOrUserRequiredForReceive" receive-access="ROLE_ADMIN, ROLE_USER"/>
<si-security:access-policy pattern="adminRequiredForSendAndReceive" send-access="ROLE_ADMIN" receive-access="ROLE_ADMIN"/>
</si-security:secured-channels>
</beans:beans>

View File

@@ -1,216 +0,0 @@
/*
* Copyright 2002-2019 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
*
* https://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.integration.security.config;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.junit.Before;
import org.junit.Test;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.integration.channel.AbstractPollableChannel;
import org.springframework.integration.core.MessageSelector;
import org.springframework.integration.security.channel.ChannelAccessPolicy;
import org.springframework.integration.security.channel.ChannelSecurityInterceptor;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
/**
* @author Jonas Partner
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
@ContextConfiguration
public class SecuredChannelsParserTests extends AbstractJUnit4SpringContextTests {
TestMessageChannel messageChannel;
@Before
public void setUp() {
messageChannel = new TestMessageChannel();
}
@Test
public void testAdminRequiredForSend() {
String beanName = "adminRequiredForSend";
messageChannel.setBeanName(beanName);
MessageChannel proxy = (MessageChannel) applicationContext.getAutowireCapableBeanFactory()
.applyBeanPostProcessorsAfterInitialization(messageChannel, beanName);
assertThat(AopUtils.isAopProxy(proxy)).as("Channel was not proxied").isTrue();
Advisor[] advisors = ((Advised) proxy).getAdvisors();
assertThat(advisors.length).as("Wrong number of interceptors").isEqualTo(1);
ChannelSecurityInterceptor interceptor = (ChannelSecurityInterceptor) advisors[0].getAdvice();
ChannelAccessPolicy policy = this.retrievePolicyForPatternString(beanName, interceptor);
assertThat(policy).as("Pattern '" + beanName + "' is not included in mappings").isNotNull();
Collection<ConfigAttribute> sendDefinition = policy.getConfigAttributesForSend();
Collection<ConfigAttribute> receiveDefinition = policy.getConfigAttributesForReceive();
assertThat(this.getRolesFromDefintion(sendDefinition).contains("ROLE_ADMIN"))
.as("ROLE_ADMIN not found as send attribute").isTrue();
assertThat(receiveDefinition.size() == 0).as("Policy applies to receive").isTrue();
}
@Test
public void testAdminOrUserRequiredForSend() {
String beanName = "adminOrUserRequiredForSend";
messageChannel.setBeanName(beanName);
MessageChannel proxy = (MessageChannel) applicationContext.getAutowireCapableBeanFactory()
.applyBeanPostProcessorsAfterInitialization(messageChannel, beanName);
assertThat(AopUtils.isAopProxy(proxy)).as("Channel was not proxied").isTrue();
Advisor[] advisors = ((Advised) proxy).getAdvisors();
assertThat(advisors.length).as("Wrong number of interceptors").isEqualTo(1);
ChannelSecurityInterceptor interceptor = (ChannelSecurityInterceptor) advisors[0].getAdvice();
ChannelAccessPolicy policy = this.retrievePolicyForPatternString(beanName, interceptor);
assertThat(policy).as("Pattern '" + beanName + "' is not included in mappings").isNotNull();
Collection<ConfigAttribute> sendDefinition = policy.getConfigAttributesForSend();
Collection<ConfigAttribute> receiveDefinition = policy.getConfigAttributesForReceive();
Collection<String> sendRoles = this.getRolesFromDefintion(sendDefinition);
assertThat(sendRoles.contains("ROLE_ADMIN")).as("ROLE_ADMIN not found as send attribute").isTrue();
assertThat(sendRoles.contains("ROLE_USER")).as("ROLE_USER not found as send attribute").isTrue();
assertThat(receiveDefinition.size() == 0).as("Policy applies to receive").isTrue();
}
@Test
public void testAdminRequiredForReceive() {
String beanName = "adminRequiredForReceive";
messageChannel.setBeanName(beanName);
MessageChannel proxy = (MessageChannel) applicationContext.getAutowireCapableBeanFactory()
.applyBeanPostProcessorsAfterInitialization(messageChannel, beanName);
assertThat(AopUtils.isAopProxy(proxy)).as("Channel was not proxied").isTrue();
Advisor[] advisors = ((Advised) proxy).getAdvisors();
assertThat(advisors.length).as("Wrong number of interceptors").isEqualTo(1);
ChannelSecurityInterceptor interceptor = (ChannelSecurityInterceptor) advisors[0].getAdvice();
ChannelAccessPolicy policy = this.retrievePolicyForPatternString(beanName, interceptor);
assertThat(policy).as("Pattern '" + beanName + "' is not included in mappings").isNotNull();
Collection<ConfigAttribute> sendDefinition = policy.getConfigAttributesForSend();
Collection<ConfigAttribute> receiveDefinition = policy.getConfigAttributesForReceive();
Collection<String> receiveRoles = this.getRolesFromDefintion(receiveDefinition);
assertThat(receiveRoles.contains("ROLE_ADMIN")).as("ROLE_ADMIN not found as receive attribute").isTrue();
assertThat(sendDefinition.size() == 0).as("Policy applies to receive").isTrue();
}
@Test
public void testAdminOrUserRequiredForReceive() {
String beanName = "adminOrUserRequiredForReceive";
messageChannel.setBeanName(beanName);
MessageChannel proxy = (MessageChannel) applicationContext.getAutowireCapableBeanFactory()
.applyBeanPostProcessorsAfterInitialization(messageChannel, beanName);
assertThat(AopUtils.isAopProxy(proxy)).as("Channel was not proxied").isTrue();
Advisor[] advisors = ((Advised) proxy).getAdvisors();
assertThat(advisors.length).as("Wrong number of interceptors").isEqualTo(1);
ChannelSecurityInterceptor interceptor = (ChannelSecurityInterceptor) advisors[0].getAdvice();
ChannelAccessPolicy policy = this.retrievePolicyForPatternString(beanName, interceptor);
assertThat(policy).as("Pattern '" + beanName + "' is not included in mappings").isNotNull();
Collection<ConfigAttribute> sendDefinition = policy.getConfigAttributesForSend();
Collection<ConfigAttribute> receiveDefinition = policy.getConfigAttributesForReceive();
Collection<String> receiveRoles = this.getRolesFromDefintion(receiveDefinition);
assertThat(receiveRoles.contains("ROLE_ADMIN")).as("ROLE_ADMIN not found as receive attribute").isTrue();
assertThat(receiveRoles.contains("ROLE_USER")).as("ROLE_USER not found as receive attribute").isTrue();
assertThat(sendDefinition.size() == 0).as("Policy applies to receive").isTrue();
}
@Test
public void testAdminRequiredForSendAndReceive() {
String beanName = "adminRequiredForSendAndReceive";
messageChannel.setBeanName(beanName);
MessageChannel proxy = (MessageChannel) applicationContext.getAutowireCapableBeanFactory()
.applyBeanPostProcessorsAfterInitialization(messageChannel, beanName);
assertThat(AopUtils.isAopProxy(proxy)).as("Channel was not proxied").isTrue();
Advisor[] advisors = ((Advised) proxy).getAdvisors();
assertThat(advisors.length).as("Wrong number of interceptors").isEqualTo(1);
ChannelSecurityInterceptor interceptor = (ChannelSecurityInterceptor) advisors[0].getAdvice();
ChannelAccessPolicy policy = this.retrievePolicyForPatternString(beanName, interceptor);
assertThat(policy).as("Pattern '" + beanName + "' is not included in mappings").isNotNull();
Collection<ConfigAttribute> sendDefinition = policy.getConfigAttributesForSend();
Collection<ConfigAttribute> receiveDefinition = policy.getConfigAttributesForReceive();
assertThat(sendDefinition).as("Pattern does not apply to 'send'").isNotNull();
assertThat(receiveDefinition).as("Pattern does not apply to 'receive'").isNotNull();
Collection<String> sendRoles = this.getRolesFromDefintion(sendDefinition);
Collection<String> receiveRoles = this.getRolesFromDefintion(receiveDefinition);
assertThat(sendRoles.contains("ROLE_ADMIN")).as("ROLE_ADMIN not found in send attributes").isTrue();
assertThat(receiveRoles.contains("ROLE_ADMIN")).as("ROLE_ADMIN not found in receive attributes").isTrue();
}
@SuppressWarnings("unchecked")
private ChannelAccessPolicy retrievePolicyForPatternString(String patternString, ChannelSecurityInterceptor interceptor) {
DirectFieldAccessor accessor = new DirectFieldAccessor(interceptor.obtainSecurityMetadataSource());
Map<Pattern, ChannelAccessPolicy> policies = (Map<Pattern, ChannelAccessPolicy>) accessor.getPropertyValue("patternMappings");
for (Map.Entry<Pattern, ChannelAccessPolicy> entry : policies.entrySet()) {
if (entry.getKey().pattern().equals(patternString)) {
return entry.getValue();
}
}
return null;
}
private Collection<String> getRolesFromDefintion(Collection<ConfigAttribute> definition) {
Set<String> roles = new HashSet<String>();
//Collection configAttributes = SecurityConfig.createListFromCommaDelimitedString(definition);
for (ConfigAttribute nextConfigAttribute : definition) {
ConfigAttribute attribute = nextConfigAttribute;
roles.add(attribute.getAttribute());
}
return roles;
}
static class TestMessageChannel extends AbstractPollableChannel {
List<ChannelInterceptor> interceptors = new ArrayList<ChannelInterceptor>();
@Override
protected Message<?> doReceive(long timeout) {
return null;
}
@Override
protected boolean doSend(Message<?> message, long timeout) {
return false;
}
public List<Message<?>> clear() {
return null;
}
public List<Message<?>> purge(MessageSelector selector) {
return null;
}
@Override
public void addInterceptor(ChannelInterceptor interceptor) {
interceptors.add(interceptor);
}
}
}

View File

@@ -1,31 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<beans:bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<beans:constructor-arg>
<beans:bean class="org.springframework.security.access.vote.RoleVoter"/>
</beans:constructor-arg>
<beans:property name="allowIfAllAbstainDecisions" value="true"/>
</beans:bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider user-service-ref="userDetailsService"/>
</security:authentication-manager>
<security:user-service id="userDetailsService">
<security:user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN"/>
<security:user name="bob" password="bobspassword" authorities="ROLE_USER"/>
</security:user-service>
</beans:beans>