Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DestinationPathPatternMessageMatcher #16635

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

pat-mccusker
Copy link
Contributor

Closes gh-16500

I wasn't sure that this configuration was safe to remove so I wanted to point it out. In WebSocketMessageBrokerSecurityConfigurationTests an AntPathMatcher is configured with a '.' path separator, and this test will fail if the mentioned config is simply removed. If consumers of spring-security are configuring a PathMatcher in a SimpAnnotationMethodMessageHandler instance, then this matching would cease to function correctly as far as I can tell.

This led me to delegating the destination pattern matching to one or another PathPatternRouteMatchers depending on which path separator is configured by the MessageMatcherDelegatingAuthorizationManager.Builder, but I've left the original builder configuration in place for the moment.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Feb 23, 2025
@pat-mccusker pat-mccusker force-pushed the gh-16500-deprecate-path_matcher branch from de8c0ce to 31b962b Compare February 25, 2025 16:48
@pat-mccusker
Copy link
Contributor Author

pat-mccusker commented Feb 25, 2025

Hi @jzheaux, not sure if there are notifications on Draft PRs but I wanted to get your thoughts on how things looked in here so far.

@jzheaux jzheaux self-assigned this Feb 27, 2025
@jzheaux jzheaux added in: messaging An issue in spring-security-messaging type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged labels Feb 27, 2025
Copy link
Contributor

@jzheaux jzheaux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the draft, @pat-mccusker, this is a great start.

As far as your question about the prototype bean method, we should have an opt-in strategy that says that an application wants to start using DestinationPathPatternMessageMatcher instead of PathMatcher.

I think you can follow the pattern in PathPatternRequestMatcherBuilderFactoryBean. This is a factory bean that publishes a PathPatternRequestMatcher.Builder instances that applications and the DSL can use to build request matchers.

If you introduced DestinationPathPatternMessageMatcherBuilderFactoryBean, then you could do the same here, e.g. "if DestinationPathPatternMessageMatcher.Builder bean exists, then I use that to construct request matchers instead of working with path matcher"

* {@link org.springframework.http.server.PathContainer.Options#MESSAGE_ROUTE}
* separator
*/
public static Builder messageRoute() {
Copy link
Contributor

@jzheaux jzheaux Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of messageRoute(), let's expose the PathPatternParser that will be used. This will allow the application to configure PathOptions as well as anything else needed.

* {@code Constraint} will be applicable
* @since 6.5
*/
public Builder.Constraint destinationPathPatterns(String... patterns) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is another way to opt-in, I'd prefer to leave the methods that take a String the same. The reason is that it still describes what the application is doing: matching a simp destination.

* configured in
* {@link org.springframework.http.server.PathContainer.Options#MESSAGE_ROUTE}
*/
public Builder messageRouteSeparator() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably too specific for the DSL. The application in this case should constructor their own message matcher instance.

@@ -224,7 +324,9 @@ public Builder simpDestPathMatcher(PathMatcher pathMatcher) {
* computation or lookup of the {@link PathMatcher}.
* @param pathMatcher the {@link PathMatcher} to use. Cannot be null.
* @return the {@link Builder} for further customization.
* @deprecated use {@link #messageRouteSeparator()} to alter the path separator
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead, let's begin with directing them to use #matchers to construct their own matcher.

@@ -412,6 +513,37 @@ Map<String, String> extractPathVariables(Message<?> message) {

}

private static final class LazySimpDestinationPatternMessageMatcher implements MessageMatcher<Object> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing deferral work is there to address an eager instantiation issue caused by trying to resolve the Spring Websocket PathMatcher instance too early. When we aren't using PathMatcher, then this lazy-loading is likely unnecessary.

@@ -43,30 +43,30 @@ public final class MessageMatcherDelegatingAuthorizationManagerTests {
void checkWhenPermitAllThenPermits() {
AuthorizationManager<Message<?>> authorizationManager = builder().anyMessage().permitAll().build();
Message<?> message = new GenericMessage<>(new Object());
assertThat(authorizationManager.check(mock(Supplier.class), message).isGranted()).isTrue();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please leave these as-is. Even though check is deprecated, we want to make sure it keeps working until we are ready to remove it.

@Test
void checkWhenMessageTypeAndPathPatternMatches() {
AuthorizationManager<Message<?>> authorizationManager = builder()
.simpTypeMessageDestinationPatterns("/destination")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please follow the following convention:

.matcher(pathOne).rule()
.matcher(pathTwo).rule()
// ...

So, in this case, it would look something like:

.simpTypeMessageDestinationPatterns("/destination").permitAll()
.simpTypeSubscribeDestinationPatterns("/destination").denyAll()
// ...

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there are no other changes here, please go ahead and revert the copyright year so that this file is left out of the PR. Please do this for any other files like this one.

@@ -87,12 +89,11 @@ private MessageAuthorizationContext<?> authorizationContext(MessageMatcher<?> ma
if (!matcher.matches((Message) message)) {
return null;
}
if (matcher instanceof SimpDestinationMessageMatcher simp) {
return new MessageAuthorizationContext<>(message, simp.extractPathVariables(message));
if (matcher instanceof Builder.LazySimpDestinationMessageMatcher pathMatcher) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we make the change to MessageMatcher suggested in this PR, then this casting can all be removed.

* @author Pat McCusker
* @since 6.5
*/
public final class DestinationPathPatternMessageMatcher implements MessageMatcher<Object> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be called PathPatternMessageMatcher.

The reason is that it already contains more aspects of the message than the destination and may contain more in the future. So far, it doesn't seem materially different from PathPatternRequestMatcher and so we don't want to give the impression that it is by using a more specific name.

Note that otherwise, we'd want to call PathPatternRequestMatcher something more like UriPathPatternRequestMatcher. Which, at least for our purposes, doesn't add much information about what it is for.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: messaging An issue in spring-security-messaging type: enhancement A general enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Deprecate usages of PathMatcher in Web Socket support
3 participants