SEC-2179: Add Spring Security Messaging Support
This commit is contained in:
parent
934937d9c1
commit
3f30529039
|
@ -18,7 +18,9 @@ dependencies {
|
|||
optional project(':spring-security-web'),
|
||||
project(':spring-security-ldap'),
|
||||
project(':spring-security-openid'),
|
||||
"org.springframework:spring-web:$springVersion",
|
||||
project(':spring-security-messaging'),
|
||||
"org.springframework:spring-web:$springVersion",
|
||||
"org.springframework:spring-websocket:$springVersion",
|
||||
"org.springframework:spring-webmvc:$springVersion",
|
||||
"org.aspectj:aspectjweaver:$aspectjVersion",
|
||||
"org.springframework:spring-jdbc:$springVersion",
|
||||
|
|
|
@ -375,7 +375,7 @@
|
|||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-jpa</artifactId>
|
||||
<version>1.4.1.RELEASE</version>
|
||||
<version>1.7.0.M1</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
|
@ -390,6 +390,12 @@
|
|||
<version>2.0.1.RELEASE</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-aspects</artifactId>
|
||||
<version>4.0.0.CI-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-cas</artifactId>
|
||||
|
|
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.messaging;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.security.config.annotation.web.configurers.RememberMeConfigurer;
|
||||
import org.springframework.security.messaging.access.expression.ExpressionBasedMessageSecurityMetadataSourceFactory;
|
||||
import org.springframework.security.messaging.access.intercept.MessageSecurityMetadataSource;
|
||||
import org.springframework.security.messaging.util.matcher.MessageMatcher;
|
||||
import org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Allows mapping security constraints using {@link MessageMatcher} to the security expressions.
|
||||
*
|
||||
* @since 4.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class MessageSecurityMetadataSourceRegistry {
|
||||
private static final String permitAll = "permitAll";
|
||||
private static final String denyAll = "denyAll";
|
||||
private static final String anonymous = "anonymous";
|
||||
private static final String authenticated = "authenticated";
|
||||
private static final String fullyAuthenticated = "fullyAuthenticated";
|
||||
private static final String rememberMe = "rememberMe";
|
||||
|
||||
private final LinkedHashMap<MessageMatcher<?>,String> matcherToExpression = new LinkedHashMap<MessageMatcher<?>,String>();
|
||||
|
||||
/**
|
||||
* Maps any {@link Message} to a security expression.
|
||||
*
|
||||
* @return the Expression to associate
|
||||
*/
|
||||
public Constraint anyMessage() {
|
||||
return new Constraint(Arrays.<MessageMatcher<?>>asList(MessageMatcher.ANY_MESSAGE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a {@link List} of {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher} instances.
|
||||
*
|
||||
* @param antPatterns the ant patterns to create {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher}
|
||||
* from
|
||||
*
|
||||
* @return the {@link Constraint} that is associated to the {@link MessageMatcher}
|
||||
*/
|
||||
public Constraint antMatchers(String... antPatterns) {
|
||||
List<MessageMatcher<?>> matchers = new ArrayList<MessageMatcher<?>>(antPatterns.length);
|
||||
for(String pattern : antPatterns) {
|
||||
matchers.add(new SimpDestinationMessageMatcher(pattern));
|
||||
}
|
||||
return new Constraint(matchers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a {@link List} of {@link MessageMatcher} instances to a security expression.
|
||||
*
|
||||
* @param matchers the {@link MessageMatcher} instances to map.
|
||||
* @return The {@link Constraint} that is associated to the {@link MessageMatcher} instances
|
||||
*/
|
||||
public Constraint matchers(MessageMatcher<?>... matchers) {
|
||||
return new Constraint(Arrays.asList(matchers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows subclasses to create creating a {@link MessageSecurityMetadataSource}.
|
||||
*
|
||||
* <p>This is not exposed so as not to confuse users of the API, which should never invoke this method.</p>
|
||||
*
|
||||
* @return the {@link MessageSecurityMetadataSource} to use
|
||||
*/
|
||||
protected MessageSecurityMetadataSource createMetadataSource() {
|
||||
return ExpressionBasedMessageSecurityMetadataSourceFactory.createExpressionMessageMetadataSource(matcherToExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the security constraint to be applied to the {@link MessageMatcher} instances.
|
||||
*/
|
||||
public class Constraint {
|
||||
private final List<MessageMatcher<?>> messageMatchers;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
* @param messageMatchers the {@link MessageMatcher} instances to map to this constraint
|
||||
*/
|
||||
public Constraint(List<MessageMatcher<?>> messageMatchers) {
|
||||
Assert.notEmpty(messageMatchers, "messageMatchers cannot be null or empty");
|
||||
this.messageMatchers = messageMatchers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for specifying {@link Message} instances require a particular role. If you do not want to have "ROLE_" automatically
|
||||
* inserted see {@link #hasAuthority(String)}.
|
||||
*
|
||||
* @param role the role to require (i.e. USER, ADMIN, etc). Note, it should not start with "ROLE_" as
|
||||
* this is automatically inserted.
|
||||
* @return the {@link MessageSecurityMetadataSourceRegistry} for further customization
|
||||
*/
|
||||
public MessageSecurityMetadataSourceRegistry hasRole(String role) {
|
||||
return access(MessageSecurityMetadataSourceRegistry.hasRole(role));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for specifying {@link Message} instances require any of a number of roles. If you
|
||||
* do not want to have "ROLE_" automatically inserted see
|
||||
* {@link #hasAnyAuthority(String...)}
|
||||
*
|
||||
* @param roles
|
||||
* the roles to require (i.e. USER, ADMIN, etc). Note, it
|
||||
* should not start with "ROLE_" as this is automatically
|
||||
* inserted.
|
||||
* @return the {@link MessageSecurityMetadataSourceRegistry} for further
|
||||
* customization
|
||||
*/
|
||||
public MessageSecurityMetadataSourceRegistry hasAnyRole(String... roles) {
|
||||
return access(MessageSecurityMetadataSourceRegistry.hasAnyRole(roles));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that {@link Message} instances require a particular authority.
|
||||
*
|
||||
* @param authority the authority to require (i.e. ROLE_USER, ROLE_ADMIN, etc).
|
||||
* @return the {@link MessageSecurityMetadataSourceRegistry} for further customization
|
||||
*/
|
||||
public MessageSecurityMetadataSourceRegistry hasAuthority(String authority) {
|
||||
return access(MessageSecurityMetadataSourceRegistry.hasAuthority(authority));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that {@link Message} instances requires any of a number authorities.
|
||||
*
|
||||
* @param authorities the requests require at least one of the authorities (i.e. "ROLE_USER","ROLE_ADMIN" would
|
||||
* mean either "ROLE_USER" or "ROLE_ADMIN" is required).
|
||||
* @return the {@link MessageSecurityMetadataSourceRegistry} for further customization
|
||||
*/
|
||||
public MessageSecurityMetadataSourceRegistry hasAnyAuthority(String... authorities) {
|
||||
return access(MessageSecurityMetadataSourceRegistry.hasAnyAuthority(authorities));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that Messages are allowed by anyone.
|
||||
*
|
||||
* @return the {@link MessageSecurityMetadataSourceRegistry} for further customization
|
||||
*/
|
||||
public MessageSecurityMetadataSourceRegistry permitAll() {
|
||||
return access(permitAll);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that Messages are allowed by anonymous users.
|
||||
*
|
||||
* @return the {@link MessageSecurityMetadataSourceRegistry} for further customization
|
||||
*/
|
||||
public MessageSecurityMetadataSourceRegistry anonymous() {
|
||||
return access(anonymous);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that Messages are allowed by users that have been remembered.
|
||||
*
|
||||
* @return the {@link MessageSecurityMetadataSourceRegistry} for further customization
|
||||
* @see {@link RememberMeConfigurer}
|
||||
*/
|
||||
public MessageSecurityMetadataSourceRegistry rememberMe() {
|
||||
return access(rememberMe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that Messages are not allowed by anyone.
|
||||
*
|
||||
* @return the {@link MessageSecurityMetadataSourceRegistry} for further customization
|
||||
*/
|
||||
public MessageSecurityMetadataSourceRegistry denyAll() {
|
||||
return access(denyAll);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that Messages are allowed by any authenticated user.
|
||||
*
|
||||
* @return the {@link MessageSecurityMetadataSourceRegistry} for further customization
|
||||
*/
|
||||
public MessageSecurityMetadataSourceRegistry authenticated() {
|
||||
return access(authenticated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that Messages are allowed by users who have authenticated and were not "remembered".
|
||||
*
|
||||
* @return the {@link MessageSecurityMetadataSourceRegistry} for further customization
|
||||
* @see {@link RememberMeConfigurer}
|
||||
*/
|
||||
public MessageSecurityMetadataSourceRegistry fullyAuthenticated() {
|
||||
return access(fullyAuthenticated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows specifying that Messages are secured by an arbitrary expression
|
||||
*
|
||||
* @param attribute the expression to secure the URLs (i.e. "hasRole('ROLE_USER') and hasRole('ROLE_SUPER')")
|
||||
* @return the {@link MessageSecurityMetadataSourceRegistry} for further customization
|
||||
*/
|
||||
public MessageSecurityMetadataSourceRegistry access(String attribute) {
|
||||
for(MessageMatcher<?> messageMatcher : messageMatchers) {
|
||||
matcherToExpression.put(messageMatcher, attribute);
|
||||
}
|
||||
return MessageSecurityMetadataSourceRegistry.this;
|
||||
}
|
||||
}
|
||||
|
||||
private static String hasAnyRole(String... authorities) {
|
||||
String anyAuthorities = StringUtils.arrayToDelimitedString(authorities, "','ROLE_");
|
||||
return "hasAnyRole('ROLE_" + anyAuthorities + "')";
|
||||
}
|
||||
|
||||
private static String hasRole(String role) {
|
||||
Assert.notNull(role, "role cannot be null");
|
||||
if (role.startsWith("ROLE_")) {
|
||||
throw new IllegalArgumentException("role should not start with 'ROLE_' since it is automatically inserted. Got '" + role + "'");
|
||||
}
|
||||
return "hasRole('ROLE_" + role + "')";
|
||||
}
|
||||
|
||||
private static String hasAuthority(String authority) {
|
||||
return "hasAuthority('" + authority + "')";
|
||||
}
|
||||
|
||||
private static String hasAnyAuthority(String... authorities) {
|
||||
String anyAuthorities = StringUtils.arrayToDelimitedString(authorities, "','");
|
||||
return "hasAnyAuthority('" + anyAuthorities + "')";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.socket;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.messaging.simp.config.ChannelRegistration;
|
||||
import org.springframework.security.access.AccessDecisionVoter;
|
||||
import org.springframework.security.access.vote.AffirmativeBased;
|
||||
import org.springframework.security.config.annotation.web.messaging.MessageSecurityMetadataSourceRegistry;
|
||||
import org.springframework.security.messaging.access.expression.ExpressionBasedMessageSecurityMetadataSourceFactory;
|
||||
import org.springframework.security.messaging.access.expression.MessageExpressionVoter;
|
||||
import org.springframework.security.messaging.access.intercept.ChannelSecurityInterceptor;
|
||||
import org.springframework.security.messaging.access.intercept.MessageSecurityMetadataSource;
|
||||
import org.springframework.security.messaging.context.SecurityContextChannelInterceptor;
|
||||
import org.springframework.security.messaging.util.matcher.MessageMatcher;
|
||||
import org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher;
|
||||
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
|
||||
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Allows configuring WebSocket Authorization.
|
||||
*
|
||||
* <p>For example:</p>
|
||||
*
|
||||
* <pre>
|
||||
* @Configuration
|
||||
* public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
|
||||
*
|
||||
* @Override
|
||||
* protected void configure(MessageSecurityMetadataSourceRegistry messages) {
|
||||
* messages
|
||||
* .antMatchers("/user/queue/errors").permitAll()
|
||||
* .antMatchers("/admin/**").hasRole("ADMIN")
|
||||
* .antMatchers("/**").authenticated();
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @since 4.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE + 100)
|
||||
public abstract class AbstractSecurityWebSocketMessageBrokerConfigurer extends AbstractWebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Override
|
||||
public void registerStompEndpoints(StompEndpointRegistry registry) {}
|
||||
|
||||
@Override
|
||||
public void configureClientInboundChannel(ChannelRegistration registration) {
|
||||
registration.setInterceptors(securityContextChannelInterceptor(),channelSecurity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureClientOutboundChannel(ChannelRegistration registration) {
|
||||
registration.setInterceptors(securityContextChannelInterceptor(),channelSecurity());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ChannelSecurityInterceptor channelSecurity() {
|
||||
ChannelSecurityInterceptor channelSecurityInterceptor = new ChannelSecurityInterceptor(metadataSource());
|
||||
List<AccessDecisionVoter> voters = new ArrayList<AccessDecisionVoter>();
|
||||
voters.add(new MessageExpressionVoter());
|
||||
AffirmativeBased manager = new AffirmativeBased(voters);
|
||||
channelSecurityInterceptor.setAccessDecisionManager(manager);
|
||||
return channelSecurityInterceptor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityContextChannelInterceptor securityContextChannelInterceptor() {
|
||||
return new SecurityContextChannelInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageSecurityMetadataSource metadataSource() {
|
||||
WebSocketMessageSecurityMetadataSourceRegistry registry = new WebSocketMessageSecurityMetadataSourceRegistry();
|
||||
configure(registry);
|
||||
return registry.createMetadataSource();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param messages
|
||||
*/
|
||||
protected abstract void configure(MessageSecurityMetadataSourceRegistry messages);
|
||||
|
||||
private class WebSocketMessageSecurityMetadataSourceRegistry extends MessageSecurityMetadataSourceRegistry {
|
||||
@Override
|
||||
public MessageSecurityMetadataSource createMetadataSource() {
|
||||
return super.createMetadataSource();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.messaging;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.messaging.access.intercept.MessageSecurityMetadataSource;
|
||||
import org.springframework.security.messaging.util.matcher.MessageMatcher;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class MessageSecurityMetadataSourceRegistryTests {
|
||||
@Mock
|
||||
private MessageMatcher<Object> matcher;
|
||||
|
||||
private MessageSecurityMetadataSourceRegistry messages;
|
||||
|
||||
private Message<String> message;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
messages = new MessageSecurityMetadataSourceRegistry();
|
||||
message = MessageBuilder
|
||||
.withPayload("Hi")
|
||||
.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, "location")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchersFalse() {
|
||||
messages
|
||||
.matchers(matcher).permitAll();
|
||||
|
||||
assertThat(getAttribute()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchersTrue() {
|
||||
when(matcher.matches(message)).thenReturn(true);
|
||||
messages
|
||||
.matchers(matcher).permitAll();
|
||||
|
||||
assertThat(getAttribute()).isEqualTo("permitAll");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void antMatcherExact() {
|
||||
messages
|
||||
.antMatchers("location").permitAll();
|
||||
|
||||
assertThat(getAttribute()).isEqualTo("permitAll");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void antMatcherMulti() {
|
||||
messages
|
||||
.antMatchers("admin/**","api/**").hasRole("ADMIN")
|
||||
.antMatchers("location").permitAll();
|
||||
|
||||
assertThat(getAttribute()).isEqualTo("permitAll");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void antMatcherRole() {
|
||||
messages
|
||||
.antMatchers("admin/**","location/**").hasRole("ADMIN")
|
||||
.anyMessage().denyAll();
|
||||
|
||||
assertThat(getAttribute()).isEqualTo("hasRole('ROLE_ADMIN')");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void antMatcherAnyRole() {
|
||||
messages
|
||||
.antMatchers("admin/**","location/**").hasAnyRole("ADMIN", "ROOT")
|
||||
.anyMessage().denyAll();
|
||||
|
||||
assertThat(getAttribute()).isEqualTo("hasAnyRole('ROLE_ADMIN','ROLE_ROOT')");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void antMatcherAuthority() {
|
||||
messages
|
||||
.antMatchers("admin/**","location/**").hasAuthority("ROLE_ADMIN")
|
||||
.anyMessage().fullyAuthenticated();
|
||||
|
||||
assertThat(getAttribute()).isEqualTo("hasAuthority('ROLE_ADMIN')");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void antMatcherAccess() {
|
||||
String expected = "hasRole('ROLE_ADMIN') and fullyAuthenticated";
|
||||
messages
|
||||
.antMatchers("admin/**","location/**").access(expected)
|
||||
.anyMessage().denyAll();
|
||||
|
||||
assertThat(getAttribute()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void antMatcherAnyAuthority() {
|
||||
messages
|
||||
.antMatchers("admin/**","location/**").hasAnyAuthority("ROLE_ADMIN", "ROLE_ROOT")
|
||||
.anyMessage().denyAll();
|
||||
|
||||
assertThat(getAttribute()).isEqualTo("hasAnyAuthority('ROLE_ADMIN','ROLE_ROOT')");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void antMatcherRememberMe() {
|
||||
messages
|
||||
.antMatchers("admin/**","location/**").rememberMe()
|
||||
.anyMessage().denyAll();
|
||||
|
||||
assertThat(getAttribute()).isEqualTo("rememberMe");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void antMatcherAnonymous() {
|
||||
messages
|
||||
.antMatchers("admin/**","location/**").anonymous()
|
||||
.anyMessage().denyAll();
|
||||
|
||||
assertThat(getAttribute()).isEqualTo("anonymous");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void antMatcherFullyAuthenticated() {
|
||||
messages
|
||||
.antMatchers("admin/**","location/**").fullyAuthenticated()
|
||||
.anyMessage().denyAll();
|
||||
|
||||
assertThat(getAttribute()).isEqualTo("fullyAuthenticated");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void antMatcherDenyAll() {
|
||||
messages
|
||||
.antMatchers("admin/**","location/**").denyAll()
|
||||
.anyMessage().permitAll();
|
||||
|
||||
assertThat(getAttribute()).isEqualTo("denyAll");
|
||||
}
|
||||
|
||||
private String getAttribute() {
|
||||
MessageSecurityMetadataSource source = messages.createMetadataSource();
|
||||
Collection<ConfigAttribute> attrs = source.getAttributes(message);
|
||||
if(attrs == null) {
|
||||
return null;
|
||||
}
|
||||
assertThat(attrs.size()).isEqualTo(1);
|
||||
return attrs.iterator().next().toString();
|
||||
}
|
||||
}
|
|
@ -38,7 +38,9 @@ import org.springframework.security.access.event.AuthorizedEvent;
|
|||
import org.springframework.security.access.event.PublicInvocationEvent;
|
||||
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
@ -105,7 +107,7 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean, A
|
|||
private ApplicationEventPublisher eventPublisher;
|
||||
private AccessDecisionManager accessDecisionManager;
|
||||
private AfterInvocationManager afterInvocationManager;
|
||||
private AuthenticationManager authenticationManager;
|
||||
private AuthenticationManager authenticationManager = new NoOpAuthenticationManager();
|
||||
private RunAsManager runAsManager = new NullRunAsManager();
|
||||
|
||||
private boolean alwaysReauthenticate = false;
|
||||
|
@ -460,4 +462,12 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean, A
|
|||
this.eventPublisher.publishEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
private static class NoOpAuthenticationManager implements AuthenticationManager {
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
throw new AuthenticationServiceException("Cannot authenticate " + authentication);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
apply plugin: 'groovy'
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-security-core'),
|
||||
'aopalliance:aopalliance:1.0',
|
||||
"org.springframework:spring-beans:$springVersion",
|
||||
"org.springframework:spring-context:$springVersion",
|
||||
"org.springframework:spring-expression:$springVersion",
|
||||
"org.springframework:spring-messaging:$springVersion"
|
||||
|
||||
testCompile project(':spring-security-core').sourceSets.test.output,
|
||||
'commons-codec:commons-codec:1.3',
|
||||
"org.slf4j:jcl-over-slf4j:$slf4jVersion",
|
||||
"org.codehaus.groovy:groovy-all:$groovyVersion",
|
||||
powerMockDependencies,
|
||||
spockDependencies
|
||||
|
||||
testRuntime "org.hsqldb:hsqldb:$hsqlVersion"
|
||||
}
|
|
@ -0,0 +1,228 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-messaging</artifactId>
|
||||
<version>4.0.0.CI-SNAPSHOT</version>
|
||||
<name>spring-security-messaging</name>
|
||||
<description>spring-security-messaging</description>
|
||||
<url>http://spring.io/spring-security</url>
|
||||
<organization>
|
||||
<name>spring.io</name>
|
||||
<url>http://spring.io/</url>
|
||||
</organization>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>rwinch</id>
|
||||
<name>Rob Winch</name>
|
||||
<email>rwinch@gopivotal.com</email>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/spring-projects/spring-security</connection>
|
||||
<developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection>
|
||||
<url>https://github.com/spring-projects/spring-security</url>
|
||||
</scm>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snasphot</id>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>aopalliance</groupId>
|
||||
<artifactId>aopalliance</artifactId>
|
||||
<version>1.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<version>4.0.0.CI-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
<version>4.1.0.RC2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<version>4.1.0.RC2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<version>4.1.0.RC2</version>
|
||||
<scope>compile</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<groupId>commons-logging</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-expression</artifactId>
|
||||
<version>4.1.0.RC2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-messaging</artifactId>
|
||||
<version>4.1.0.RC2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<version>1.1.1</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>0.9.29</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>1.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>groovy-all</artifactId>
|
||||
<version>2.0.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.easytesting</groupId>
|
||||
<artifactId>fest-assert</artifactId>
|
||||
<version>1.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hsqldb</groupId>
|
||||
<artifactId>hsqldb</artifactId>
|
||||
<version>2.3.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-api-mockito</artifactId>
|
||||
<version>1.5.1</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<groupId>org.mockito</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-api-support</artifactId>
|
||||
<version>1.5.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-core</artifactId>
|
||||
<version>1.5.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-module-junit4</artifactId>
|
||||
<version>1.5.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-module-junit4-common</artifactId>
|
||||
<version>1.5.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-reflect</artifactId>
|
||||
<version>1.5.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>1.7.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spockframework</groupId>
|
||||
<artifactId>spock-core</artifactId>
|
||||
<version>0.7-groovy-2.0</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>junit-dep</artifactId>
|
||||
<groupId>junit</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spockframework</groupId>
|
||||
<artifactId>spock-spring</artifactId>
|
||||
<version>0.7-groovy-2.0</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>junit-dep</artifactId>
|
||||
<groupId>junit</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<version>4.1.0.RC2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.springframework.security.messaging.access.expression;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.security.access.expression.AbstractSecurityExpressionHandler;
|
||||
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.access.expression.SecurityExpressionOperations;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* The default implementation of {@link SecurityExpressionHandler} which uses a {@link MessageSecurityExpressionRoot}.
|
||||
*
|
||||
* @since 4.0
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class DefaultMessageSecurityExpressionHandler<T> extends AbstractSecurityExpressionHandler<Message<T>> {
|
||||
|
||||
@Override
|
||||
protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, Message<T> invocation) {
|
||||
return new MessageSecurityExpressionRoot(authentication,invocation);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.springframework.security.messaging.access.expression;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.messaging.access.intercept.DefaultMessageSecurityMetadataSource;
|
||||
import org.springframework.security.messaging.access.intercept.MessageSecurityMetadataSource;
|
||||
import org.springframework.security.messaging.util.matcher.MessageMatcher;
|
||||
import org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A class used to create a {@link MessageSecurityMetadataSource} that uses {@link MessageMatcher} mapped to Spring
|
||||
* Expressions.
|
||||
*
|
||||
* @since 4.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public final class ExpressionBasedMessageSecurityMetadataSourceFactory {
|
||||
|
||||
/**
|
||||
* Create a {@link MessageSecurityMetadataSource} that uses {@link MessageMatcher} mapped to Spring
|
||||
* Expressions. Each entry is considered in order and only the first match is used.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* <pre>
|
||||
* LinkedHashMap<MessageMatcher<?> matcherToExpression = new LinkedHashMap<MessageMatcher<Object>();
|
||||
* matcherToExpression.put(new SimDestinationMessageMatcher("/public/**"), "permitAll");
|
||||
* matcherToExpression.put(new SimDestinationMessageMatcher("/admin/**"), "hasRole('ROLE_ADMIN')");
|
||||
* matcherToExpression.put(new SimDestinationMessageMatcher("/**"), "authenticated");
|
||||
*
|
||||
* MessageSecurityMetadataSource metadataSource = createExpressionMessageMetadataSource(matcherToExpression);
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* If our destination is "/public/hello", it would match on "/public/**" and on "/**". However, only "/public/**"
|
||||
* would be used since it is the first entry. That means that a destination of "/public/hello" will be mapped to
|
||||
* "permitAll".
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* For a complete listing of expressions see {@link MessageSecurityExpressionRoot}
|
||||
* </p>
|
||||
*
|
||||
* @param matcherToExpression an ordered mapping of {@link MessageMatcher} to Strings that are turned into an
|
||||
* Expression using {@link DefaultMessageSecurityExpressionHandler#getExpressionParser()}
|
||||
* @return the {@link MessageSecurityMetadataSource} to use. Cannot be null.
|
||||
*/
|
||||
public static MessageSecurityMetadataSource createExpressionMessageMetadataSource(LinkedHashMap<MessageMatcher<?>,String> matcherToExpression) {
|
||||
DefaultMessageSecurityExpressionHandler handler = new DefaultMessageSecurityExpressionHandler();
|
||||
|
||||
LinkedHashMap<MessageMatcher<?>, Collection<ConfigAttribute>> matcherToAttrs = new LinkedHashMap<MessageMatcher<?>, Collection<ConfigAttribute>>();
|
||||
|
||||
for(Map.Entry<MessageMatcher<?>,String> entry : matcherToExpression.entrySet()) {
|
||||
MessageMatcher<?> matcher = entry.getKey();
|
||||
String rawExpression = entry.getValue();
|
||||
Expression expression = handler.getExpressionParser().parseExpression(rawExpression);
|
||||
ConfigAttribute attribute = new MessageExpressionConfigAttribute(expression);
|
||||
matcherToAttrs.put(matcher, Arrays.asList(attribute));
|
||||
}
|
||||
return new DefaultMessageSecurityMetadataSource(matcherToAttrs);
|
||||
}
|
||||
|
||||
private ExpressionBasedMessageSecurityMetadataSourceFactory() {}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.springframework.security.messaging.access.expression;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple expression configuration attribute for use in {@link Message} authorizations.
|
||||
*
|
||||
* @since 4.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class MessageExpressionConfigAttribute implements ConfigAttribute {
|
||||
private final Expression authorizeExpression;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
* @param authorizeExpression the {@link Expression} to use. Cannot be null
|
||||
*/
|
||||
public MessageExpressionConfigAttribute(Expression authorizeExpression) {
|
||||
Assert.notNull(authorizeExpression, "authorizeExpression cannot be null");
|
||||
|
||||
this.authorizeExpression = authorizeExpression;
|
||||
}
|
||||
|
||||
Expression getAuthorizeExpression() {
|
||||
return authorizeExpression;
|
||||
}
|
||||
|
||||
public String getAttribute() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return authorizeExpression.getExpressionString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.springframework.security.messaging.access.expression;
|
||||
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.security.access.AccessDecisionVoter;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.expression.ExpressionUtils;
|
||||
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Voter which handles {@link Message} authorisation decisions. If a {@link MessageExpressionConfigAttribute} is found,
|
||||
* then its expression is evaluated. If true, {@code ACCESS_GRANTED} is returned. If false, {@code ACCESS_DENIED} is
|
||||
* returned. If no {@code MessageExpressionConfigAttribute} is found, then {@code ACCESS_ABSTAIN} is returned.
|
||||
*
|
||||
* @since 4.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class MessageExpressionVoter<T> implements AccessDecisionVoter<Message<T>> {
|
||||
private SecurityExpressionHandler<Message<T>> expressionHandler = new DefaultMessageSecurityExpressionHandler();
|
||||
|
||||
public int vote(Authentication authentication, Message<T> message, Collection<ConfigAttribute> attributes) {
|
||||
assert authentication != null;
|
||||
assert message != null;
|
||||
assert attributes != null;
|
||||
|
||||
MessageExpressionConfigAttribute attr = findConfigAttribute(attributes);
|
||||
|
||||
if (attr == null) {
|
||||
return ACCESS_ABSTAIN;
|
||||
}
|
||||
|
||||
EvaluationContext ctx = expressionHandler.createEvaluationContext(authentication, message);
|
||||
|
||||
return ExpressionUtils.evaluateAsBoolean(attr.getAuthorizeExpression(), ctx) ?
|
||||
ACCESS_GRANTED : ACCESS_DENIED;
|
||||
}
|
||||
|
||||
private MessageExpressionConfigAttribute findConfigAttribute(Collection<ConfigAttribute> attributes) {
|
||||
for (ConfigAttribute attribute : attributes) {
|
||||
if (attribute instanceof MessageExpressionConfigAttribute) {
|
||||
return (MessageExpressionConfigAttribute)attribute;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return attribute instanceof MessageExpressionConfigAttribute;
|
||||
}
|
||||
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return Message.class.isAssignableFrom(clazz);
|
||||
}
|
||||
|
||||
public void setExpressionHandler(SecurityExpressionHandler<Message<T>> expressionHandler) {
|
||||
Assert.notNull(expressionHandler, "expressionHandler cannot be null");
|
||||
this.expressionHandler = expressionHandler;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.springframework.security.messaging.access.expression;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.security.access.expression.SecurityExpressionRoot;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* The {@link SecurityExpressionRoot} used for {@link Message} expressions.
|
||||
*
|
||||
* @since 4.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
final class MessageSecurityExpressionRoot extends SecurityExpressionRoot {
|
||||
|
||||
public MessageSecurityExpressionRoot(Authentication authentication, Message message) {
|
||||
super(authentication);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.springframework.security.messaging.access.intercept;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.support.ChannelInterceptor;
|
||||
import org.springframework.security.access.SecurityMetadataSource;
|
||||
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
|
||||
import org.springframework.security.access.intercept.InterceptorStatusToken;
|
||||
import org.springframework.security.messaging.access.expression.ExpressionBasedMessageSecurityMetadataSourceFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Performs security handling of Message resources via a ChannelInterceptor implementation.
|
||||
* <p>
|
||||
* The <code>SecurityMetadataSource</code> required by this security interceptor is of type {@link
|
||||
* MessageSecurityMetadataSource}.
|
||||
* </p>
|
||||
* <p>
|
||||
* Refer to {@link AbstractSecurityInterceptor} for details on the workflow.
|
||||
* </p>
|
||||
*
|
||||
* @see 4.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public final class ChannelSecurityInterceptor extends AbstractSecurityInterceptor implements ChannelInterceptor {
|
||||
|
||||
private final MessageSecurityMetadataSource metadataSource;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
* @param metadataSource the MessageSecurityMetadataSource to use. Cannot be null.
|
||||
*
|
||||
* @see DefaultMessageSecurityMetadataSource
|
||||
* @see ExpressionBasedMessageSecurityMetadataSourceFactory
|
||||
*/
|
||||
public ChannelSecurityInterceptor(MessageSecurityMetadataSource metadataSource) {
|
||||
Assert.notNull(metadataSource, "metadataSource cannot be null");
|
||||
this.metadataSource = metadataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getSecureObjectClass() {
|
||||
return Message.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecurityMetadataSource obtainSecurityMetadataSource() {
|
||||
return metadataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<?> preSend(Message<?> message, MessageChannel channel) {
|
||||
InterceptorStatusToken token = beforeInvocation(message);
|
||||
return token == null ? message : new TokenMessage(message,token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
|
||||
if(!(message instanceof TokenMessage)) {
|
||||
// TODO What if other classes return another instance too?
|
||||
return;
|
||||
}
|
||||
InterceptorStatusToken token = ((TokenMessage)message).getToken();
|
||||
afterInvocation(token, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, Exception ex) {
|
||||
if(!(message instanceof TokenMessage)) {
|
||||
// TODO What if other classes return another instance too?
|
||||
return;
|
||||
}
|
||||
InterceptorStatusToken token = ((TokenMessage)message).getToken();
|
||||
finallyInvocation(token);
|
||||
}
|
||||
|
||||
public boolean preReceive(MessageChannel channel) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<?> postReceive(Message<?> message, MessageChannel channel) {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterReceiveCompletion(Message<?> message, MessageChannel channel, Exception ex) {
|
||||
}
|
||||
|
||||
static final class TokenMessage implements Message {
|
||||
private final Message delegate;
|
||||
private final InterceptorStatusToken token;
|
||||
|
||||
TokenMessage(Message delegate, InterceptorStatusToken token) {
|
||||
this.delegate = delegate;
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public InterceptorStatusToken getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageHeaders getHeaders() {
|
||||
return delegate.getHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPayload() {
|
||||
return delegate.getPayload();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.springframework.security.messaging.access.intercept;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.messaging.access.expression.ExpressionBasedMessageSecurityMetadataSourceFactory;
|
||||
import org.springframework.security.messaging.util.matcher.MessageMatcher;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A default implementation of {@link MessageSecurityMetadataSource} that looks up the {@link ConfigAttribute} instances
|
||||
* using a {@link MessageMatcher}.
|
||||
*
|
||||
* <p>
|
||||
* Each entry is considered in order. The first entry that matches, the corresponding {@code Collection<ConfigAttribute>}
|
||||
* is returned.
|
||||
* </p>
|
||||
*
|
||||
* @see ChannelSecurityInterceptor
|
||||
* @see ExpressionBasedMessageSecurityMetadataSourceFactory
|
||||
*
|
||||
* @since 4.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public final class DefaultMessageSecurityMetadataSource implements MessageSecurityMetadataSource {
|
||||
private final Map<MessageMatcher<?>,Collection<ConfigAttribute>> messageMap;
|
||||
|
||||
public DefaultMessageSecurityMetadataSource(LinkedHashMap<MessageMatcher<?>, Collection<ConfigAttribute>> messageMap) {
|
||||
this.messageMap = messageMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
|
||||
final Message message = (Message) object;
|
||||
for (Map.Entry<MessageMatcher<?>, Collection<ConfigAttribute>> entry : messageMap.entrySet()) {
|
||||
if (entry.getKey().matches(message)) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ConfigAttribute> getAllConfigAttributes() {
|
||||
Set<ConfigAttribute> allAttributes = new HashSet<ConfigAttribute>();
|
||||
|
||||
for (Collection<ConfigAttribute> entry : messageMap.values()) {
|
||||
allAttributes.addAll(entry);
|
||||
}
|
||||
|
||||
return allAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return Message.class.isAssignableFrom(clazz);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.springframework.security.messaging.access.intercept;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.security.access.SecurityMetadataSource;
|
||||
|
||||
/**
|
||||
* A {@link SecurityMetadataSource} that is used for securing {@link Message}
|
||||
*
|
||||
* @see ChannelSecurityInterceptor
|
||||
* @see DefaultMessageSecurityMetadataSource
|
||||
*
|
||||
* @since 4.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public interface MessageSecurityMetadataSource extends SecurityMetadataSource {
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.springframework.security.messaging.context;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||
import org.springframework.messaging.support.ChannelInterceptorAdapter;
|
||||
import org.springframework.messaging.support.ExecutorChannelInterceptor;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Creates a {@link ExecutorChannelInterceptor} that will obtain the {@link Authentication} from the specified
|
||||
* {@link Message#getHeaders()}.
|
||||
* </p>
|
||||
*
|
||||
* @since 4.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public final class SecurityContextChannelInterceptor extends ChannelInterceptorAdapter implements ExecutorChannelInterceptor {
|
||||
private final String authenticationHeaderName;
|
||||
|
||||
/**
|
||||
* Creates a new instance using the header of the name {@link SimpMessageHeaderAccessor#USER_HEADER}.
|
||||
*/
|
||||
public SecurityContextChannelInterceptor() {
|
||||
this(SimpMessageHeaderAccessor.USER_HEADER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance that uses the specified header to obtain the {@link Authentication}.
|
||||
*
|
||||
* @param authenticationHeaderName the header name to obtain the {@link Authentication}. Cannot be null.
|
||||
*/
|
||||
public SecurityContextChannelInterceptor(String authenticationHeaderName) {
|
||||
Assert.notNull(authenticationHeaderName, "authenticationHeaderName cannot be null");
|
||||
this.authenticationHeaderName = authenticationHeaderName;
|
||||
}
|
||||
@Override
|
||||
public Message<?> preSend(Message<?> message, MessageChannel channel) {
|
||||
setup(message);
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, Exception ex) {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<?> beforeHandle(Message<?> message, MessageChannel channel, MessageHandler handler) {
|
||||
setup(message);
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterMessageHandled(Message<?> message, MessageChannel channel, MessageHandler handler, Exception ex) {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
private void setup(Message<?> message) {
|
||||
Object user = message.getHeaders().get(authenticationHeaderName);
|
||||
if(!(user instanceof Authentication)) {
|
||||
return;
|
||||
}
|
||||
Authentication authentication = (Authentication) user;
|
||||
SecurityContext context = SecurityContextHolder.createEmptyContext();
|
||||
context.setAuthentication(authentication);
|
||||
SecurityContextHolder.setContext(context);
|
||||
}
|
||||
|
||||
private void cleanup() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.springframework.security.messaging.util.matcher;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
* API for determining if a {@link Message} should be matched on.
|
||||
*
|
||||
* @since 4.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public interface MessageMatcher<T> {
|
||||
|
||||
/**
|
||||
* Returns true if the {@link Message} matches, else false
|
||||
* @param message the {@link Message} to match on
|
||||
* @return true if the {@link Message} matches, else false
|
||||
*/
|
||||
boolean matches(Message<? extends T> message);
|
||||
|
||||
/**
|
||||
* Matches every {@link Message}
|
||||
*/
|
||||
MessageMatcher ANY_MESSAGE = new MessageMatcher<Object>() {
|
||||
public boolean matches(Message<? extends Object> message) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.springframework.security.messaging.util.matcher;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* MessageMatcher which compares a pre-defined ant-style pattern against the destination of a {@link Message}.
|
||||
* </p>
|
||||
*
|
||||
* <p>The mapping matches destinations using the following rules:
|
||||
*
|
||||
* <ul>
|
||||
* <li>? matches one character</li>
|
||||
* <li>* matches zero or more characters</li>
|
||||
* <li>** matches zero or more 'directories' in a path</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Some examples:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code com/t?st.jsp} - matches {@code com/test} but also
|
||||
* {@code com/tast} or {@code com/txst}</li>
|
||||
* <li>{@code com/*suffix} - matches all files ending in {@code suffix} in the {@code com} directory</li>
|
||||
* <li>{@code com/**/test} - matches all destinations ending with {@code test} underneath the {@code com} path</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public final class SimpDestinationMessageMatcher implements MessageMatcher<Object> {
|
||||
private final AntPathMatcher matcher = new AntPathMatcher();
|
||||
private final String pattern;
|
||||
|
||||
public SimpDestinationMessageMatcher(String pattern) {
|
||||
Assert.notNull(pattern, "pattern cannot be null");
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Message<? extends Object> message) {
|
||||
String destination = SimpMessageHeaderAccessor.getDestination(message.getHeaders());
|
||||
return destination != null && matcher.match(pattern, destination);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.springframework.security.messaging.access.expression;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.powermock.api.mockito.PowerMockito.when;
|
||||
import static org.springframework.security.messaging.access.expression.ExpressionBasedMessageSecurityMetadataSourceFactory.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.messaging.access.intercept.MessageSecurityMetadataSource;
|
||||
import org.springframework.security.messaging.util.matcher.MessageMatcher;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ExpressionBasedMessageSecurityMetadataSourceFactoryTests {
|
||||
@Mock
|
||||
MessageMatcher matcher1;
|
||||
@Mock
|
||||
MessageMatcher matcher2;
|
||||
@Mock
|
||||
Message message;
|
||||
@Mock
|
||||
Authentication authentication;
|
||||
|
||||
String expression1;
|
||||
|
||||
String expression2;
|
||||
|
||||
LinkedHashMap<MessageMatcher<?>,String> matcherToExpression;
|
||||
|
||||
MessageSecurityMetadataSource source;
|
||||
|
||||
MessageSecurityExpressionRoot rootObject;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
expression1 = "permitAll";
|
||||
expression2 = "denyAll";
|
||||
matcherToExpression = new LinkedHashMap<MessageMatcher<?>, String>();
|
||||
matcherToExpression.put(matcher1, expression1);
|
||||
matcherToExpression.put(matcher2, expression2);
|
||||
|
||||
source = createExpressionMessageMetadataSource(matcherToExpression);
|
||||
rootObject = new MessageSecurityExpressionRoot(authentication, message);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createExpressionMessageMetadataSourceNoMatch() {
|
||||
|
||||
Collection<ConfigAttribute> attrs = source.getAttributes(message);
|
||||
|
||||
assertThat(attrs).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createExpressionMessageMetadataSourceMatchFirst() {
|
||||
when(matcher1.matches(message)).thenReturn(true);
|
||||
|
||||
Collection<ConfigAttribute> attrs = source.getAttributes(message);
|
||||
|
||||
assertThat(attrs.size()).isEqualTo(1);
|
||||
ConfigAttribute attr = attrs.iterator().next();
|
||||
assertThat(attr).isInstanceOf(MessageExpressionConfigAttribute.class);
|
||||
assertThat(((MessageExpressionConfigAttribute)attr).getAuthorizeExpression().getValue(rootObject)).isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createExpressionMessageMetadataSourceMatchSecond() {
|
||||
when(matcher2.matches(message)).thenReturn(true);
|
||||
|
||||
Collection<ConfigAttribute> attrs = source.getAttributes(message);
|
||||
|
||||
assertThat(attrs.size()).isEqualTo(1);
|
||||
ConfigAttribute attr = attrs.iterator().next();
|
||||
assertThat(attr).isInstanceOf(MessageExpressionConfigAttribute.class);
|
||||
assertThat(((MessageExpressionConfigAttribute)attr).getAuthorizeExpression().getValue(rootObject)).isEqualTo(false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.springframework.security.messaging.access.expression;
|
||||
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.expression.Expression;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class MessageExpressionConfigAttributeTests {
|
||||
@Mock
|
||||
Expression expression;
|
||||
|
||||
MessageExpressionConfigAttribute attribute;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
attribute = new MessageExpressionConfigAttribute(expression);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorNullExpression() {
|
||||
new MessageExpressionConfigAttribute(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAuthorizeExpression() {
|
||||
assertThat(attribute.getAuthorizeExpression()).isSameAs(expression);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAttribute() {
|
||||
assertThat(attribute.getAttribute()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toStringUsesExpressionString() {
|
||||
when(expression.getExpressionString()).thenReturn("toString");
|
||||
|
||||
assertThat(attribute.toString()).isEqualTo(expression.getExpressionString());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.springframework.security.messaging.access.expression;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.security.access.AccessDecisionVoter.*;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class MessageExpressionVoterTests {
|
||||
@Mock
|
||||
Authentication authentication;
|
||||
@Mock
|
||||
Message<Object> message;
|
||||
Collection<ConfigAttribute> attributes;
|
||||
@Mock
|
||||
Expression expression;
|
||||
@Mock
|
||||
SecurityExpressionHandler<Message> expressionHandler;
|
||||
@Mock
|
||||
EvaluationContext evaluationContext;
|
||||
|
||||
MessageExpressionVoter voter;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
attributes = Arrays.<ConfigAttribute>asList(new MessageExpressionConfigAttribute(expression));
|
||||
|
||||
voter = new MessageExpressionVoter();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void voteGranted() {
|
||||
when(expression.getValue(any(EvaluationContext.class),eq(Boolean.class))).thenReturn(true);
|
||||
assertThat(voter.vote(authentication, message, attributes)).isEqualTo(ACCESS_GRANTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void voteDenied() {
|
||||
when(expression.getValue(any(EvaluationContext.class),eq(Boolean.class))).thenReturn(false);
|
||||
assertThat(voter.vote(authentication, message, attributes)).isEqualTo(ACCESS_DENIED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void voteAbstain() {
|
||||
attributes = Arrays.<ConfigAttribute>asList(new SecurityConfig("ROLE_USER"));
|
||||
assertThat(voter.vote(authentication, message, attributes)).isEqualTo(ACCESS_ABSTAIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsObjectClassFalse() {
|
||||
assertThat(voter.supports(Object.class)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsMessageClassTrue() {
|
||||
assertThat(voter.supports(Message.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsSecurityConfigFalse() {
|
||||
assertThat(voter.supports(new SecurityConfig("ROLE_USER"))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsMessageExpressionConfigAttributeTrue() {
|
||||
assertThat(voter.supports(new MessageExpressionConfigAttribute(expression))).isTrue();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void setExpressionHandlerNull() {
|
||||
voter.setExpressionHandler(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customExpressionHandler() {
|
||||
voter.setExpressionHandler(expressionHandler);
|
||||
when(expressionHandler.createEvaluationContext(authentication, message)).thenReturn(evaluationContext);
|
||||
when(expression.getValue(evaluationContext, Boolean.class)).thenReturn(true);
|
||||
|
||||
assertThat(voter.vote(authentication, message, attributes)).isEqualTo(ACCESS_GRANTED);
|
||||
|
||||
verify(expressionHandler).createEvaluationContext(authentication, message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.springframework.security.messaging.access.intercept;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.security.access.AccessDecisionManager;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.access.intercept.InterceptorStatusToken;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ChannelSecurityInterceptorTests {
|
||||
@Mock
|
||||
Message message;
|
||||
@Mock
|
||||
MessageChannel channel;
|
||||
@Mock
|
||||
MessageSecurityMetadataSource source;
|
||||
@Mock
|
||||
AccessDecisionManager accessDecisionManager;
|
||||
List<ConfigAttribute> attrs;
|
||||
|
||||
ChannelSecurityInterceptor interceptor;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
attrs = Arrays.<ConfigAttribute>asList(new SecurityConfig("ROLE_USER"));
|
||||
interceptor = new ChannelSecurityInterceptor(source);
|
||||
interceptor.setAccessDecisionManager(accessDecisionManager);
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "pass", "ROLE_USER"));
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorMessageSecurityMetadataSourceNull() {
|
||||
new ChannelSecurityInterceptor(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSecureObjectClass() throws Exception {
|
||||
assertThat(interceptor.getSecureObjectClass()).isEqualTo(Message.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void obtainSecurityMetadataSource() throws Exception {
|
||||
assertThat(interceptor.obtainSecurityMetadataSource()).isEqualTo(source);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendNullAttributes() throws Exception {
|
||||
assertThat(interceptor.preSend(message, channel)).isSameAs(message);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendGrant() throws Exception {
|
||||
when(source.getAttributes(message)).thenReturn(attrs);
|
||||
|
||||
Message<?> result = interceptor.preSend(message, channel);
|
||||
|
||||
assertThat(result).isInstanceOf(ChannelSecurityInterceptor.TokenMessage.class);
|
||||
ChannelSecurityInterceptor.TokenMessage tm = (ChannelSecurityInterceptor.TokenMessage) result;
|
||||
assertThat(tm.getHeaders()).isSameAs(message.getHeaders());
|
||||
assertThat(tm.getPayload()).isSameAs(message.getPayload());
|
||||
assertThat(tm.getToken()).isNotNull();
|
||||
}
|
||||
|
||||
@Test(expected = AccessDeniedException.class)
|
||||
public void preSendDeny() throws Exception {
|
||||
when(source.getAttributes(message)).thenReturn(attrs);
|
||||
doThrow(new AccessDeniedException("")).when(accessDecisionManager).decide(any(Authentication.class), eq(message), eq(attrs));
|
||||
|
||||
interceptor.preSend(message, channel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postSendNotTokenMessageNoExceptionThrown() throws Exception {
|
||||
interceptor.postSend(message, channel, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postSendTokenMessage() throws Exception {
|
||||
InterceptorStatusToken token = new InterceptorStatusToken(SecurityContextHolder.createEmptyContext(),true,attrs,message);
|
||||
ChannelSecurityInterceptor.TokenMessage tokenMessage = new ChannelSecurityInterceptor.TokenMessage(message, token);
|
||||
|
||||
interceptor.postSend(tokenMessage, channel, true);
|
||||
|
||||
assertThat(SecurityContextHolder.getContext()).isSameAs(token.getSecurityContext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterSendCompletionNotTokenMessageNoExceptionThrown() throws Exception {
|
||||
interceptor.afterSendCompletion(message, channel, true, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterSendCompletionTokenMessage() throws Exception {
|
||||
InterceptorStatusToken token = new InterceptorStatusToken(SecurityContextHolder.createEmptyContext(),true,attrs,message);
|
||||
ChannelSecurityInterceptor.TokenMessage tokenMessage = new ChannelSecurityInterceptor.TokenMessage(message, token);
|
||||
|
||||
interceptor.afterSendCompletion(tokenMessage, channel, true, null);
|
||||
|
||||
assertThat(SecurityContextHolder.getContext()).isSameAs(token.getSecurityContext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preReceive() throws Exception {
|
||||
assertThat(interceptor.preReceive(channel)).isTrue();;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postReceive() throws Exception {
|
||||
assertThat(interceptor.postReceive(message, channel)).isSameAs(message);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterReceiveCompletionNullExceptionNoExceptionThrown() throws Exception {
|
||||
interceptor.afterReceiveCompletion(message, channel, null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.springframework.security.messaging.access.intercept;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.messaging.util.matcher.MessageMatcher;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.powermock.api.mockito.PowerMockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DefaultMessageSecurityMetadataSourceTests {
|
||||
@Mock
|
||||
MessageMatcher matcher1;
|
||||
@Mock
|
||||
MessageMatcher matcher2;
|
||||
@Mock
|
||||
Message message;
|
||||
@Mock
|
||||
Authentication authentication;
|
||||
|
||||
SecurityConfig config1;
|
||||
|
||||
SecurityConfig config2;
|
||||
|
||||
LinkedHashMap<MessageMatcher<?>,Collection<ConfigAttribute>> messageMap;
|
||||
|
||||
MessageSecurityMetadataSource source;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
messageMap = new LinkedHashMap<MessageMatcher<?>, Collection<ConfigAttribute>>();
|
||||
messageMap.put(matcher1, Arrays.<ConfigAttribute>asList(config1));
|
||||
messageMap.put(matcher2, Arrays.<ConfigAttribute>asList(config2));
|
||||
|
||||
source = new DefaultMessageSecurityMetadataSource(messageMap);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAttributesNull() {
|
||||
assertThat(source.getAttributes(message)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAttributesFirst() {
|
||||
when(matcher1.matches(message)).thenReturn(true);
|
||||
|
||||
assertThat(source.getAttributes(message)).containsOnly(config1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAttributesSecond() {
|
||||
when(matcher1.matches(message)).thenReturn(true);
|
||||
|
||||
assertThat(source.getAttributes(message)).containsOnly(config2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAllConfigAttributes() {
|
||||
assertThat(source.getAllConfigAttributes()).containsOnly(config1,config2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsFalse() {
|
||||
assertThat(source.supports(Object.class)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsTrue() {
|
||||
assertThat(source.supports(Message.class)).isTrue();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
package org.springframework.security.messaging.context;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.messaging.context.SecurityContextChannelInterceptor;
|
||||
|
||||
import java.security.Principal;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.springframework.security.core.context.SecurityContextHolder.*;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class SecurityContextChannelInterceptorTests {
|
||||
@Mock
|
||||
MessageChannel channel;
|
||||
@Mock
|
||||
MessageHandler handler;
|
||||
@Mock
|
||||
Principal principal;
|
||||
|
||||
MessageBuilder messageBuilder;
|
||||
|
||||
Authentication authentication;
|
||||
|
||||
SecurityContextChannelInterceptor interceptor;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
authentication = new TestingAuthenticationToken("user","pass", "ROLE_USER");
|
||||
messageBuilder = MessageBuilder.withPayload("payload");
|
||||
|
||||
interceptor = new SecurityContextChannelInterceptor();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() {
|
||||
clearContext();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorNullHeader() {
|
||||
new SecurityContextChannelInterceptor(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendCustomHeader() throws Exception {
|
||||
String headerName = "header";
|
||||
interceptor = new SecurityContextChannelInterceptor(headerName);
|
||||
messageBuilder.setHeader(headerName, authentication);
|
||||
|
||||
interceptor.preSend(messageBuilder.build(), channel);
|
||||
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(authentication);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendUserSet() throws Exception {
|
||||
messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, authentication);
|
||||
|
||||
interceptor.preSend(messageBuilder.build(), channel);
|
||||
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(authentication);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendUserNotAuthentication() throws Exception {
|
||||
messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, principal);
|
||||
|
||||
interceptor.preSend(messageBuilder.build(), channel);
|
||||
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendUserNotSet() throws Exception {
|
||||
interceptor.preSend(messageBuilder.build(), channel);
|
||||
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterSendCompletion() throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
|
||||
interceptor.afterSendCompletion(messageBuilder.build(), channel, true, null);
|
||||
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterSendCompletionNullAuthentication() throws Exception {
|
||||
interceptor.afterSendCompletion(messageBuilder.build(), channel, true, null);
|
||||
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beforeHandleUserSet() throws Exception {
|
||||
messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, authentication);
|
||||
|
||||
interceptor.beforeHandle(messageBuilder.build(), channel, handler);
|
||||
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(authentication);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beforeHandleUserNotAuthentication() throws Exception {
|
||||
messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, principal);
|
||||
|
||||
interceptor.beforeHandle(messageBuilder.build(), channel, handler);
|
||||
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beforeHandleUserNotSet() throws Exception {
|
||||
interceptor.beforeHandle(messageBuilder.build(), channel, handler);
|
||||
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void afterMessageHandledUserNotSet() throws Exception {
|
||||
interceptor.afterMessageHandled(messageBuilder.build(), channel, handler, null);
|
||||
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterMessageHandled() throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
|
||||
interceptor.afterMessageHandled(messageBuilder.build(), channel, handler, null);
|
||||
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.springframework.security.messaging.util.matcher;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
|
||||
|
||||
public class SimpDestinationMessageMatcherTests {
|
||||
MessageBuilder<String> messageBuilder;
|
||||
|
||||
SimpDestinationMessageMatcher matcher;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
messageBuilder = MessageBuilder.withPayload("M");
|
||||
matcher = new SimpDestinationMessageMatcher("/**");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorPatternNull() {
|
||||
new SimpDestinationMessageMatcher(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesDoesNotMatchNullDestination() throws Exception {
|
||||
assertThat(matcher.matches(messageBuilder.build())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesAllWithDestination() throws Exception {
|
||||
messageBuilder.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER,"/destination/1");
|
||||
|
||||
assertThat(matcher.matches(messageBuilder.build())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesSpecificWithDestination() throws Exception {
|
||||
matcher = new SimpDestinationMessageMatcher("/destination/1");
|
||||
|
||||
messageBuilder.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER,"/destination/1");
|
||||
|
||||
assertThat(matcher.matches(messageBuilder.build())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesFalseWithDestination() throws Exception {
|
||||
matcher = new SimpDestinationMessageMatcher("/nomatch");
|
||||
|
||||
messageBuilder.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER,"/destination/1");
|
||||
|
||||
assertThat(matcher.matches(messageBuilder.build())).isFalse();
|
||||
}
|
||||
}
|
|
@ -88,7 +88,7 @@
|
|||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-jpa</artifactId>
|
||||
<version>1.3.4.RELEASE</version>
|
||||
<version>1.7.0.M1</version>
|
||||
<scope>compile</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
|
|
|
@ -11,6 +11,7 @@ def String[] modules = [
|
|||
'taglibs',
|
||||
'aspects',
|
||||
'crypto',
|
||||
'messaging',
|
||||
'test'
|
||||
]
|
||||
|
||||
|
|
Loading…
Reference in New Issue