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:
13
build.gradle
13
build.gradle
@@ -169,6 +169,7 @@ allprojects {
|
||||
mavenBom "org.apache.camel:camel-bom:$camelVersion"
|
||||
mavenBom "org.testcontainers:testcontainers-bom:$testcontainersVersion"
|
||||
mavenBom "org.apache.groovy:groovy-bom:$groovyVersion"
|
||||
mavenBom "org.springframework.security:spring-security-bom:$springSecurityVersion"
|
||||
mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:$kotlinCoroutinesVersion"
|
||||
}
|
||||
|
||||
@@ -675,10 +676,10 @@ project('spring-integration-http') {
|
||||
|
||||
testImplementation project(':spring-integration-security')
|
||||
testImplementation "org.hamcrest:hamcrest-core:$hamcrestVersion"
|
||||
testImplementation("org.springframework.security:spring-security-config:$springSecurityVersion") {
|
||||
testImplementation('org.springframework.security:spring-security-config') {
|
||||
exclude group: 'org.springframework'
|
||||
}
|
||||
testImplementation("org.springframework.security:spring-security-test:$springSecurityVersion") {
|
||||
testImplementation('org.springframework.security:spring-security-test') {
|
||||
exclude group: 'org.springframework'
|
||||
}
|
||||
testImplementation 'com.fasterxml.jackson.core:jackson-databind'
|
||||
@@ -879,11 +880,11 @@ project('spring-integration-security') {
|
||||
description = 'Spring Integration Security Support'
|
||||
dependencies {
|
||||
api project(':spring-integration-core')
|
||||
api("org.springframework.security:spring-security-core:$springSecurityVersion") {
|
||||
api('org.springframework.security:spring-security-messaging') {
|
||||
exclude group: 'org.springframework'
|
||||
}
|
||||
|
||||
testImplementation("org.springframework.security:spring-security-config:$springSecurityVersion") {
|
||||
testImplementation('org.springframework.security:spring-security-config') {
|
||||
exclude group: 'org.springframework'
|
||||
}
|
||||
}
|
||||
@@ -971,10 +972,10 @@ project('spring-integration-webflux') {
|
||||
testImplementation "jakarta.servlet:jakarta.servlet-api:$servletApiVersion"
|
||||
testImplementation "org.hamcrest:hamcrest-core:$hamcrestVersion"
|
||||
testImplementation 'org.springframework:spring-webmvc'
|
||||
testImplementation("org.springframework.security:spring-security-config:$springSecurityVersion") {
|
||||
testImplementation('org.springframework.security:spring-security-config') {
|
||||
exclude group: 'org.springframework'
|
||||
}
|
||||
testImplementation("org.springframework.security:spring-security-test:$springSecurityVersion") {
|
||||
testImplementation('org.springframework.security:spring-security-test') {
|
||||
exclude group: 'org.springframework'
|
||||
}
|
||||
testImplementation 'com.fasterxml.jackson.core:jackson-databind'
|
||||
|
||||
@@ -27,7 +27,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -52,22 +51,18 @@ import org.springframework.integration.dsl.context.IntegrationFlowContext;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.http.multipart.UploadedMultipartFile;
|
||||
import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler;
|
||||
import org.springframework.integration.security.channel.ChannelSecurityInterceptor;
|
||||
import org.springframework.integration.security.channel.SecuredChannel;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.messaging.support.ErrorMessage;
|
||||
import org.springframework.mock.web.MockPart;
|
||||
import org.springframework.security.access.AccessDecisionManager;
|
||||
import org.springframework.security.access.vote.AffirmativeBased;
|
||||
import org.springframework.security.access.vote.RoleVoter;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.authorization.AuthorityAuthorizationManager;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
|
||||
import org.springframework.security.messaging.access.intercept.AuthorizationChannelInterceptor;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
@@ -335,9 +330,11 @@ public class HttpDslTests {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SecuredChannel(interceptor = "channelSecurityInterceptor", sendAccess = "ROLE_ADMIN")
|
||||
public MessageChannel transformSecuredChannel() {
|
||||
return new DirectChannel();
|
||||
DirectChannel directChannel = new DirectChannel();
|
||||
directChannel.addInterceptor(
|
||||
new AuthorizationChannelInterceptor(AuthorityAuthorizationManager.hasRole("ADMIN")));
|
||||
return directChannel;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -393,21 +390,6 @@ public class HttpDslTests {
|
||||
return new StandardServletMultipartResolver();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AccessDecisionManager accessDecisionManager() {
|
||||
return new AffirmativeBased(Collections.singletonList(new RoleVoter()));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ChannelSecurityInterceptor channelSecurityInterceptor(AccessDecisionManager accessDecisionManager,
|
||||
AuthenticationManagerBuilder authenticationManagerBuilder) {
|
||||
|
||||
ChannelSecurityInterceptor channelSecurityInterceptor = new ChannelSecurityInterceptor();
|
||||
channelSecurityInterceptor.setAuthenticationManager(authenticationManagerBuilder.getOrBuild());
|
||||
channelSecurityInterceptor.setAccessDecisionManager(accessDecisionManager);
|
||||
return channelSecurityInterceptor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Validator customValidator() {
|
||||
return new TestModelValidator();
|
||||
|
||||
@@ -111,12 +111,6 @@ public class CookieTests {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public String getMethodValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ClientHttpResponse execute() {
|
||||
allHeaders.add(headers);
|
||||
return new ClientHttpResponse() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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"/>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
@@ -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>
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
@@ -28,7 +28,6 @@ import java.util.Collections;
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
@@ -59,9 +58,6 @@ import org.springframework.integration.webflux.outbound.WebFluxRequestExecutingM
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.security.access.AccessDecisionManager;
|
||||
import org.springframework.security.access.vote.AffirmativeBased;
|
||||
import org.springframework.security.access.vote.RoleVoter;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
@@ -294,7 +290,6 @@ public class WebFluxDslTests {
|
||||
private Validator validator;
|
||||
|
||||
@Test
|
||||
@Disabled("Fails after some recent SF change")
|
||||
public void testValidation() {
|
||||
IntegrationFlow flow =
|
||||
IntegrationFlow.from(
|
||||
@@ -482,11 +477,6 @@ public class WebFluxDslTests {
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AccessDecisionManager accessDecisionManager() {
|
||||
return new AffirmativeBased(Collections.singletonList(new RoleVoter()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class TestModel {
|
||||
|
||||
@@ -7,6 +7,10 @@ Messaging independence and loose coupling let target systems communicate with ea
|
||||
We can either trust all those messages or secure our service against "`infecting`" messages.
|
||||
|
||||
Spring Integration, together with https://projects.spring.io/spring-security/[Spring Security], provides a simple and comprehensive way to secure message channels, as well as other part of the integration solution.
|
||||
Starting with version 6.0, the `ChannelSecurityInterceptor` as well as its configuration via `@SecuredChannel` annotation and XML `<secured-channels>` have been deprecation in favor of using `AuthorizationChannelInterceptor` from the `spring-security-messaging` module.
|
||||
The respective `AuthorizationManager` infrastructure fully covers the previously supported role-based authentication, plus it allows the configuration of any other possible authorization strategies.
|
||||
|
||||
The only remaining Spring Integration feature is a `SecurityContextPropagationChannelInterceptor` which may be promoted to the mentioned `spring-security-messaging` module in the future as well.
|
||||
|
||||
You need to include this dependency into your project:
|
||||
|
||||
@@ -30,93 +34,41 @@ compile "org.springframework.integration:spring-integration-security:{project-ve
|
||||
[[securing-channels]]
|
||||
=== Securing channels
|
||||
|
||||
Spring Integration provides the `ChannelSecurityInterceptor` interceptor, which extends `AbstractSecurityInterceptor` and intercepts send and receive calls on the channel.
|
||||
Access decisions are then made with reference to a `ChannelSecurityMetadataSource`, which provides the metadata that describes the `send()` and `receive()` access policies for certain channels.
|
||||
The interceptor requires that a valid `SecurityContext` has been established by authenticating with Spring Security.
|
||||
See the https://docs.spring.io/spring-security/reference/[Spring Security Reference Guide] for details.
|
||||
|
||||
Spring Integration provides Namespace support to allow easy configuration of security constraints.
|
||||
This support consists of the secured channels tag, which allows definition of one or more channel name patterns in conjunction with a definition of the security configuration for `send()` and `receive()`.
|
||||
The pattern is a `java.util.regexp.Pattern`.
|
||||
|
||||
The following example shows how to configure a bean that includes security and how to set up policies with patterns:
|
||||
To secure message channels in the integration flow, an `AuthorizationChannelInterceptor` has to be added to those channels, or it can be configured as a global channel interceptor with respective pattern:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
[source, java, role="primary"]
|
||||
.Java
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-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"
|
||||
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">
|
||||
|
||||
<int-security:secured-channels>
|
||||
<int-security:access-policy pattern="admin.*" send-access="ROLE_ADMIN"/>
|
||||
<int-security:access-policy pattern="user.*" receive-access="ROLE_USER"/>
|
||||
</int-security:secured-channels>
|
||||
----
|
||||
====
|
||||
|
||||
By default, the `secured-channels` namespace element expects a bean named `authenticationManager` (which implements `AuthenticationManager`) and a bean named `accessDecisionManager` (which implements `AccessDecisionManager`).
|
||||
Where this is not the case, references to the appropriate beans can be configured as attributes of the `secured-channels` element, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<int-security:secured-channels access-decision-manager="customAccessDecisionManager"
|
||||
authentication-manager="customAuthenticationManager">
|
||||
<int-security:access-policy pattern="admin.*" send-access="ROLE_ADMIN"/>
|
||||
<int-security:access-policy pattern="user.*" receive-access="ROLE_USER"/>
|
||||
</int-security:secured-channels>
|
||||
----
|
||||
====
|
||||
|
||||
Starting with version 4.2, the `@SecuredChannel` annotation is available for Java configuration in `@Configuration` classes.
|
||||
|
||||
The following example shows the Java equivalent of the preceding XML examples:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
@EnableIntegration
|
||||
public class ContextConfiguration {
|
||||
|
||||
@Bean
|
||||
@SecuredChannel(interceptor = "channelSecurityInterceptor", sendAccess = "ROLE_ADMIN")
|
||||
public SubscribableChannel adminChannel() {
|
||||
return new DirectChannel();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SecuredChannel(interceptor = "channelSecurityInterceptor", receiveAccess = "ROLE_USER")
|
||||
public SubscribableChannel userChannel() {
|
||||
return new DirectChannel();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ChannelSecurityInterceptor channelSecurityInterceptor(
|
||||
AuthenticationManager authenticationManager,
|
||||
AccessDecisionManager accessDecisionManager) {
|
||||
ChannelSecurityInterceptor channelSecurityInterceptor = new ChannelSecurityInterceptor();
|
||||
channelSecurityInterceptor.setAuthenticationManager(authenticationManager);
|
||||
channelSecurityInterceptor.setAccessDecisionManager(accessDecisionManager);
|
||||
return channelSecurityInterceptor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@GlobalChannelInterceptor(patterns = "secured*")
|
||||
AuthorizationChannelInterceptor authorizationChannelInterceptor() {
|
||||
return new AuthorizationChannelInterceptor(AuthorityAuthorizationManager.hasAnyRole("ADMIN", "PRESIDENT"));
|
||||
}
|
||||
----
|
||||
[source, xml, role="secondary"]
|
||||
.XML
|
||||
----
|
||||
<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>
|
||||
----
|
||||
====
|
||||
|
||||
See <<./channel.adoc#global-channel-configuration-interceptors,Global Channel Interceptor Configuration>> for more information.
|
||||
|
||||
[[security-context-propagation]]
|
||||
=== Security Context Propagation
|
||||
|
||||
@@ -176,8 +128,6 @@ public class ContextConfiguration {
|
||||
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
@MessagingGateway(asyncExecutor = "securityContextExecutor")
|
||||
public interface SecuredGateway {
|
||||
|
||||
|
||||
@@ -160,3 +160,9 @@ See <<./ip.adoc#ip,TCP and UDP Support>> for more information.
|
||||
The `JmsOutboundGateway` now creates a `TemporaryTopic` instead of `TemporaryQueue` if `replyPubSubDomain` option is set to `true`.
|
||||
|
||||
See <<./jms.adoc#jms,JMS Support>> for more information.
|
||||
|
||||
=== Security Changes
|
||||
|
||||
The `ChannelSecurityInterceptor` and its annotation `@SecuredChannel` and XML `<secured-channels>` configurations have been deprecated in favor of `AuthorizationChannelInterceptor`.
|
||||
|
||||
See <<./security.adoc#security,Security Support>> for more information.
|
||||
|
||||
Reference in New Issue
Block a user