mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-07-12 13:23:29 +00:00
Remove MessageSecurityMetadataSourceRegistry
Issue gh-17295
This commit is contained in:
parent
686cc5fc1f
commit
4d3024cb49
@ -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 @@
|
||||
/*
|
||||
* 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…
x
Reference in New Issue
Block a user