Skip to content

Commit 97b53f0

Browse files
committed
Read Extracted Variables
Closes gh-12540
1 parent aea13b6 commit 97b53f0

File tree

2 files changed

+60
-46
lines changed

2 files changed

+60
-46
lines changed

messaging/src/main/java/org/springframework/security/messaging/access/intercept/MessageMatcherDelegatingAuthorizationManager.java

Lines changed: 26 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.util.ArrayList;
2020
import java.util.List;
21+
import java.util.Map;
2122
import java.util.function.Supplier;
2223

2324
import org.apache.commons.logging.Log;
@@ -37,6 +38,7 @@
3738
import org.springframework.util.AntPathMatcher;
3839
import org.springframework.util.Assert;
3940
import org.springframework.util.PathMatcher;
41+
import org.springframework.util.function.SingletonSupplier;
4042

4143
public final class MessageMatcherDelegatingAuthorizationManager implements AuthorizationManager<Message<?>> {
4244

@@ -87,6 +89,10 @@ private MessageAuthorizationContext<?> authorizationContext(MessageMatcher<?> ma
8789
SimpDestinationMessageMatcher simp = (SimpDestinationMessageMatcher) matcher;
8890
return new MessageAuthorizationContext<>(message, simp.extractPathVariables(message));
8991
}
92+
if (matcher instanceof Builder.LazySimpDestinationMessageMatcher) {
93+
Builder.LazySimpDestinationMessageMatcher path = (Builder.LazySimpDestinationMessageMatcher) matcher;
94+
return new MessageAuthorizationContext<>(message, path.extractPathVariables(message));
95+
}
9096
return new MessageAuthorizationContext<>(message);
9197
}
9298

@@ -192,8 +198,7 @@ public Builder.Constraint simpSubscribeDestMatchers(String... patterns) {
192198
private Builder.Constraint simpDestMatchers(SimpMessageType type, String... patterns) {
193199
List<MessageMatcher<?>> matchers = new ArrayList<>(patterns.length);
194200
for (String pattern : patterns) {
195-
Supplier<MessageMatcher<Object>> supplier = new Builder.PathMatcherMessageMatcherBuilder(pattern, type);
196-
MessageMatcher<?> matcher = new Builder.SupplierMessageMatcher(supplier);
201+
MessageMatcher<Object> matcher = new LazySimpDestinationMessageMatcher(pattern, type);
197202
matchers.add(matcher);
198203
}
199204
return new Builder.Constraint(matchers);
@@ -375,58 +380,33 @@ public Builder access(AuthorizationManager<MessageAuthorizationContext<?>> autho
375380

376381
}
377382

378-
private static final class SupplierMessageMatcher implements MessageMatcher<Object> {
379-
380-
private final Supplier<MessageMatcher<Object>> supplier;
383+
private final class LazySimpDestinationMessageMatcher implements MessageMatcher<Object> {
381384

382-
private volatile MessageMatcher<Object> delegate;
385+
private final Supplier<SimpDestinationMessageMatcher> delegate;
383386

384-
SupplierMessageMatcher(Supplier<MessageMatcher<Object>> supplier) {
385-
this.supplier = supplier;
387+
private LazySimpDestinationMessageMatcher(String pattern, SimpMessageType type) {
388+
this.delegate = SingletonSupplier.of(() -> {
389+
PathMatcher pathMatcher = Builder.this.pathMatcher.get();
390+
if (type == null) {
391+
return new SimpDestinationMessageMatcher(pattern, pathMatcher);
392+
}
393+
if (SimpMessageType.MESSAGE == type) {
394+
return SimpDestinationMessageMatcher.createMessageMatcher(pattern, pathMatcher);
395+
}
396+
if (SimpMessageType.SUBSCRIBE == type) {
397+
return SimpDestinationMessageMatcher.createSubscribeMatcher(pattern, pathMatcher);
398+
}
399+
throw new IllegalStateException(type + " is not supported since it does not have a destination");
400+
});
386401
}
387402

388403
@Override
389404
public boolean matches(Message<?> message) {
390-
if (this.delegate == null) {
391-
synchronized (this.supplier) {
392-
if (this.delegate == null) {
393-
this.delegate = this.supplier.get();
394-
}
395-
}
396-
}
397-
return this.delegate.matches(message);
398-
}
399-
400-
}
401-
402-
private final class PathMatcherMessageMatcherBuilder implements Supplier<MessageMatcher<Object>> {
403-
404-
private final String pattern;
405-
406-
private final SimpMessageType type;
407-
408-
private PathMatcherMessageMatcherBuilder(String pattern, SimpMessageType type) {
409-
this.pattern = pattern;
410-
this.type = type;
411-
}
412-
413-
private PathMatcher resolvePathMatcher() {
414-
return Builder.this.pathMatcher.get();
405+
return this.delegate.get().matches(message);
415406
}
416407

417-
@Override
418-
public MessageMatcher<Object> get() {
419-
PathMatcher pathMatcher = resolvePathMatcher();
420-
if (this.type == null) {
421-
return new SimpDestinationMessageMatcher(this.pattern, pathMatcher);
422-
}
423-
if (SimpMessageType.MESSAGE == this.type) {
424-
return SimpDestinationMessageMatcher.createMessageMatcher(this.pattern, pathMatcher);
425-
}
426-
if (SimpMessageType.SUBSCRIBE == this.type) {
427-
return SimpDestinationMessageMatcher.createSubscribeMatcher(this.pattern, pathMatcher);
428-
}
429-
throw new IllegalStateException(this.type + " is not supported since it does not have a destination");
408+
Map<String, String> extractPathVariables(Message<?> message) {
409+
return this.delegate.get().extractPathVariables(message);
430410
}
431411

432412
}

messaging/src/test/java/org/springframework/security/messaging/access/intercept/MessageMatcherDelegatingAuthorizationManagerTests.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.springframework.messaging.simp.SimpMessageType;
2828
import org.springframework.messaging.support.GenericMessage;
2929
import org.springframework.security.authentication.TestingAuthenticationToken;
30+
import org.springframework.security.authorization.AuthorizationDecision;
3031
import org.springframework.security.authorization.AuthorizationManager;
3132
import org.springframework.security.core.Authentication;
3233

@@ -87,8 +88,41 @@ void checkWhenSimpTypeMatchesThenUses() {
8788
assertThat(authorizationManager.check(mock(Supplier.class), message).isGranted()).isTrue();
8889
}
8990

91+
// gh-12540
92+
@Test
93+
void checkWhenSimpDestinationMatchesThenVariablesExtracted() {
94+
AuthorizationManager<Message<?>> authorizationManager = builder().simpDestMatchers("destination/{id}")
95+
.access(variable("id").isEqualTo("3")).anyMessage().denyAll().build();
96+
MessageHeaders headers = new MessageHeaders(
97+
Map.of(SimpMessageHeaderAccessor.DESTINATION_HEADER, "destination/3"));
98+
Message<?> message = new GenericMessage<>(new Object(), headers);
99+
assertThat(authorizationManager.check(mock(Supplier.class), message).isGranted()).isTrue();
100+
}
101+
90102
private MessageMatcherDelegatingAuthorizationManager.Builder builder() {
91103
return MessageMatcherDelegatingAuthorizationManager.builder();
92104
}
93105

106+
private Builder variable(String name) {
107+
return new Builder(name);
108+
109+
}
110+
111+
private static final class Builder {
112+
113+
private final String name;
114+
115+
private Builder(String name) {
116+
this.name = name;
117+
}
118+
119+
AuthorizationManager<MessageAuthorizationContext<?>> isEqualTo(String value) {
120+
return (authentication, object) -> {
121+
String extracted = object.getVariables().get(this.name);
122+
return new AuthorizationDecision(value.equals(extracted));
123+
};
124+
}
125+
126+
}
127+
94128
}

0 commit comments

Comments
 (0)