1717package org .springframework .security .messaging .access .intercept ;
1818
1919import java .util .ArrayList ;
20+ import java .util .Arrays ;
2021import java .util .List ;
2122import java .util .Map ;
2223import java .util .function .Supplier ;
2526import org .apache .commons .logging .LogFactory ;
2627
2728import org .springframework .core .log .LogMessage ;
29+ import org .springframework .http .server .PathContainer ;
2830import org .springframework .messaging .Message ;
2931import org .springframework .messaging .simp .SimpMessageType ;
3032import org .springframework .security .authorization .AuthenticatedAuthorizationManager ;
3436import org .springframework .security .authorization .SingleResultAuthorizationManager ;
3537import org .springframework .security .core .Authentication ;
3638import org .springframework .security .messaging .util .matcher .MessageMatcher ;
39+ import org .springframework .security .messaging .util .matcher .PathPatternMessageMatcher ;
3740import org .springframework .security .messaging .util .matcher .SimpDestinationMessageMatcher ;
3841import org .springframework .security .messaging .util .matcher .SimpMessageTypeMatcher ;
3942import org .springframework .util .AntPathMatcher ;
4043import org .springframework .util .Assert ;
4144import org .springframework .util .PathMatcher ;
4245import org .springframework .util .function .SingletonSupplier ;
46+ import org .springframework .web .util .pattern .PathPatternParser ;
4347
4448public final class MessageMatcherDelegatingAuthorizationManager implements AuthorizationManager <Message <?>> {
4549
@@ -88,12 +92,11 @@ private MessageAuthorizationContext<?> authorizationContext(MessageMatcher<?> ma
8892 if (!matcher .matches ((Message ) message )) {
8993 return null ;
9094 }
91- if (matcher instanceof SimpDestinationMessageMatcher simp ) {
92- return new MessageAuthorizationContext <>(message , simp .extractPathVariables (message ));
95+ if (matcher instanceof Builder . LazySimpDestinationMessageMatcher pathMatcher ) {
96+ return new MessageAuthorizationContext <>(message , pathMatcher .extractPathVariables (message ));
9397 }
94- if (matcher instanceof Builder .LazySimpDestinationMessageMatcher ) {
95- Builder .LazySimpDestinationMessageMatcher path = (Builder .LazySimpDestinationMessageMatcher ) matcher ;
96- return new MessageAuthorizationContext <>(message , path .extractPathVariables (message ));
98+ if (matcher instanceof Builder .LazySimpDestinationPatternMessageMatcher pathMatcher ) {
99+ return new MessageAuthorizationContext <>(message , pathMatcher .extractPathVariables (message ));
97100 }
98101 return new MessageAuthorizationContext <>(message );
99102 }
@@ -113,8 +116,11 @@ public static final class Builder {
113116
114117 private final List <Entry <AuthorizationManager <MessageAuthorizationContext <?>>>> mappings = new ArrayList <>();
115118
119+ @ Deprecated
116120 private Supplier <PathMatcher > pathMatcher = AntPathMatcher ::new ;
117121
122+ private boolean useHttpPathSeparator = true ;
123+
118124 public Builder () {
119125 }
120126
@@ -133,11 +139,11 @@ public Builder.Constraint anyMessage() {
133139 * @return the Expression to associate
134140 */
135141 public Builder .Constraint nullDestMatcher () {
136- return matchers (SimpDestinationMessageMatcher .NULL_DESTINATION_MATCHER );
142+ return matchers (PathPatternMessageMatcher .NULL_DESTINATION_MATCHER );
137143 }
138144
139145 /**
140- * Maps a {@link List} of {@link SimpDestinationMessageMatcher } instances.
146+ * Maps a {@link List} of {@link SimpMessageTypeMatcher } instances.
141147 * @param typesToMatch the {@link SimpMessageType} instance to match on
142148 * @return the {@link Builder.Constraint} associated to the matchers.
143149 */
@@ -157,35 +163,88 @@ public Builder.Constraint simpTypeMatchers(SimpMessageType... typesToMatch) {
157163 * @param patterns the patterns to create
158164 * {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher}
159165 * from.
166+ * @deprecated use {@link #destinationPathPatterns(String...)}
160167 */
168+ @ Deprecated
161169 public Builder .Constraint simpDestMatchers (String ... patterns ) {
162170 return simpDestMatchers (null , patterns );
163171 }
164172
173+ /**
174+ * Allows the creation of a security {@link Constraint} applying to messages whose
175+ * destinations match the provided {@code patterns}.
176+ * <p>
177+ * The matching of each pattern is performed by a
178+ * {@link PathPatternMessageMatcher} instance that matches irrespectively of
179+ * {@link SimpMessageType}. If no destination is found on the {@code Message},
180+ * then each {@code Matcher} returns false.
181+ * </p>
182+ * @param patterns the destination path patterns to which the security
183+ * {@code Constraint} will be applicable
184+ * @since 6.5
185+ */
186+ public Builder .Constraint destinationPathPatterns (String ... patterns ) {
187+ return destinationPathPatterns (null , patterns );
188+ }
189+
165190 /**
166191 * Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances that
167192 * match on {@code SimpMessageType.MESSAGE}. If no destination is found on the
168193 * Message, then the Matcher returns false.
169194 * @param patterns the patterns to create
170195 * {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher}
171196 * from.
197+ * @deprecated use {@link #destinationPathPatterns(String...)}
172198 */
199+ @ Deprecated
173200 public Builder .Constraint simpMessageDestMatchers (String ... patterns ) {
174201 return simpDestMatchers (SimpMessageType .MESSAGE , patterns );
175202 }
176203
204+ /**
205+ * Allows the creation of a security {@link Constraint} applying to messages of
206+ * the type {@code SimpMessageType.MESSAGE} whose destinations match the provided
207+ * {@code patterns}.
208+ * <p>
209+ * The matching of each pattern is performed by a
210+ * {@link PathPatternMessageMatcher}. If no destination is found on the
211+ * {@code Message}, then each {@code Matcher} returns false.
212+ * @param patterns the patterns to create {@link PathPatternMessageMatcher} from.
213+ * @since 6.5
214+ */
215+ public Builder .Constraint simpTypeMessageDestinationPatterns (String ... patterns ) {
216+ return destinationPathPatterns (SimpMessageType .MESSAGE , patterns );
217+ }
218+
177219 /**
178220 * Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances that
179221 * match on {@code SimpMessageType.SUBSCRIBE}. If no destination is found on the
180222 * Message, then the Matcher returns false.
181223 * @param patterns the patterns to create
182224 * {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher}
183225 * from.
226+ * @deprecated use {@link #simpTypeSubscribeDestinationPatterns(String...)}
184227 */
228+ @ Deprecated
185229 public Builder .Constraint simpSubscribeDestMatchers (String ... patterns ) {
186230 return simpDestMatchers (SimpMessageType .SUBSCRIBE , patterns );
187231 }
188232
233+ /**
234+ * Allows the creation of a security {@link Constraint} applying to messages of
235+ * the type {@code SimpMessageType.SUBSCRIBE} whose destinations match the
236+ * provided {@code patterns}.
237+ * <p>
238+ * The matching of each pattern is performed by a
239+ * {@link PathPatternMessageMatcher}. If no destination is found on the
240+ * {@code Message}, then each {@code Matcher} returns false.
241+ * @param patterns the patterns to create {@link PathPatternMessageMatcher} from.
242+ * @since 6.5
243+ */
244+ public Builder .Constraint simpTypeSubscribeDestinationPatterns (String ... patterns ) {
245+ return destinationPathPatterns (SimpMessageType .SUBSCRIBE , patterns );
246+ }
247+
189248 /**
190249 * Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances. If no
191250 * destination is found on the Message, then the Matcher returns false.
@@ -196,7 +255,9 @@ public Builder.Constraint simpSubscribeDestMatchers(String... patterns) {
196255 * from.
197256 * @return the {@link Builder.Constraint} that is associated to the
198257 * {@link MessageMatcher}
258+ * @deprecated use {@link #destinationPathPatterns(String...)}
199259 */
260+ @ Deprecated
200261 private Builder .Constraint simpDestMatchers (SimpMessageType type , String ... patterns ) {
201262 List <MessageMatcher <?>> matchers = new ArrayList <>(patterns .length );
202263 for (String pattern : patterns ) {
@@ -206,13 +267,51 @@ private Builder.Constraint simpDestMatchers(SimpMessageType type, String... patt
206267 return new Builder .Constraint (matchers );
207268 }
208269
270+ /**
271+ * Allows the creation of a security {@link Constraint} applying to messages of
272+ * the provided {@code type} whose destinations match the provided
273+ * {@code patterns}.
274+ * <p>
275+ * The matching of each pattern is performed by a
276+ * {@link PathPatternMessageMatcher}. If no destination is found on the
277+ * {@code Message}, then each {@code Matcher} returns false.
278+ * </p>
279+ * @param type the {@link SimpMessageType} to match on. If null, the
280+ * {@link SimpMessageType} is not considered for matching.
281+ * @param patterns the patterns to create {@link PathPatternMessageMatcher} from.
282+ * @return the {@link Builder.Constraint} that is associated to the
283+ * {@link MessageMatcher}s
284+ * @since 6.5
285+ */
286+ private Builder .Constraint destinationPathPatterns (SimpMessageType type , String ... patterns ) {
287+ List <MessageMatcher <?>> matchers = new ArrayList <>(patterns .length );
288+ for (String pattern : patterns ) {
289+ MessageMatcher <Object > matcher = new LazySimpDestinationPatternMessageMatcher (pattern , type ,
290+ this .useHttpPathSeparator );
291+ matchers .add (matcher );
292+ }
293+ return new Builder .Constraint (matchers );
294+ }
295+
296+ /**
297+ * Instruct this builder to match message destinations using the separator
298+ * configured in
299+ * {@link org.springframework.http.server.PathContainer.Options#MESSAGE_ROUTE}
300+ */
301+ public Builder messageRouteSeparator () {
302+ this .useHttpPathSeparator = false ;
303+ return this ;
304+ }
305+
209306 /**
210307 * The {@link PathMatcher} to be used with the
211308 * {@link Builder#simpDestMatchers(String...)}. The default is to use the default
212309 * constructor of {@link AntPathMatcher}.
213310 * @param pathMatcher the {@link PathMatcher} to use. Cannot be null.
214311 * @return the {@link Builder} for further customization.
312+ * @deprecated use {@link #messageRouteSeparator()} to alter the path separator
215313 */
314+ @ Deprecated
216315 public Builder simpDestPathMatcher (PathMatcher pathMatcher ) {
217316 Assert .notNull (pathMatcher , "pathMatcher cannot be null" );
218317 this .pathMatcher = () -> pathMatcher ;
@@ -225,7 +324,9 @@ public Builder simpDestPathMatcher(PathMatcher pathMatcher) {
225324 * computation or lookup of the {@link PathMatcher}.
226325 * @param pathMatcher the {@link PathMatcher} to use. Cannot be null.
227326 * @return the {@link Builder} for further customization.
327+ * @deprecated use {@link #messageRouteSeparator()} to alter the path separator
228328 */
329+ @ Deprecated
229330 public Builder simpDestPathMatcher (Supplier <PathMatcher > pathMatcher ) {
230331 Assert .notNull (pathMatcher , "pathMatcher cannot be null" );
231332 this .pathMatcher = pathMatcher ;
@@ -241,9 +342,7 @@ public Builder simpDestPathMatcher(Supplier<PathMatcher> pathMatcher) {
241342 */
242343 public Builder .Constraint matchers (MessageMatcher <?>... matchers ) {
243344 List <MessageMatcher <?>> builders = new ArrayList <>(matchers .length );
244- for (MessageMatcher <?> matcher : matchers ) {
245- builders .add (matcher );
246- }
345+ builders .addAll (Arrays .asList (matchers ));
247346 return new Builder .Constraint (builders );
248347 }
249348
@@ -382,6 +481,7 @@ public Builder access(AuthorizationManager<MessageAuthorizationContext<?>> autho
382481
383482 }
384483
484+ @ Deprecated
385485 private final class LazySimpDestinationMessageMatcher implements MessageMatcher <Object > {
386486
387487 private final Supplier <SimpDestinationMessageMatcher > delegate ;
@@ -413,6 +513,40 @@ Map<String, String> extractPathVariables(Message<?> message) {
413513
414514 }
415515
516+ private static final class LazySimpDestinationPatternMessageMatcher implements MessageMatcher <Object > {
517+
518+ private final Supplier <PathPatternMessageMatcher > delegate ;
519+
520+ private LazySimpDestinationPatternMessageMatcher (String pattern , SimpMessageType type ,
521+ boolean useHttpPathSeparator ) {
522+ this .delegate = SingletonSupplier .of (() -> {
523+ PathPatternParser dotSeparatedPathParser = new PathPatternParser ();
524+ dotSeparatedPathParser .setPathOptions (PathContainer .Options .MESSAGE_ROUTE );
525+ PathPatternMessageMatcher .Builder builder = (useHttpPathSeparator )
526+ ? PathPatternMessageMatcher .withDefaults ()
527+ : PathPatternMessageMatcher .withPathPatternParser (dotSeparatedPathParser );
528+ if (type == null ) {
529+ return builder .matcher (pattern );
530+ }
531+ if (SimpMessageType .MESSAGE == type || SimpMessageType .SUBSCRIBE == type ) {
532+ return builder .matcher (pattern , type );
533+ }
534+ throw new IllegalStateException (type + " is not supported since it does not have a destination" );
535+ });
536+ }
537+
538+ @ Override
539+ public boolean matches (Message <?> message ) {
540+ return this .delegate .get ().matches (message );
541+ }
542+
543+ Map <String , String > extractPathVariables (Message <?> message ) {
544+ MatchResult matchResult = this .delegate .get ().matcher (message );
545+ return matchResult .getVariables ();
546+ }
547+
548+ }
549+
416550 }
417551
418552 private static final class Entry <T > {
@@ -421,7 +555,7 @@ private static final class Entry<T> {
421555
422556 private final T entry ;
423557
424- Entry (MessageMatcher requestMatcher , T entry ) {
558+ Entry (MessageMatcher <?> requestMatcher , T entry ) {
425559 this .messageMatcher = requestMatcher ;
426560 this .entry = entry ;
427561 }
0 commit comments