Skip to content

Commit f9d2d36

Browse files
Merge branch 'spring-projects:main' into gh-16206
2 parents fdfda10 + b9f3a28 commit f9d2d36

File tree

62 files changed

+382
-64
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+382
-64
lines changed

.github/dependabot.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ updates:
1919
- dependency-name: com.nimbusds:nimbus-jose-jwt
2020
- dependency-name: org.python:jython
2121
- dependency-name: org.apache.directory.server:*
22+
- dependency-name: org.apache.directory.shared:*
2223
- dependency-name: org.junit:junit-bom
2324
update-types:
2425
- version-update:semver-major
@@ -44,6 +45,7 @@ updates:
4445
- dependency-name: com.nimbusds:nimbus-jose-jwt
4546
- dependency-name: org.python:jython
4647
- dependency-name: org.apache.directory.server:*
48+
- dependency-name: org.apache.directory.shared:*
4749
- dependency-name: org.junit:junit-bom
4850
update-types:
4951
- version-update:semver-major
@@ -69,6 +71,7 @@ updates:
6971
- dependency-name: com.nimbusds:nimbus-jose-jwt
7072
- dependency-name: org.python:jython
7173
- dependency-name: org.apache.directory.server:*
74+
- dependency-name: org.apache.directory.shared:*
7275
- dependency-name: org.junit:junit-bom
7376
update-types:
7477
- version-update:semver-major

config/src/integration-test/java/org/springframework/security/config/annotation/configurers/WebAuthnWebDriverTests.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import org.junit.jupiter.api.AfterEach;
3232
import org.junit.jupiter.api.BeforeAll;
3333
import org.junit.jupiter.api.BeforeEach;
34-
import org.junit.jupiter.api.Disabled;
3534
import org.junit.jupiter.api.Test;
3635
import org.openqa.selenium.By;
3736
import org.openqa.selenium.WebElement;
@@ -67,7 +66,6 @@
6766
*
6867
* @author Daniel Garnier-Moiroux
6968
*/
70-
@Disabled
7169
class WebAuthnWebDriverTests {
7270

7371
private String baseUrl;
@@ -194,6 +192,11 @@ void loginWhenAuthenticatorRegisteredThenSuccess() {
194192
this.driver.findElement(passkeyLabel()).sendKeys("Virtual authenticator");
195193
this.driver.findElement(registerPasskeyButton()).click();
196194

195+
// Ensure the page location has changed before performing further assertions.
196+
// This is required because the location change is asynchronously performed in
197+
// javascript, and performing assertions based on this.driver.findElement(...)
198+
// may result in a StaleElementReferenceException.
199+
await(() -> assertThat(this.driver.getCurrentUrl()).endsWith("/webauthn/register?success"));
197200
await(() -> assertHasAlertStartingWith("success", "Success!"));
198201

199202
List<WebElement> passkeyRows = this.driver.findElements(passkeyTableRows());

config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/InitializeUserDetailsBeanManagerConfigurer.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,10 @@ else if (beanNames.length > 1) {
9595
PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
9696
UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
9797
CompromisedPasswordChecker passwordChecker = getBeanOrNull(CompromisedPasswordChecker.class);
98-
DaoAuthenticationProvider provider;
98+
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(userDetailsService);
9999
if (passwordEncoder != null) {
100-
provider = new DaoAuthenticationProvider(passwordEncoder);
100+
provider.setPasswordEncoder(passwordEncoder);
101101
}
102-
else {
103-
provider = new DaoAuthenticationProvider();
104-
}
105-
provider.setUserDetailsService(userDetailsService);
106102
if (passwordManager != null) {
107103
provider.setUserDetailsPasswordService(passwordManager);
108104
}

config/src/main/java/org/springframework/security/config/annotation/authentication/configurers/userdetails/AbstractDaoAuthenticationConfigurer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
public abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, C extends AbstractDaoAuthenticationConfigurer<B, C, U>, U extends UserDetailsService>
3737
extends UserDetailsAwareConfigurer<B, U> {
3838

39-
private DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
39+
private DaoAuthenticationProvider provider;
4040

4141
private final U userDetailsService;
4242

@@ -46,7 +46,7 @@ public abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderMana
4646
*/
4747
AbstractDaoAuthenticationConfigurer(U userDetailsService) {
4848
this.userDetailsService = userDetailsService;
49-
this.provider.setUserDetailsService(userDetailsService);
49+
this.provider = new DaoAuthenticationProvider(userDetailsService);
5050
if (userDetailsService instanceof UserDetailsPasswordService) {
5151
this.provider.setUserDetailsPasswordService((UserDetailsPasswordService) userDetailsService);
5252
}

config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurity.java

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@
6565
import org.springframework.security.web.firewall.ObservationMarkingRequestRejectedHandler;
6666
import org.springframework.security.web.firewall.RequestRejectedHandler;
6767
import org.springframework.security.web.firewall.StrictHttpFirewall;
68-
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
6968
import org.springframework.security.web.util.matcher.RequestMatcher;
7069
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
7170
import org.springframework.util.Assert;
@@ -310,20 +309,8 @@ protected Filter performBuild() throws Exception {
310309
requestMatcherPrivilegeEvaluatorsEntries
311310
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
312311
}
313-
DefaultSecurityFilterChain anyRequestFilterChain = null;
314312
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
315313
SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
316-
if (anyRequestFilterChain != null) {
317-
String message = "A filter chain that matches any request [" + anyRequestFilterChain
318-
+ "] has already been configured, which means that this filter chain [" + securityFilterChain
319-
+ "] will never get invoked. Please use `HttpSecurity#securityMatcher` to ensure that there is only one filter chain configured for 'any request' and that the 'any request' filter chain is published last.";
320-
throw new IllegalArgumentException(message);
321-
}
322-
if (securityFilterChain instanceof DefaultSecurityFilterChain defaultSecurityFilterChain) {
323-
if (defaultSecurityFilterChain.getRequestMatcher() instanceof AnyRequestMatcher) {
324-
anyRequestFilterChain = defaultSecurityFilterChain;
325-
}
326-
}
327314
securityFilterChains.add(securityFilterChain);
328315
requestMatcherPrivilegeEvaluatorsEntries
329316
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
@@ -345,6 +332,7 @@ else if (!this.observationRegistry.isNoop()) {
345332
new HttpStatusRequestRejectedHandler());
346333
filterChainProxy.setRequestRejectedHandler(requestRejectedHandler);
347334
}
335+
filterChainProxy.setFilterChainValidator(new WebSecurityFilterChainValidator());
348336
filterChainProxy.setFilterChainDecorator(getFilterChainDecorator());
349337
filterChainProxy.afterPropertiesSet();
350338

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.annotation.web.builders;
18+
19+
import java.util.List;
20+
21+
import org.springframework.security.web.DefaultSecurityFilterChain;
22+
import org.springframework.security.web.FilterChainProxy;
23+
import org.springframework.security.web.SecurityFilterChain;
24+
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
25+
26+
/**
27+
* A filter chain validator for filter chains built by {@link WebSecurity}
28+
*
29+
* @since 6.5
30+
*/
31+
final class WebSecurityFilterChainValidator implements FilterChainProxy.FilterChainValidator {
32+
33+
@Override
34+
public void validate(FilterChainProxy filterChainProxy) {
35+
List<SecurityFilterChain> chains = filterChainProxy.getFilterChains();
36+
DefaultSecurityFilterChain anyRequestFilterChain = null;
37+
for (SecurityFilterChain chain : chains) {
38+
if (anyRequestFilterChain != null) {
39+
String message = "A filter chain that matches any request [" + anyRequestFilterChain
40+
+ "] has already been configured, which means that this filter chain [" + chain
41+
+ "] will never get invoked. Please use `HttpSecurity#securityMatcher` to ensure that there is only one filter chain configured for 'any request' and that the 'any request' filter chain is published last.";
42+
throw new IllegalArgumentException(message);
43+
}
44+
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
45+
if (defaultChain.getRequestMatcher() instanceof AnyRequestMatcher) {
46+
anyRequestFilterChain = defaultChain;
47+
}
48+
}
49+
}
50+
}
51+
52+
}

config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutAuthentication.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 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.
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.security.config.annotation.web.configurers.oauth2.client;
1818

19+
import java.io.Serial;
1920
import java.util.Collections;
2021

2122
import org.springframework.security.authentication.AbstractAuthenticationToken;
@@ -36,6 +37,9 @@
3637
*/
3738
class OidcBackChannelLogoutAuthentication extends AbstractAuthenticationToken {
3839

40+
@Serial
41+
private static final long serialVersionUID = 9095810699956350287L;
42+
3943
private final OidcLogoutToken logoutToken;
4044

4145
private final ClientRegistration clientRegistration;

config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcLogoutAuthenticationToken.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 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.
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.security.config.annotation.web.configurers.oauth2.client;
1818

19+
import java.io.Serial;
20+
1921
import org.springframework.security.authentication.AbstractAuthenticationToken;
2022
import org.springframework.security.core.authority.AuthorityUtils;
2123
import org.springframework.security.oauth2.client.registration.ClientRegistration;
@@ -29,6 +31,9 @@
2931
*/
3032
class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken {
3133

34+
@Serial
35+
private static final long serialVersionUID = -1568528983223505540L;
36+
3237
private final String logoutToken;
3338

3439
private final ClientRegistration clientRegistration;

config/src/main/java/org/springframework/security/config/authentication/AuthenticationManagerFactoryBean.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@ public AuthenticationManager getObject() throws Exception {
6565
if (uds == null) {
6666
throw new NoSuchBeanDefinitionException(BeanIds.AUTHENTICATION_MANAGER, MISSING_BEAN_ERROR_MESSAGE);
6767
}
68-
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
69-
provider.setUserDetailsService(uds);
68+
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(uds);
7069
PasswordEncoder passwordEncoder = this.bf.getBeanProvider(PasswordEncoder.class).getIfUnique();
7170
if (passwordEncoder != null) {
7271
provider.setPasswordEncoder(passwordEncoder);

config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelLogoutAuthentication.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 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.
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.security.config.web.server;
1818

19+
import java.io.Serial;
1920
import java.util.Collections;
2021

2122
import org.springframework.security.authentication.AbstractAuthenticationToken;
@@ -36,6 +37,9 @@
3637
*/
3738
class OidcBackChannelLogoutAuthentication extends AbstractAuthenticationToken {
3839

40+
@Serial
41+
private static final long serialVersionUID = 9095810699956350287L;
42+
3943
private final OidcLogoutToken logoutToken;
4044

4145
private final ClientRegistration clientRegistration;

config/src/main/java/org/springframework/security/config/web/server/OidcLogoutAuthenticationToken.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 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.
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.security.config.web.server;
1818

19+
import java.io.Serial;
20+
1921
import org.springframework.security.authentication.AbstractAuthenticationToken;
2022
import org.springframework.security.core.authority.AuthorityUtils;
2123
import org.springframework.security.oauth2.client.registration.ClientRegistration;
@@ -29,6 +31,9 @@
2931
*/
3032
class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken {
3133

34+
@Serial
35+
private static final long serialVersionUID = -1568528983223505540L;
36+
3237
private final String logoutToken;
3338

3439
private final ClientRegistration clientRegistration;

config/src/main/kotlin/org/springframework/security/config/web/server/ServerHeadersDsl.kt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2024 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,6 +17,7 @@
1717
package org.springframework.security.config.web.server
1818

1919
import org.springframework.security.web.server.header.CacheControlServerHttpHeadersWriter
20+
import org.springframework.security.web.server.header.ServerHttpHeadersWriter
2021
import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter
2122
import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter
2223
import org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter
@@ -43,6 +44,7 @@ class ServerHeadersDsl {
4344
private var crossOriginOpenerPolicy: ((ServerHttpSecurity.HeaderSpec.CrossOriginOpenerPolicySpec) -> Unit)? = null
4445
private var crossOriginEmbedderPolicy: ((ServerHttpSecurity.HeaderSpec.CrossOriginEmbedderPolicySpec) -> Unit)? = null
4546
private var crossOriginResourcePolicy: ((ServerHttpSecurity.HeaderSpec.CrossOriginResourcePolicySpec) -> Unit)? = null
47+
private var writers = mutableListOf<ServerHttpHeadersWriter>()
4648

4749
private var disabled = false
4850

@@ -198,6 +200,16 @@ class ServerHeadersDsl {
198200
this.crossOriginResourcePolicy = ServerCrossOriginResourcePolicyDsl().apply(crossOriginResourcePolicyConfig).get()
199201
}
200202

203+
/**
204+
* Configures custom headers writer
205+
*
206+
* @since 6.5
207+
* @param writer the [ServerHttpHeadersWriter] to provide custom headers writer
208+
*/
209+
fun writer(writer: ServerHttpHeadersWriter) {
210+
this.writers.add(writer)
211+
}
212+
201213
/**
202214
* Disables HTTP response headers.
203215
*/
@@ -244,6 +256,9 @@ class ServerHeadersDsl {
244256
crossOriginResourcePolicy?.also {
245257
headers.crossOriginResourcePolicy(crossOriginResourcePolicy)
246258
}
259+
writers.also {
260+
writers.forEach { writer -> headers.writer(writer) }
261+
}
247262
if (disabled) {
248263
headers.disable()
249264
}

0 commit comments

Comments
 (0)