Move Messaging Access API
Issue gh-17847
This commit is contained in:
@@ -11,6 +11,8 @@ dependencies {
|
||||
api 'org.springframework:spring-expression'
|
||||
api 'io.micrometer:micrometer-observation'
|
||||
|
||||
optional project(':spring-security-messaging')
|
||||
optional 'org.springframework:spring-websocket'
|
||||
optional 'com.fasterxml.jackson.core:jackson-databind'
|
||||
optional 'io.micrometer:context-propagation'
|
||||
optional 'io.projectreactor:reactor-core'
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.messaging.access.expression;
|
||||
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
|
||||
/**
|
||||
* Allows post processing the {@link EvaluationContext}
|
||||
*
|
||||
* <p>
|
||||
* This API is intentionally kept package scope as it may evolve over time.
|
||||
* </p>
|
||||
*
|
||||
* @author Daniel Bustamante Ospina
|
||||
* @since 5.2
|
||||
* @deprecated Since {@link MessageExpressionVoter} is deprecated, there is no more need
|
||||
* for this class
|
||||
*/
|
||||
@Deprecated
|
||||
interface EvaluationContextPostProcessor<I> {
|
||||
|
||||
/**
|
||||
* Allows post processing of the {@link EvaluationContext}. Implementations may return
|
||||
* a new instance of {@link EvaluationContext} or modify the {@link EvaluationContext}
|
||||
* that was passed in.
|
||||
* @param context the original {@link EvaluationContext}
|
||||
* @param invocation the security invocation object (i.e. Message)
|
||||
* @return the upated context.
|
||||
*/
|
||||
EvaluationContext postProcess(EvaluationContext context, I invocation);
|
||||
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.messaging.access.expression;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.messaging.access.intercept.DefaultMessageSecurityMetadataSource;
|
||||
import org.springframework.security.messaging.access.intercept.MessageSecurityMetadataSource;
|
||||
import org.springframework.security.messaging.util.matcher.MessageMatcher;
|
||||
|
||||
/**
|
||||
* A class used to create a {@link MessageSecurityMetadataSource} that uses
|
||||
* {@link MessageMatcher} mapped to Spring Expressions.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 4.0
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
public final class ExpressionBasedMessageSecurityMetadataSourceFactory {
|
||||
|
||||
private 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<?>,String> matcherToExpression = new LinkedHashMap<MessageMatcher<Object>,String>();
|
||||
* matcherToExpression.put(new SimDestinationMessageMatcher("/public/**"), "permitAll");
|
||||
* matcherToExpression.put(new SimDestinationMessageMatcher("/admin/**"), "hasRole('ROLE_ADMIN')");
|
||||
* matcherToExpression.put(new SimDestinationMessageMatcher("/topics/{name}/**"), "@someBean.customLogic(authentication, #name)");
|
||||
* 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>
|
||||
* For a complete listing of expressions see {@link MessageSecurityExpressionRoot}
|
||||
* @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) {
|
||||
return createExpressionMessageMetadataSource(matcherToExpression,
|
||||
new DefaultMessageSecurityExpressionHandler<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<?>,String> matcherToExpression = new LinkedHashMap<MessageMatcher<Object>,String>();
|
||||
* matcherToExpression.put(new SimDestinationMessageMatcher("/public/**"), "permitAll");
|
||||
* matcherToExpression.put(new SimDestinationMessageMatcher("/admin/**"), "hasRole('ROLE_ADMIN')");
|
||||
* matcherToExpression.put(new SimDestinationMessageMatcher("/topics/{name}/**"), "@someBean.customLogic(authentication, #name)");
|
||||
* 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()}
|
||||
* @param handler the {@link SecurityExpressionHandler} to use
|
||||
* @return the {@link MessageSecurityMetadataSource} to use. Cannot be null.
|
||||
*/
|
||||
public static MessageSecurityMetadataSource createExpressionMessageMetadataSource(
|
||||
LinkedHashMap<MessageMatcher<?>, String> matcherToExpression,
|
||||
SecurityExpressionHandler<Message<Object>> handler) {
|
||||
LinkedHashMap<MessageMatcher<?>, Collection<ConfigAttribute>> matcherToAttrs = new LinkedHashMap<>();
|
||||
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, matcher);
|
||||
matcherToAttrs.put(matcher, Arrays.asList(attribute));
|
||||
}
|
||||
return new DefaultMessageSecurityMetadataSource(matcherToAttrs);
|
||||
}
|
||||
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.messaging.access.expression;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
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.messaging.util.matcher.MessageMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple expression configuration attribute for use in {@link Message} authorizations.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Daniel Bustamante Ospina
|
||||
* @since 4.0
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("serial")
|
||||
class MessageExpressionConfigAttribute implements ConfigAttribute, EvaluationContextPostProcessor<Message<?>> {
|
||||
|
||||
private final Expression authorizeExpression;
|
||||
|
||||
private final MessageMatcher<Object> matcher;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param authorizeExpression the {@link Expression} to use. Cannot be null
|
||||
* @param matcher the {@link MessageMatcher} used to match the messages.
|
||||
*/
|
||||
MessageExpressionConfigAttribute(Expression authorizeExpression, MessageMatcher<?> matcher) {
|
||||
Assert.notNull(authorizeExpression, "authorizeExpression cannot be null");
|
||||
Assert.notNull(matcher, "matcher cannot be null");
|
||||
this.authorizeExpression = authorizeExpression;
|
||||
this.matcher = (MessageMatcher<Object>) matcher;
|
||||
}
|
||||
|
||||
Expression getAuthorizeExpression() {
|
||||
return this.authorizeExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getAttribute() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.authorizeExpression.getExpressionString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EvaluationContext postProcess(EvaluationContext ctx, Message<?> message) {
|
||||
Map<String, String> variables = this.matcher.matcher(message).getVariables();
|
||||
for (Map.Entry<String, String> entry : variables.entrySet()) {
|
||||
ctx.setVariable(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.messaging.access.expression;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Daniel Bustamante Ospina
|
||||
* @since 4.0
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
public class MessageExpressionVoter<T> implements AccessDecisionVoter<Message<T>> {
|
||||
|
||||
private SecurityExpressionHandler<Message<T>> expressionHandler = new DefaultMessageSecurityExpressionHandler<>();
|
||||
|
||||
@Override
|
||||
public int vote(Authentication authentication, Message<T> message, Collection<ConfigAttribute> attributes) {
|
||||
Assert.notNull(authentication, "authentication must not be null");
|
||||
Assert.notNull(message, "message must not be null");
|
||||
Assert.notNull(attributes, "attributes must not be null");
|
||||
MessageExpressionConfigAttribute attr = findConfigAttribute(attributes);
|
||||
if (attr == null) {
|
||||
return ACCESS_ABSTAIN;
|
||||
}
|
||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, message);
|
||||
ctx = attr.postProcess(ctx, message);
|
||||
return ExpressionUtils.evaluateAsBoolean(attr.getAuthorizeExpression(), ctx) ? ACCESS_GRANTED : ACCESS_DENIED;
|
||||
}
|
||||
|
||||
private @Nullable MessageExpressionConfigAttribute findConfigAttribute(Collection<ConfigAttribute> attributes) {
|
||||
for (ConfigAttribute attribute : attributes) {
|
||||
if (attribute instanceof MessageExpressionConfigAttribute) {
|
||||
return (MessageExpressionConfigAttribute) attribute;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return attribute instanceof MessageExpressionConfigAttribute;
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.messaging.access.intercept;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
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>
|
||||
* Refer to {@link AbstractSecurityInterceptor} for details on the workflow.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 4.0
|
||||
* @deprecated Use {@code AuthorizationChannelInterceptor} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public final class ChannelSecurityInterceptor extends AbstractSecurityInterceptor implements ChannelInterceptor {
|
||||
|
||||
private static final ThreadLocal<InterceptorStatusToken> tokenHolder = new ThreadLocal<>();
|
||||
|
||||
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 this.metadataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<?> preSend(Message<?> message, MessageChannel channel) {
|
||||
InterceptorStatusToken token = beforeInvocation(message);
|
||||
if (token != null) {
|
||||
tokenHolder.set(token);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
|
||||
InterceptorStatusToken token = clearToken();
|
||||
afterInvocation(token, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, @Nullable Exception ex) {
|
||||
InterceptorStatusToken token = clearToken();
|
||||
finallyInvocation(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preReceive(MessageChannel channel) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<?> postReceive(Message<?> message, MessageChannel channel) {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterReceiveCompletion(@Nullable Message<?> message, MessageChannel channel, @Nullable Exception ex) {
|
||||
}
|
||||
|
||||
private InterceptorStatusToken clearToken() {
|
||||
InterceptorStatusToken token = tokenHolder.get();
|
||||
tokenHolder.remove();
|
||||
return token;
|
||||
}
|
||||
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.messaging.access.intercept;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 4.0
|
||||
* @see ChannelSecurityInterceptor
|
||||
* @see ExpressionBasedMessageSecurityMetadataSourceFactory
|
||||
* @deprecated Use {@link MessageMatcherDelegatingAuthorizationManager} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public final class DefaultMessageSecurityMetadataSource implements MessageSecurityMetadataSource {
|
||||
|
||||
private final Map<MessageMatcher<?>, Collection<ConfigAttribute>> messageMap;
|
||||
|
||||
public DefaultMessageSecurityMetadataSource(
|
||||
LinkedHashMap<MessageMatcher<?>, Collection<ConfigAttribute>> messageMap) {
|
||||
this.messageMap = messageMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
|
||||
final Message message = (Message) object;
|
||||
for (Map.Entry<MessageMatcher<?>, Collection<ConfigAttribute>> entry : this.messageMap.entrySet()) {
|
||||
if (entry.getKey().matches(message)) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ConfigAttribute> getAllConfigAttributes() {
|
||||
Set<ConfigAttribute> allAttributes = new HashSet<>();
|
||||
for (Collection<ConfigAttribute> entry : this.messageMap.values()) {
|
||||
allAttributes.addAll(entry);
|
||||
}
|
||||
return allAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return Message.class.isAssignableFrom(clazz);
|
||||
}
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.messaging.access.intercept;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.security.access.SecurityMetadataSource;
|
||||
|
||||
/**
|
||||
* A {@link SecurityMetadataSource} that is used for securing {@link Message}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 4.0
|
||||
* @see ChannelSecurityInterceptor
|
||||
* @see DefaultMessageSecurityMetadataSource
|
||||
* @deprecated Use {@link MessageMatcherDelegatingAuthorizationManager} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public interface MessageSecurityMetadataSource extends SecurityMetadataSource {
|
||||
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.messaging.access.expression;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
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.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 static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class ExpressionBasedMessageSecurityMetadataSourceFactoryTests {
|
||||
|
||||
@Mock
|
||||
MessageMatcher<Object> matcher1;
|
||||
|
||||
@Mock
|
||||
MessageMatcher<Object> matcher2;
|
||||
|
||||
@Mock
|
||||
Message<Object> message;
|
||||
|
||||
@Mock
|
||||
Authentication authentication;
|
||||
|
||||
String expression1;
|
||||
|
||||
String expression2;
|
||||
|
||||
LinkedHashMap<MessageMatcher<?>, String> matcherToExpression;
|
||||
|
||||
MessageSecurityMetadataSource source;
|
||||
|
||||
MessageSecurityExpressionRoot rootObject;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
this.expression1 = "permitAll";
|
||||
this.expression2 = "denyAll";
|
||||
this.matcherToExpression = new LinkedHashMap<>();
|
||||
this.matcherToExpression.put(this.matcher1, this.expression1);
|
||||
this.matcherToExpression.put(this.matcher2, this.expression2);
|
||||
this.source = ExpressionBasedMessageSecurityMetadataSourceFactory
|
||||
.createExpressionMessageMetadataSource(this.matcherToExpression);
|
||||
this.rootObject = new MessageSecurityExpressionRoot(this.authentication, this.message);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createExpressionMessageMetadataSourceNoMatch() {
|
||||
Collection<ConfigAttribute> attrs = this.source.getAttributes(this.message);
|
||||
assertThat(attrs).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createExpressionMessageMetadataSourceMatchFirst() {
|
||||
given(this.matcher1.matches(this.message)).willReturn(true);
|
||||
Collection<ConfigAttribute> attrs = this.source.getAttributes(this.message);
|
||||
assertThat(attrs).hasSize(1);
|
||||
ConfigAttribute attr = attrs.iterator().next();
|
||||
assertThat(attr).isInstanceOf(MessageExpressionConfigAttribute.class);
|
||||
assertThat(((MessageExpressionConfigAttribute) attr).getAuthorizeExpression().getValue(this.rootObject))
|
||||
.isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createExpressionMessageMetadataSourceMatchSecond() {
|
||||
given(this.matcher2.matches(this.message)).willReturn(true);
|
||||
Collection<ConfigAttribute> attrs = this.source.getAttributes(this.message);
|
||||
assertThat(attrs).hasSize(1);
|
||||
ConfigAttribute attr = attrs.iterator().next();
|
||||
assertThat(attr).isInstanceOf(MessageExpressionConfigAttribute.class);
|
||||
assertThat(((MessageExpressionConfigAttribute) attr).getAuthorizeExpression().getValue(this.rootObject))
|
||||
.isEqualTo(false);
|
||||
}
|
||||
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.messaging.access.expression;
|
||||
|
||||
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.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.security.messaging.util.matcher.MessageMatcher;
|
||||
import org.springframework.security.messaging.util.matcher.PathPatternMessageMatcher;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class MessageExpressionConfigAttributeTests {
|
||||
|
||||
@Mock
|
||||
Expression expression;
|
||||
|
||||
@Mock
|
||||
MessageMatcher<?> matcher;
|
||||
|
||||
MessageExpressionConfigAttribute attribute;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
this.attribute = new MessageExpressionConfigAttribute(this.expression, this.matcher);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorNullExpression() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new MessageExpressionConfigAttribute(null, this.matcher));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorNullMatcher() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new MessageExpressionConfigAttribute(this.expression, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAuthorizeExpression() {
|
||||
assertThat(this.attribute.getAuthorizeExpression()).isSameAs(this.expression);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAttribute() {
|
||||
assertThat(this.attribute.getAttribute()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toStringUsesExpressionString() {
|
||||
given(this.expression.getExpressionString()).willReturn("toString");
|
||||
assertThat(this.attribute.toString()).isEqualTo(this.expression.getExpressionString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessContext() {
|
||||
PathPatternMessageMatcher matcher = PathPatternMessageMatcher.withDefaults().matcher("/topics/{topic}/**");
|
||||
// @formatter:off
|
||||
Message<?> message = MessageBuilder.withPayload("M")
|
||||
.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, "/topics/someTopic/sub1")
|
||||
.build();
|
||||
// @formatter:on
|
||||
EvaluationContext context = mock(EvaluationContext.class);
|
||||
this.attribute = new MessageExpressionConfigAttribute(this.expression, matcher);
|
||||
this.attribute.postProcess(context, message);
|
||||
verify(context).setVariable("topic", "someTopic");
|
||||
}
|
||||
|
||||
}
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.messaging.access.expression;
|
||||
|
||||
import java.util.Arrays;
|
||||
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.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.security.access.AccessDecisionVoter;
|
||||
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 org.springframework.security.messaging.util.matcher.MessageMatcher;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class MessageExpressionVoterTests {
|
||||
|
||||
@Mock
|
||||
Authentication authentication;
|
||||
|
||||
@Mock
|
||||
Message<Object> message;
|
||||
|
||||
Collection<ConfigAttribute> attributes;
|
||||
|
||||
@Mock
|
||||
Expression expression;
|
||||
|
||||
@Mock
|
||||
MessageMatcher<?> matcher;
|
||||
|
||||
@Mock
|
||||
SecurityExpressionHandler<Message> expressionHandler;
|
||||
|
||||
@Mock
|
||||
EvaluationContext evaluationContext;
|
||||
|
||||
MessageExpressionVoter voter;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
this.attributes = Arrays
|
||||
.<ConfigAttribute>asList(new MessageExpressionConfigAttribute(this.expression, this.matcher));
|
||||
this.voter = new MessageExpressionVoter();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void voteGranted() {
|
||||
given(this.expression.getValue(any(EvaluationContext.class), eq(Boolean.class))).willReturn(true);
|
||||
given(this.matcher.matcher(any())).willCallRealMethod();
|
||||
assertThat(this.voter.vote(this.authentication, this.message, this.attributes))
|
||||
.isEqualTo(AccessDecisionVoter.ACCESS_GRANTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void voteDenied() {
|
||||
given(this.expression.getValue(any(EvaluationContext.class), eq(Boolean.class))).willReturn(false);
|
||||
given(this.matcher.matcher(any())).willCallRealMethod();
|
||||
assertThat(this.voter.vote(this.authentication, this.message, this.attributes))
|
||||
.isEqualTo(AccessDecisionVoter.ACCESS_DENIED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void voteAbstain() {
|
||||
this.attributes = Arrays.<ConfigAttribute>asList(new SecurityConfig("ROLE_USER"));
|
||||
assertThat(this.voter.vote(this.authentication, this.message, this.attributes))
|
||||
.isEqualTo(AccessDecisionVoter.ACCESS_ABSTAIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsObjectClassFalse() {
|
||||
assertThat(this.voter.supports(Object.class)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsMessageClassTrue() {
|
||||
assertThat(this.voter.supports(Message.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsSecurityConfigFalse() {
|
||||
assertThat(this.voter.supports(new SecurityConfig("ROLE_USER"))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsMessageExpressionConfigAttributeTrue() {
|
||||
assertThat(this.voter.supports(new MessageExpressionConfigAttribute(this.expression, this.matcher))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setExpressionHandlerNull() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.voter.setExpressionHandler(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customExpressionHandler() {
|
||||
this.voter.setExpressionHandler(this.expressionHandler);
|
||||
given(this.expressionHandler.createEvaluationContext(this.authentication, this.message))
|
||||
.willReturn(this.evaluationContext);
|
||||
given(this.expression.getValue(this.evaluationContext, Boolean.class)).willReturn(true);
|
||||
given(this.matcher.matcher(any())).willCallRealMethod();
|
||||
assertThat(this.voter.vote(this.authentication, this.message, this.attributes))
|
||||
.isEqualTo(AccessDecisionVoter.ACCESS_GRANTED);
|
||||
verify(this.expressionHandler).createEvaluationContext(this.authentication, this.message);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessEvaluationContext() {
|
||||
final MessageExpressionConfigAttribute configAttribute = mock(MessageExpressionConfigAttribute.class);
|
||||
this.voter.setExpressionHandler(this.expressionHandler);
|
||||
given(this.expressionHandler.createEvaluationContext(this.authentication, this.message))
|
||||
.willReturn(this.evaluationContext);
|
||||
given(configAttribute.getAuthorizeExpression()).willReturn(this.expression);
|
||||
this.attributes = Arrays.<ConfigAttribute>asList(configAttribute);
|
||||
given(configAttribute.postProcess(this.evaluationContext, this.message)).willReturn(this.evaluationContext);
|
||||
given(this.expression.getValue(any(EvaluationContext.class), eq(Boolean.class))).willReturn(true);
|
||||
assertThat(this.voter.vote(this.authentication, this.message, this.attributes))
|
||||
.isEqualTo(AccessDecisionVoter.ACCESS_GRANTED);
|
||||
verify(configAttribute).postProcess(this.evaluationContext, this.message);
|
||||
}
|
||||
|
||||
}
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.messaging.access.intercept;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
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.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.RunAsManager;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.willThrow;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class ChannelSecurityInterceptorTests {
|
||||
|
||||
@Mock
|
||||
Message<Object> message;
|
||||
|
||||
@Mock
|
||||
MessageChannel channel;
|
||||
|
||||
@Mock
|
||||
MessageSecurityMetadataSource source;
|
||||
|
||||
@Mock
|
||||
AccessDecisionManager accessDecisionManager;
|
||||
|
||||
@Mock
|
||||
RunAsManager runAsManager;
|
||||
|
||||
@Mock
|
||||
Authentication runAs;
|
||||
|
||||
Authentication originalAuth;
|
||||
|
||||
List<ConfigAttribute> attrs;
|
||||
|
||||
ChannelSecurityInterceptor interceptor;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
this.attrs = Arrays.<ConfigAttribute>asList(new SecurityConfig("ROLE_USER"));
|
||||
this.interceptor = new ChannelSecurityInterceptor(this.source);
|
||||
this.interceptor.setAccessDecisionManager(this.accessDecisionManager);
|
||||
this.interceptor.setRunAsManager(this.runAsManager);
|
||||
this.originalAuth = new TestingAuthenticationToken("user", "pass", "ROLE_USER");
|
||||
SecurityContextHolder.getContext().setAuthentication(this.originalAuth);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void cleanup() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorMessageSecurityMetadataSourceNull() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new ChannelSecurityInterceptor(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSecureObjectClass() {
|
||||
assertThat(this.interceptor.getSecureObjectClass()).isEqualTo(Message.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void obtainSecurityMetadataSource() {
|
||||
assertThat(this.interceptor.obtainSecurityMetadataSource()).isEqualTo(this.source);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendNullAttributes() {
|
||||
assertThat(this.interceptor.preSend(this.message, this.channel)).isSameAs(this.message);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendGrant() {
|
||||
given(this.source.getAttributes(this.message)).willReturn(this.attrs);
|
||||
Message<?> result = this.interceptor.preSend(this.message, this.channel);
|
||||
assertThat(result).isSameAs(this.message);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendDeny() {
|
||||
given(this.source.getAttributes(this.message)).willReturn(this.attrs);
|
||||
willThrow(new AccessDeniedException("")).given(this.accessDecisionManager)
|
||||
.decide(any(Authentication.class), eq(this.message), eq(this.attrs));
|
||||
assertThatExceptionOfType(AccessDeniedException.class)
|
||||
.isThrownBy(() -> this.interceptor.preSend(this.message, this.channel));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void preSendPostSendRunAs() {
|
||||
given(this.source.getAttributes(this.message)).willReturn(this.attrs);
|
||||
given(this.runAsManager.buildRunAs(any(Authentication.class), any(), any(Collection.class)))
|
||||
.willReturn(this.runAs);
|
||||
Message<?> preSend = this.interceptor.preSend(this.message, this.channel);
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.runAs);
|
||||
this.interceptor.postSend(preSend, this.channel, true);
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.originalAuth);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterSendCompletionNotTokenMessageNoExceptionThrown() {
|
||||
this.interceptor.afterSendCompletion(this.message, this.channel, true, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void preSendFinallySendRunAs() {
|
||||
given(this.source.getAttributes(this.message)).willReturn(this.attrs);
|
||||
given(this.runAsManager.buildRunAs(any(Authentication.class), any(), any(Collection.class)))
|
||||
.willReturn(this.runAs);
|
||||
Message<?> preSend = this.interceptor.preSend(this.message, this.channel);
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.runAs);
|
||||
this.interceptor.afterSendCompletion(preSend, this.channel, true, new RuntimeException());
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.originalAuth);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preReceive() {
|
||||
assertThat(this.interceptor.preReceive(this.channel)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postReceive() {
|
||||
assertThat(this.interceptor.postReceive(this.message, this.channel)).isSameAs(this.message);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterReceiveCompletionNullExceptionNoExceptionThrown() {
|
||||
this.interceptor.afterReceiveCompletion(this.message, this.channel, null);
|
||||
}
|
||||
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.messaging.access.intercept;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
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.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.messaging.util.matcher.MessageMatcher;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class DefaultMessageSecurityMetadataSourceTests {
|
||||
|
||||
@Mock
|
||||
MessageMatcher<Object> matcher1;
|
||||
|
||||
@Mock
|
||||
MessageMatcher<Object> matcher2;
|
||||
|
||||
@Mock
|
||||
Message<?> message;
|
||||
|
||||
@Mock
|
||||
Authentication authentication;
|
||||
|
||||
SecurityConfig config1;
|
||||
|
||||
SecurityConfig config2;
|
||||
|
||||
LinkedHashMap<MessageMatcher<?>, Collection<ConfigAttribute>> messageMap;
|
||||
|
||||
MessageSecurityMetadataSource source;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
this.messageMap = new LinkedHashMap<>();
|
||||
this.messageMap.put(this.matcher1, Arrays.<ConfigAttribute>asList(this.config1));
|
||||
this.messageMap.put(this.matcher2, Arrays.<ConfigAttribute>asList(this.config2));
|
||||
this.source = new DefaultMessageSecurityMetadataSource(this.messageMap);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAttributesEmpty() {
|
||||
assertThat(this.source.getAttributes(this.message)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAttributesFirst() {
|
||||
given(this.matcher1.matches(this.message)).willReturn(true);
|
||||
assertThat(this.source.getAttributes(this.message)).containsOnly(this.config1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAttributesSecond() {
|
||||
given(this.matcher1.matches(this.message)).willReturn(true);
|
||||
assertThat(this.source.getAttributes(this.message)).containsOnly(this.config2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAllConfigAttributes() {
|
||||
assertThat(this.source.getAllConfigAttributes()).containsOnly(this.config1, this.config2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsFalse() {
|
||||
assertThat(this.source.supports(Object.class)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsTrue() {
|
||||
assertThat(this.source.supports(Message.class)).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user