Skip to content

Commit 9bba1a1

Browse files
committed
Propagate Variables in And and OrRequestMatcher
Closes gh-12847
1 parent 8c17b97 commit 9bba1a1

File tree

4 files changed

+102
-4
lines changed

4 files changed

+102
-4
lines changed

web/src/main/java/org/springframework/security/web/util/matcher/AndRequestMatcher.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,7 +17,9 @@
1717
package org.springframework.security.web.util.matcher;
1818

1919
import java.util.Arrays;
20+
import java.util.LinkedHashMap;
2021
import java.util.List;
22+
import java.util.Map;
2123

2224
import jakarta.servlet.http.HttpServletRequest;
2325
import org.apache.commons.logging.Log;
@@ -66,6 +68,28 @@ public boolean matches(HttpServletRequest request) {
6668
return true;
6769
}
6870

71+
/**
72+
* Returns a {@link MatchResult} for this {@link HttpServletRequest}. In the case of a
73+
* match, request variables are a composition of the request variables in underlying
74+
* matchers. In the event that two matchers have the same key, the last key is the one
75+
* propagated.
76+
* @param request the HTTP request
77+
* @return a {@link MatchResult} based on the given HTTP request
78+
* @since 6.1
79+
*/
80+
@Override
81+
public MatchResult matcher(HttpServletRequest request) {
82+
Map<String, String> variables = new LinkedHashMap<>();
83+
for (RequestMatcher matcher : this.requestMatchers) {
84+
MatchResult result = matcher.matcher(request);
85+
if (!result.isMatch()) {
86+
return MatchResult.notMatch();
87+
}
88+
variables.putAll(result.getVariables());
89+
}
90+
return MatchResult.match(variables);
91+
}
92+
6993
@Override
7094
public String toString() {
7195
return "And " + this.requestMatchers;

web/src/main/java/org/springframework/security/web/util/matcher/OrRequestMatcher.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -62,6 +62,25 @@ public boolean matches(HttpServletRequest request) {
6262
return false;
6363
}
6464

65+
/**
66+
* Returns a {@link MatchResult} for this {@link HttpServletRequest}. In the case of a
67+
* match, request variables are any request variables from the first underlying
68+
* matcher.
69+
* @param request the HTTP request
70+
* @return a {@link MatchResult} based on the given HTTP request
71+
* @since 6.1
72+
*/
73+
@Override
74+
public MatchResult matcher(HttpServletRequest request) {
75+
for (RequestMatcher matcher : this.requestMatchers) {
76+
MatchResult result = matcher.matcher(request);
77+
if (result.isMatch()) {
78+
return result;
79+
}
80+
}
81+
return MatchResult.notMatch();
82+
}
83+
6584
@Override
6685
public String toString() {
6786
return "Or " + this.requestMatchers;

web/src/test/java/org/springframework/security/web/util/matcher/AndRequestMatcherTests.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,13 +19,16 @@
1919
import java.util.Arrays;
2020
import java.util.Collections;
2121
import java.util.List;
22+
import java.util.Map;
2223

2324
import jakarta.servlet.http.HttpServletRequest;
2425
import org.junit.jupiter.api.Test;
2526
import org.junit.jupiter.api.extension.ExtendWith;
2627
import org.mockito.Mock;
2728
import org.mockito.junit.jupiter.MockitoExtension;
2829

30+
import org.springframework.security.web.util.matcher.RequestMatcher.MatchResult;
31+
2932
import static org.assertj.core.api.Assertions.assertThat;
3033
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
3134
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
@@ -123,4 +126,30 @@ public void matchesMultiSingleFalse() {
123126
assertThat(this.matcher.matches(this.request)).isFalse();
124127
}
125128

129+
@Test
130+
public void matcherWhenMatchersHavePlaceholdersThenPropagatesMatches() {
131+
this.matcher = new AndRequestMatcher(this.delegate, this.delegate2);
132+
133+
given(this.delegate.matcher(this.request)).willReturn(MatchResult.match(Map.of("param", "value")));
134+
given(this.delegate2.matcher(this.request)).willReturn(MatchResult.match(Map.of("param", "othervalue")));
135+
MatchResult result = this.matcher.matcher(this.request);
136+
assertThat(result.getVariables()).containsExactlyEntriesOf(Map.of("param", "othervalue"));
137+
138+
given(this.delegate.matcher(this.request)).willReturn(MatchResult.match());
139+
given(this.delegate2.matcher(this.request)).willReturn(MatchResult.match(Map.of("param", "value")));
140+
result = this.matcher.matcher(this.request);
141+
assertThat(result.getVariables()).containsExactlyEntriesOf(Map.of("param", "value"));
142+
143+
given(this.delegate.matcher(this.request)).willReturn(MatchResult.match(Map.of("param", "value")));
144+
given(this.delegate2.matcher(this.request)).willReturn(MatchResult.notMatch());
145+
result = this.matcher.matcher(this.request);
146+
assertThat(result.getVariables()).isEmpty();
147+
148+
given(this.delegate.matcher(this.request)).willReturn(MatchResult.match(Map.of("otherparam", "value")));
149+
given(this.delegate2.matcher(this.request)).willReturn(MatchResult.match(Map.of("param", "value")));
150+
result = this.matcher.matcher(this.request);
151+
assertThat(result.getVariables())
152+
.containsExactlyInAnyOrderEntriesOf(Map.of("otherparam", "value", "param", "value"));
153+
}
154+
126155
}

web/src/test/java/org/springframework/security/web/util/matcher/OrRequestMatcherTests.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,17 +19,21 @@
1919
import java.util.Arrays;
2020
import java.util.Collections;
2121
import java.util.List;
22+
import java.util.Map;
2223

2324
import jakarta.servlet.http.HttpServletRequest;
2425
import org.junit.jupiter.api.Test;
2526
import org.junit.jupiter.api.extension.ExtendWith;
2627
import org.mockito.Mock;
2728
import org.mockito.junit.jupiter.MockitoExtension;
2829

30+
import org.springframework.security.web.util.matcher.RequestMatcher.MatchResult;
31+
2932
import static org.assertj.core.api.Assertions.assertThat;
3033
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
3134
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
3235
import static org.mockito.BDDMockito.given;
36+
import static org.mockito.Mockito.verifyNoInteractions;
3337

3438
/**
3539
* @author Rob Winch
@@ -122,4 +126,26 @@ public void matchesMultiSingleFalse() {
122126
assertThat(this.matcher.matches(this.request)).isTrue();
123127
}
124128

129+
@Test
130+
public void matcherWhenMatchersHavePlaceholdersThenPropagatesFirstMatch() {
131+
this.matcher = new OrRequestMatcher(this.delegate, this.delegate2);
132+
133+
given(this.delegate.matcher(this.request)).willReturn(MatchResult.match(Map.of("param", "value")));
134+
given(this.delegate2.matcher(this.request)).willReturn(MatchResult.match(Map.of("param", "othervalue")));
135+
MatchResult result = this.matcher.matcher(this.request);
136+
assertThat(result.getVariables()).containsExactlyEntriesOf(Map.of("param", "value"));
137+
verifyNoInteractions(this.delegate2);
138+
139+
given(this.delegate.matcher(this.request)).willReturn(MatchResult.match());
140+
given(this.delegate2.matcher(this.request)).willReturn(MatchResult.match(Map.of("param", "value")));
141+
result = this.matcher.matcher(this.request);
142+
assertThat(result.getVariables()).isEmpty();
143+
verifyNoInteractions(this.delegate2);
144+
145+
given(this.delegate.matcher(this.request)).willReturn(MatchResult.notMatch());
146+
given(this.delegate2.matcher(this.request)).willReturn(MatchResult.match(Map.of("param", "value")));
147+
result = this.matcher.matcher(this.request);
148+
assertThat(result.getVariables()).containsExactlyEntriesOf(Map.of("param", "value"));
149+
}
150+
125151
}

0 commit comments

Comments
 (0)