2 changed files with 0 additions and 849 deletions
@ -1,512 +0,0 @@
@@ -1,512 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.config.annotation.web.messaging; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Comparator; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import org.springframework.messaging.Message; |
||||
import org.springframework.messaging.simp.SimpMessageType; |
||||
import org.springframework.security.access.expression.SecurityExpressionHandler; |
||||
import org.springframework.security.config.annotation.web.configurers.RememberMeConfigurer; |
||||
import org.springframework.security.messaging.access.expression.DefaultMessageSecurityExpressionHandler; |
||||
import org.springframework.security.messaging.access.expression.ExpressionBasedMessageSecurityMetadataSourceFactory; |
||||
import org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager; |
||||
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.security.messaging.util.matcher.SimpMessageTypeMatcher; |
||||
import org.springframework.util.AntPathMatcher; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.PathMatcher; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* Allows mapping security constraints using {@link MessageMatcher} to the security |
||||
* expressions. |
||||
* |
||||
* @author Rob Winch |
||||
* @since 4.0 |
||||
* @deprecated Use {@link MessageMatcherDelegatingAuthorizationManager} instead |
||||
*/ |
||||
@Deprecated |
||||
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 SecurityExpressionHandler<Message<Object>> expressionHandler = new DefaultMessageSecurityExpressionHandler<>(); |
||||
|
||||
private final LinkedHashMap<MatcherBuilder, String> matcherToExpression = new LinkedHashMap<>(); |
||||
|
||||
private DelegatingPathMatcher pathMatcher = new DelegatingPathMatcher(); |
||||
|
||||
private boolean defaultPathMatcher = true; |
||||
|
||||
/** |
||||
* Maps any {@link Message} to a security expression. |
||||
* @return the Expression to associate |
||||
*/ |
||||
public Constraint anyMessage() { |
||||
return matchers(MessageMatcher.ANY_MESSAGE); |
||||
} |
||||
|
||||
/** |
||||
* Maps any {@link Message} that has a null SimpMessageHeaderAccessor destination |
||||
* header (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, UNSUBSCRIBE, DISCONNECT, |
||||
* DISCONNECT_ACK, OTHER) |
||||
* @return the Expression to associate |
||||
*/ |
||||
public Constraint nullDestMatcher() { |
||||
return matchers(SimpDestinationMessageMatcher.NULL_DESTINATION_MATCHER); |
||||
} |
||||
|
||||
/** |
||||
* Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances. |
||||
* @param typesToMatch the {@link SimpMessageType} instance to match on |
||||
* @return the {@link Constraint} associated to the matchers. |
||||
*/ |
||||
public Constraint simpTypeMatchers(SimpMessageType... typesToMatch) { |
||||
MessageMatcher<?>[] typeMatchers = new MessageMatcher<?>[typesToMatch.length]; |
||||
for (int i = 0; i < typesToMatch.length; i++) { |
||||
SimpMessageType typeToMatch = typesToMatch[i]; |
||||
typeMatchers[i] = new SimpMessageTypeMatcher(typeToMatch); |
||||
} |
||||
return matchers(typeMatchers); |
||||
} |
||||
|
||||
/** |
||||
* Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances without |
||||
* regard to the {@link SimpMessageType}. If no destination is found on the Message, |
||||
* then the Matcher returns false. |
||||
* @param patterns the patterns to create |
||||
* {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher} |
||||
* from. Uses |
||||
* {@link MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher)} . |
||||
* @return the {@link Constraint} that is associated to the {@link MessageMatcher} |
||||
* @see MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher) |
||||
*/ |
||||
public Constraint simpDestMatchers(String... patterns) { |
||||
return simpDestMatchers(null, patterns); |
||||
} |
||||
|
||||
/** |
||||
* Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances that match |
||||
* on {@code SimpMessageType.MESSAGE}. If no destination is found on the Message, then |
||||
* the Matcher returns false. |
||||
* @param patterns the patterns to create |
||||
* {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher} |
||||
* from. Uses |
||||
* {@link MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher)}. |
||||
* @return the {@link Constraint} that is associated to the {@link MessageMatcher} |
||||
* @see MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher) |
||||
*/ |
||||
public Constraint simpMessageDestMatchers(String... patterns) { |
||||
return simpDestMatchers(SimpMessageType.MESSAGE, patterns); |
||||
} |
||||
|
||||
/** |
||||
* Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances that match |
||||
* on {@code SimpMessageType.SUBSCRIBE}. If no destination is found on the Message, |
||||
* then the Matcher returns false. |
||||
* @param patterns the patterns to create |
||||
* {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher} |
||||
* from. Uses |
||||
* {@link MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher)}. |
||||
* @return the {@link Constraint} that is associated to the {@link MessageMatcher} |
||||
* @see MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher) |
||||
*/ |
||||
public Constraint simpSubscribeDestMatchers(String... patterns) { |
||||
return simpDestMatchers(SimpMessageType.SUBSCRIBE, patterns); |
||||
} |
||||
|
||||
/** |
||||
* Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances. If no |
||||
* destination is found on the Message, then the Matcher returns false. |
||||
* @param type the {@link SimpMessageType} to match on. If null, the |
||||
* {@link SimpMessageType} is not considered for matching. |
||||
* @param patterns the patterns to create |
||||
* {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher} |
||||
* from. Uses |
||||
* {@link MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher)}. |
||||
* @return the {@link Constraint} that is associated to the {@link MessageMatcher} |
||||
* @see MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher) |
||||
*/ |
||||
private Constraint simpDestMatchers(SimpMessageType type, String... patterns) { |
||||
List<MatcherBuilder> matchers = new ArrayList<>(patterns.length); |
||||
for (String pattern : patterns) { |
||||
matchers.add(new PathMatcherMessageMatcherBuilder(pattern, type)); |
||||
} |
||||
return new Constraint(matchers); |
||||
} |
||||
|
||||
/** |
||||
* The {@link PathMatcher} to be used with the |
||||
* {@link MessageSecurityMetadataSourceRegistry#simpDestMatchers(String...)}. The |
||||
* default is to use the default constructor of {@link AntPathMatcher}. |
||||
* @param pathMatcher the {@link PathMatcher} to use. Cannot be null. |
||||
* @return the {@link MessageSecurityMetadataSourceRegistry} for further |
||||
* customization. |
||||
*/ |
||||
public MessageSecurityMetadataSourceRegistry simpDestPathMatcher(PathMatcher pathMatcher) { |
||||
Assert.notNull(pathMatcher, "pathMatcher cannot be null"); |
||||
this.pathMatcher.setPathMatcher(pathMatcher); |
||||
this.defaultPathMatcher = false; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Determines if the {@link #simpDestPathMatcher(PathMatcher)} has been explicitly |
||||
* set. |
||||
* @return true if {@link #simpDestPathMatcher(PathMatcher)} has been explicitly set, |
||||
* else false. |
||||
*/ |
||||
protected boolean isSimpDestPathMatcherConfigured() { |
||||
return !this.defaultPathMatcher; |
||||
} |
||||
|
||||
/** |
||||
* 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) { |
||||
List<MatcherBuilder> builders = new ArrayList<>(matchers.length); |
||||
for (MessageMatcher<?> matcher : matchers) { |
||||
builders.add(new PreBuiltMatcherBuilder(matcher)); |
||||
} |
||||
return new Constraint(builders); |
||||
} |
||||
|
||||
/** |
||||
* The {@link SecurityExpressionHandler} to be used. The default is to use |
||||
* {@link DefaultMessageSecurityExpressionHandler}. |
||||
* @param expressionHandler the {@link SecurityExpressionHandler} to use. Cannot be |
||||
* null. |
||||
* @return the {@link MessageSecurityMetadataSourceRegistry} for further |
||||
* customization. |
||||
*/ |
||||
public MessageSecurityMetadataSourceRegistry expressionHandler( |
||||
SecurityExpressionHandler<Message<Object>> expressionHandler) { |
||||
Assert.notNull(expressionHandler, "expressionHandler cannot be null"); |
||||
this.expressionHandler = expressionHandler; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 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() { |
||||
LinkedHashMap<MessageMatcher<?>, String> matcherToExpression = new LinkedHashMap<>(); |
||||
for (Map.Entry<MatcherBuilder, String> entry : this.matcherToExpression.entrySet()) { |
||||
matcherToExpression.put(entry.getKey().build(), entry.getValue()); |
||||
} |
||||
return ExpressionBasedMessageSecurityMetadataSourceFactory |
||||
.createExpressionMessageMetadataSource(matcherToExpression, this.expressionHandler); |
||||
} |
||||
|
||||
/** |
||||
* Allows determining if a mapping was added. |
||||
* |
||||
* <p> |
||||
* This is not exposed so as not to confuse users of the API, which should never need |
||||
* to invoke this method. |
||||
* </p> |
||||
* @return true if a mapping was added, else false |
||||
*/ |
||||
protected boolean containsMapping() { |
||||
return !this.matcherToExpression.isEmpty(); |
||||
} |
||||
|
||||
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 + "')"; |
||||
} |
||||
|
||||
/** |
||||
* Represents the security constraint to be applied to the {@link MessageMatcher} |
||||
* instances. |
||||
*/ |
||||
public final class Constraint { |
||||
|
||||
private final List<? extends MatcherBuilder> messageMatchers; |
||||
|
||||
/** |
||||
* Creates a new instance |
||||
* @param messageMatchers the {@link MessageMatcher} instances to map to this |
||||
* constraint |
||||
*/ |
||||
private Constraint(List<? extends MatcherBuilder> 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 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 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 (MatcherBuilder messageMatcher : this.messageMatchers) { |
||||
MessageSecurityMetadataSourceRegistry.this.matcherToExpression.put(messageMatcher, attribute); |
||||
} |
||||
return MessageSecurityMetadataSourceRegistry.this; |
||||
} |
||||
|
||||
} |
||||
|
||||
private static final class PreBuiltMatcherBuilder implements MatcherBuilder { |
||||
|
||||
private MessageMatcher<?> matcher; |
||||
|
||||
private PreBuiltMatcherBuilder(MessageMatcher<?> matcher) { |
||||
this.matcher = matcher; |
||||
} |
||||
|
||||
@Override |
||||
public MessageMatcher<?> build() { |
||||
return this.matcher; |
||||
} |
||||
|
||||
} |
||||
|
||||
private final class PathMatcherMessageMatcherBuilder implements MatcherBuilder { |
||||
|
||||
private final String pattern; |
||||
|
||||
private final SimpMessageType type; |
||||
|
||||
private PathMatcherMessageMatcherBuilder(String pattern, SimpMessageType type) { |
||||
this.pattern = pattern; |
||||
this.type = type; |
||||
} |
||||
|
||||
@Override |
||||
public MessageMatcher<?> build() { |
||||
if (this.type == null) { |
||||
return new SimpDestinationMessageMatcher(this.pattern, |
||||
MessageSecurityMetadataSourceRegistry.this.pathMatcher); |
||||
} |
||||
if (SimpMessageType.MESSAGE == this.type) { |
||||
return SimpDestinationMessageMatcher.createMessageMatcher(this.pattern, |
||||
MessageSecurityMetadataSourceRegistry.this.pathMatcher); |
||||
} |
||||
if (SimpMessageType.SUBSCRIBE == this.type) { |
||||
return SimpDestinationMessageMatcher.createSubscribeMatcher(this.pattern, |
||||
MessageSecurityMetadataSourceRegistry.this.pathMatcher); |
||||
} |
||||
throw new IllegalStateException(this.type + " is not supported since it does not have a destination"); |
||||
} |
||||
|
||||
} |
||||
|
||||
private interface MatcherBuilder { |
||||
|
||||
MessageMatcher<?> build(); |
||||
|
||||
} |
||||
|
||||
static class DelegatingPathMatcher implements PathMatcher { |
||||
|
||||
private PathMatcher delegate = new AntPathMatcher(); |
||||
|
||||
@Override |
||||
public boolean isPattern(String path) { |
||||
return this.delegate.isPattern(path); |
||||
} |
||||
|
||||
@Override |
||||
public boolean match(String pattern, String path) { |
||||
return this.delegate.match(pattern, path); |
||||
} |
||||
|
||||
@Override |
||||
public boolean matchStart(String pattern, String path) { |
||||
return this.delegate.matchStart(pattern, path); |
||||
} |
||||
|
||||
@Override |
||||
public String extractPathWithinPattern(String pattern, String path) { |
||||
return this.delegate.extractPathWithinPattern(pattern, path); |
||||
} |
||||
|
||||
@Override |
||||
public Map<String, String> extractUriTemplateVariables(String pattern, String path) { |
||||
return this.delegate.extractUriTemplateVariables(pattern, path); |
||||
} |
||||
|
||||
@Override |
||||
public Comparator<String> getPatternComparator(String path) { |
||||
return this.delegate.getPatternComparator(path); |
||||
} |
||||
|
||||
@Override |
||||
public String combine(String pattern1, String pattern2) { |
||||
return this.delegate.combine(pattern1, pattern2); |
||||
} |
||||
|
||||
void setPathMatcher(PathMatcher pathMatcher) { |
||||
this.delegate = pathMatcher; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,337 +0,0 @@
@@ -1,337 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2017 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.config.annotation.web.messaging; |
||||
|
||||
import java.util.Collection; |
||||
|
||||
import org.junit.jupiter.api.BeforeEach; |
||||
import org.junit.jupiter.api.Test; |
||||
import org.junit.jupiter.api.extension.ExtendWith; |
||||
import org.mockito.Mock; |
||||
import org.mockito.junit.jupiter.MockitoExtension; |
||||
|
||||
import org.springframework.messaging.Message; |
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor; |
||||
import org.springframework.messaging.simp.SimpMessageType; |
||||
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 org.springframework.util.AntPathMatcher; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
||||
import static org.mockito.BDDMockito.given; |
||||
|
||||
@ExtendWith(MockitoExtension.class) |
||||
public class MessageSecurityMetadataSourceRegistryTests { |
||||
|
||||
@Mock |
||||
private MessageMatcher<Object> matcher; |
||||
|
||||
private MessageSecurityMetadataSourceRegistry messages; |
||||
|
||||
private Message<String> message; |
||||
|
||||
@BeforeEach |
||||
public void setup() { |
||||
this.messages = new MessageSecurityMetadataSourceRegistry(); |
||||
// @formatter:off
|
||||
this.message = MessageBuilder.withPayload("Hi") |
||||
.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, "location") |
||||
.setHeader(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER, SimpMessageType.MESSAGE) |
||||
.build(); |
||||
// @formatter:on
|
||||
} |
||||
|
||||
// See
|
||||
// https://github.com/spring-projects/spring-security/commit/3f30529039c76facf335d6ca69d18d8ae287f3f9#commitcomment-7412712
|
||||
// https://jira.spring.io/browse/SPR-11660
|
||||
@Test |
||||
public void simpDestMatchersCustom() { |
||||
// @formatter:off
|
||||
this.message = MessageBuilder.withPayload("Hi") |
||||
.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, "price.stock.1.2") |
||||
.build(); |
||||
// @formatter:on
|
||||
this.messages.simpDestPathMatcher(new AntPathMatcher(".")).simpDestMatchers("price.stock.*").permitAll(); |
||||
assertThat(getAttribute()).isNull(); |
||||
// @formatter:off
|
||||
this.message = MessageBuilder.withPayload("Hi") |
||||
.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, "price.stock.1.2") |
||||
.build(); |
||||
// @formatter:on
|
||||
this.messages.simpDestPathMatcher(new AntPathMatcher(".")).simpDestMatchers("price.stock.**").permitAll(); |
||||
assertThat(getAttribute()).isEqualTo("permitAll"); |
||||
} |
||||
|
||||
@Test |
||||
public void simpDestMatchersCustomSetAfterMatchersDoesNotMatter() { |
||||
// @formatter:off
|
||||
this.message = MessageBuilder.withPayload("Hi") |
||||
.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, "price.stock.1.2") |
||||
.build(); |
||||
// @formatter:on
|
||||
this.messages.simpDestMatchers("price.stock.*").permitAll().simpDestPathMatcher(new AntPathMatcher(".")); |
||||
assertThat(getAttribute()).isNull(); |
||||
// @formatter:off
|
||||
this.message = MessageBuilder.withPayload("Hi") |
||||
.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, "price.stock.1.2") |
||||
.build(); |
||||
// @formatter:on
|
||||
this.messages.simpDestMatchers("price.stock.**").permitAll().simpDestPathMatcher(new AntPathMatcher(".")); |
||||
assertThat(getAttribute()).isEqualTo("permitAll"); |
||||
} |
||||
|
||||
@Test |
||||
public void pathMatcherNull() { |
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.messages.simpDestPathMatcher(null)); |
||||
} |
||||
|
||||
@Test |
||||
public void matchersFalse() { |
||||
this.messages.matchers(this.matcher).permitAll(); |
||||
assertThat(getAttribute()).isNull(); |
||||
} |
||||
|
||||
@Test |
||||
public void matchersTrue() { |
||||
given(this.matcher.matches(this.message)).willReturn(true); |
||||
this.messages.matchers(this.matcher).permitAll(); |
||||
assertThat(getAttribute()).isEqualTo("permitAll"); |
||||
} |
||||
|
||||
@Test |
||||
public void simpDestMatchersExact() { |
||||
this.messages.simpDestMatchers("location").permitAll(); |
||||
assertThat(getAttribute()).isEqualTo("permitAll"); |
||||
} |
||||
|
||||
@Test |
||||
public void simpDestMatchersMulti() { |
||||
// @formatter:off
|
||||
this.messages |
||||
.simpDestMatchers("admin/**", "api/**").hasRole("ADMIN") |
||||
.simpDestMatchers("location").permitAll(); |
||||
// @formatter:on
|
||||
assertThat(getAttribute()).isEqualTo("permitAll"); |
||||
} |
||||
|
||||
@Test |
||||
public void simpDestMatchersRole() { |
||||
// @formatter:off
|
||||
this.messages |
||||
.simpDestMatchers("admin/**", "location/**").hasRole("ADMIN") |
||||
.anyMessage().denyAll(); |
||||
// @formatter:on
|
||||
assertThat(getAttribute()).isEqualTo("hasRole('ROLE_ADMIN')"); |
||||
} |
||||
|
||||
@Test |
||||
public void simpDestMatchersAnyRole() { |
||||
// @formatter:off
|
||||
this.messages |
||||
.simpDestMatchers("admin/**", "location/**").hasAnyRole("ADMIN", "ROOT") |
||||
.anyMessage().denyAll(); |
||||
// @formatter:on
|
||||
assertThat(getAttribute()).isEqualTo("hasAnyRole('ROLE_ADMIN','ROLE_ROOT')"); |
||||
} |
||||
|
||||
@Test |
||||
public void simpDestMatchersAuthority() { |
||||
// @formatter:off
|
||||
this.messages |
||||
.simpDestMatchers("admin/**", "location/**").hasAuthority("ROLE_ADMIN") |
||||
.anyMessage().fullyAuthenticated(); |
||||
// @formatter:on
|
||||
assertThat(getAttribute()).isEqualTo("hasAuthority('ROLE_ADMIN')"); |
||||
} |
||||
|
||||
@Test |
||||
public void simpDestMatchersAccess() { |
||||
String expected = "hasRole('ROLE_ADMIN') and fullyAuthenticated"; |
||||
this.messages.simpDestMatchers("admin/**", "location/**").access(expected).anyMessage().denyAll(); |
||||
assertThat(getAttribute()).isEqualTo(expected); |
||||
} |
||||
|
||||
@Test |
||||
public void simpDestMatchersAnyAuthority() { |
||||
// @formatter:off
|
||||
this.messages |
||||
.simpDestMatchers("admin/**", "location/**").hasAnyAuthority("ROLE_ADMIN", "ROLE_ROOT") |
||||
.anyMessage().denyAll(); |
||||
// @formatter:on
|
||||
assertThat(getAttribute()).isEqualTo("hasAnyAuthority('ROLE_ADMIN','ROLE_ROOT')"); |
||||
} |
||||
|
||||
@Test |
||||
public void simpDestMatchersRememberMe() { |
||||
// @formatter:off
|
||||
this.messages |
||||
.simpDestMatchers("admin/**", "location/**").rememberMe() |
||||
.anyMessage().denyAll(); |
||||
// @formatter:on
|
||||
assertThat(getAttribute()).isEqualTo("rememberMe"); |
||||
} |
||||
|
||||
@Test |
||||
public void simpDestMatchersAnonymous() { |
||||
// @formatter:off
|
||||
this.messages |
||||
.simpDestMatchers("admin/**", "location/**").anonymous() |
||||
.anyMessage().denyAll(); |
||||
// @formatter:on
|
||||
assertThat(getAttribute()).isEqualTo("anonymous"); |
||||
} |
||||
|
||||
@Test |
||||
public void simpDestMatchersFullyAuthenticated() { |
||||
// @formatter:off
|
||||
this.messages |
||||
.simpDestMatchers("admin/**", "location/**").fullyAuthenticated() |
||||
.anyMessage().denyAll(); |
||||
// @formatter:on
|
||||
assertThat(getAttribute()).isEqualTo("fullyAuthenticated"); |
||||
} |
||||
|
||||
@Test |
||||
public void simpDestMatchersDenyAll() { |
||||
// @formatter:off
|
||||
this.messages |
||||
.simpDestMatchers("admin/**", "location/**").denyAll() |
||||
.anyMessage().permitAll(); |
||||
// @formatter:on
|
||||
assertThat(getAttribute()).isEqualTo("denyAll"); |
||||
} |
||||
|
||||
@Test |
||||
public void simpDestMessageMatchersNotMatch() { |
||||
// @formatter:off
|
||||
this.messages. |
||||
simpMessageDestMatchers("admin/**").denyAll() |
||||
.anyMessage().permitAll(); |
||||
// @formatter:on
|
||||
assertThat(getAttribute()).isEqualTo("permitAll"); |
||||
} |
||||
|
||||
@Test |
||||
public void simpDestMessageMatchersMatch() { |
||||
// @formatter:off
|
||||
this.messages |
||||
.simpMessageDestMatchers("location/**").denyAll() |
||||
.anyMessage().permitAll(); |
||||
// @formatter:on
|
||||
assertThat(getAttribute()).isEqualTo("denyAll"); |
||||
} |
||||
|
||||
@Test |
||||
public void simpDestSubscribeMatchersNotMatch() { |
||||
// @formatter:off
|
||||
this.messages |
||||
.simpSubscribeDestMatchers("location/**").denyAll() |
||||
.anyMessage().permitAll(); |
||||
// @formatter:on
|
||||
assertThat(getAttribute()).isEqualTo("permitAll"); |
||||
} |
||||
|
||||
@Test |
||||
public void simpDestSubscribeMatchersMatch() { |
||||
// @formatter:off
|
||||
this.message = MessageBuilder.fromMessage(this.message) |
||||
.setHeader(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER, SimpMessageType.SUBSCRIBE) |
||||
.build(); |
||||
this.messages |
||||
.simpSubscribeDestMatchers("location/**").denyAll() |
||||
.anyMessage().permitAll(); |
||||
// @formatter:on
|
||||
assertThat(getAttribute()).isEqualTo("denyAll"); |
||||
} |
||||
|
||||
@Test |
||||
public void nullDestMatcherNotMatches() { |
||||
// @formatter:off
|
||||
this.messages |
||||
.nullDestMatcher().denyAll() |
||||
.anyMessage().permitAll(); |
||||
// @formatter:on
|
||||
assertThat(getAttribute()).isEqualTo("permitAll"); |
||||
} |
||||
|
||||
@Test |
||||
public void nullDestMatcherMatch() { |
||||
// @formatter:off
|
||||
this.message = MessageBuilder.withPayload("Hi") |
||||
.setHeader(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER, SimpMessageType.CONNECT) |
||||
.build(); |
||||
this.messages |
||||
.nullDestMatcher().denyAll() |
||||
.anyMessage().permitAll(); |
||||
// @formatter:on
|
||||
assertThat(getAttribute()).isEqualTo("denyAll"); |
||||
} |
||||
|
||||
@Test |
||||
public void simpTypeMatchersMatch() { |
||||
// @formatter:off
|
||||
this.messages |
||||
.simpTypeMatchers(SimpMessageType.MESSAGE).denyAll() |
||||
.anyMessage().permitAll(); |
||||
// @formatter:on
|
||||
assertThat(getAttribute()).isEqualTo("denyAll"); |
||||
} |
||||
|
||||
@Test |
||||
public void simpTypeMatchersMatchMulti() { |
||||
// @formatter:off
|
||||
this.messages |
||||
.simpTypeMatchers(SimpMessageType.CONNECT, SimpMessageType.MESSAGE).denyAll() |
||||
.anyMessage().permitAll(); |
||||
// @formatter:on
|
||||
assertThat(getAttribute()).isEqualTo("denyAll"); |
||||
} |
||||
|
||||
@Test |
||||
public void simpTypeMatchersNotMatch() { |
||||
// @formatter:off
|
||||
this.messages |
||||
.simpTypeMatchers(SimpMessageType.CONNECT).denyAll() |
||||
.anyMessage().permitAll(); |
||||
// @formatter:on
|
||||
assertThat(getAttribute()).isEqualTo("permitAll"); |
||||
} |
||||
|
||||
@Test |
||||
public void simpTypeMatchersNotMatchMulti() { |
||||
// @formatter:off
|
||||
this.messages |
||||
.simpTypeMatchers(SimpMessageType.CONNECT, SimpMessageType.DISCONNECT).denyAll() |
||||
.anyMessage().permitAll(); |
||||
// @formatter:on
|
||||
assertThat(getAttribute()).isEqualTo("permitAll"); |
||||
} |
||||
|
||||
private String getAttribute() { |
||||
MessageSecurityMetadataSource source = this.messages.createMetadataSource(); |
||||
Collection<ConfigAttribute> attrs = source.getAttributes(this.message); |
||||
if (attrs == null) { |
||||
return null; |
||||
} |
||||
assertThat(attrs).hasSize(1); |
||||
return attrs.iterator().next().toString(); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue