Revert unnecessary commits from main

Issue gh-15016
This commit is contained in:
Marcus Hert Da Coregio
2024-05-08 13:44:07 -03:00
parent e4745c99e9
commit 08f11f06ab
317 changed files with 1281 additions and 30496 deletions

View File

@@ -1,135 +0,0 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.authentication;
import jakarta.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.test.web.CodecTestUtils;
import org.springframework.security.web.authentication.www.BasicAuthenticationConverter;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Tests for {@link DelegatingAuthenticationConverter}.
*
* @author Max Batischev
*/
@ExtendWith(MockitoExtension.class)
public class DelegatingAuthenticationConverterTests {
private static final String X_AUTH_TOKEN_HEADER = "X-Auth-Token";
private static final String TEST_X_AUTH_TOKEN = "test-x-auth-token";
private static final String TEST_CUSTOM_PRINCIPAL = "test_custom_principal";
private static final String TEST_CUSTOM_CREDENTIALS = "test_custom_credentials";
private static final String TEST_BASIC_CREDENTIALS = "username:password";
private static final String INVALID_BASIC_CREDENTIALS = "invalid_credentials";
private DelegatingAuthenticationConverter converter;
@Mock
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;
@Test
public void requestWhenBasicAuthorizationHeaderIsPresentThenAuthenticates() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader(HttpHeaders.AUTHORIZATION, "Basic " + CodecTestUtils.encodeBase64(TEST_BASIC_CREDENTIALS));
this.converter = new DelegatingAuthenticationConverter(
new BasicAuthenticationConverter(this.authenticationDetailsSource),
new TestNullableAuthenticationConverter());
Authentication authentication = this.converter.convert(request);
assertThat(authentication).isNotNull();
assertThat(authentication.getName()).isEqualTo("username");
}
@Test
public void requestWhenXAuthHeaderIsPresentThenAuthenticates() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader(X_AUTH_TOKEN_HEADER, TEST_X_AUTH_TOKEN);
this.converter = new DelegatingAuthenticationConverter(new TestAuthenticationConverter(),
new TestNullableAuthenticationConverter());
Authentication authentication = this.converter.convert(request);
assertThat(authentication).isNotNull();
assertThat(authentication.getName()).isEqualTo(TEST_CUSTOM_PRINCIPAL);
}
@Test
public void requestWhenXAuthHeaderIsPresentThenDoesntAuthenticate() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader(X_AUTH_TOKEN_HEADER, TEST_X_AUTH_TOKEN);
this.converter = new DelegatingAuthenticationConverter(new TestNullableAuthenticationConverter());
Authentication authentication = this.converter.convert(request);
assertThat(authentication).isNull();
}
@Test
public void requestWhenInvalidBasicAuthorizationTokenThenError() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader(HttpHeaders.AUTHORIZATION, "Basic " + CodecTestUtils.encodeBase64(INVALID_BASIC_CREDENTIALS));
this.converter = new DelegatingAuthenticationConverter(
new BasicAuthenticationConverter(this.authenticationDetailsSource),
new TestNullableAuthenticationConverter());
assertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.converter.convert(request));
}
private static class TestAuthenticationConverter implements AuthenticationConverter {
@Override
public Authentication convert(HttpServletRequest request) {
String header = request.getHeader(X_AUTH_TOKEN_HEADER);
if (header != null) {
return new TestingAuthenticationToken(TEST_CUSTOM_PRINCIPAL, TEST_CUSTOM_CREDENTIALS);
}
else {
return null;
}
}
}
private static class TestNullableAuthenticationConverter implements AuthenticationConverter {
@Override
public Authentication convert(HttpServletRequest request) {
return null;
}
}
}

View File

@@ -1,99 +0,0 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.authentication.password;
import java.io.IOException;
import okhttp3.HttpUrl;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.security.authentication.password.CompromisedPasswordCheckResult;
import org.springframework.web.client.RestClient;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatNoException;
class HaveIBeenPwnedRestApiPasswordCheckerTests {
private final String pwnedPasswords = """
2CDE4CDCFA5AD7D223BD1800338FBEAA04E:1
2CF90F92EE1941547BB13DFC7D0E0AFE504:1
2D10A6654B6D75908AE572559542245CBFA:6
2D4FCF535FE92B8B950424E16E65EFBFED3:1
2D6980B9098804E7A83DC5831BFBAF3927F:1
2D8D1B3FAACCA6A3C6A91617B2FA32E2F57:1
2DC183F740EE76F27B78EB39C8AD972A757:300185
2DE4C0087846D223DBBCCF071614590F300:3
2DEA2B1D02714099E4B7A874B4364D518F6:1
2E750AE8C4756A20CE040BF3DDF094FA7EC:1
2E90B7B3C5C1181D16C48E273D9AC7F3C16:5
2E991A9162F24F01826D8AF73CA20F2B430:1
2EAE5EA981BFAF29A8869A40BDDADF3879B:2
2F1AC09E3846595E436BBDDDD2189358AF9:1
""";
private final MockWebServer server = new MockWebServer();
private final HaveIBeenPwnedRestApiPasswordChecker passwordChecker = new HaveIBeenPwnedRestApiPasswordChecker();
@BeforeEach
void setup() throws IOException {
this.server.start();
HttpUrl url = this.server.url("/range/");
this.passwordChecker.setRestClient(RestClient.builder().baseUrl(url.toString()).build());
}
@AfterEach
void tearDown() throws IOException {
this.server.shutdown();
}
@Test
void checkWhenPasswordIsLeakedThenIsCompromised() throws InterruptedException {
this.server.enqueue(new MockResponse().setBody(this.pwnedPasswords).setResponseCode(200));
CompromisedPasswordCheckResult check = this.passwordChecker.check("P@ssw0rd");
assertThat(check.isCompromised()).isTrue();
assertThat(this.server.takeRequest().getPath()).isEqualTo("/range/21BD1");
}
@Test
void checkWhenPasswordNotLeakedThenNotCompromised() {
this.server.enqueue(new MockResponse().setBody(this.pwnedPasswords).setResponseCode(200));
CompromisedPasswordCheckResult check = this.passwordChecker.check("My1nCr3d!bL3P@SS0W0RD");
assertThat(check.isCompromised()).isFalse();
}
@Test
void checkWhenNoPasswordsReturnedFromApiCallThenNotCompromised() {
this.server.enqueue(new MockResponse().setResponseCode(200));
CompromisedPasswordCheckResult check = this.passwordChecker.check("123456");
assertThat(check.isCompromised()).isFalse();
}
@Test
void checkWhenResponseStatusNot200ThenNotCompromised() {
this.server.enqueue(new MockResponse().setResponseCode(503));
assertThatNoException().isThrownBy(() -> this.passwordChecker.check("123456"));
this.server.enqueue(new MockResponse().setResponseCode(404));
assertThatNoException().isThrownBy(() -> this.passwordChecker.check("123456"));
}
}

View File

@@ -1,105 +0,0 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.authentication.password;
import java.io.IOException;
import okhttp3.HttpUrl;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import reactor.test.StepVerifier;
import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat;
class HaveIBeenPwnedRestApiReactivePasswordCheckerTests {
private final String pwnedPasswords = """
2CDE4CDCFA5AD7D223BD1800338FBEAA04E:1
2CF90F92EE1941547BB13DFC7D0E0AFE504:1
2D10A6654B6D75908AE572559542245CBFA:6
2D4FCF535FE92B8B950424E16E65EFBFED3:1
2D6980B9098804E7A83DC5831BFBAF3927F:1
2D8D1B3FAACCA6A3C6A91617B2FA32E2F57:1
2DC183F740EE76F27B78EB39C8AD972A757:300185
2DE4C0087846D223DBBCCF071614590F300:3
2DEA2B1D02714099E4B7A874B4364D518F6:1
2E750AE8C4756A20CE040BF3DDF094FA7EC:1
2E90B7B3C5C1181D16C48E273D9AC7F3C16:5
2E991A9162F24F01826D8AF73CA20F2B430:1
2EAE5EA981BFAF29A8869A40BDDADF3879B:2
2F1AC09E3846595E436BBDDDD2189358AF9:1
""";
private final MockWebServer server = new MockWebServer();
private final HaveIBeenPwnedRestApiReactivePasswordChecker passwordChecker = new HaveIBeenPwnedRestApiReactivePasswordChecker();
@BeforeEach
void setup() throws IOException {
this.server.start();
HttpUrl url = this.server.url("/range/");
this.passwordChecker.setWebClient(WebClient.builder().baseUrl(url.toString()).build());
}
@AfterEach
void tearDown() throws IOException {
this.server.shutdown();
}
@Test
void checkWhenPasswordIsLeakedThenIsCompromised() throws InterruptedException {
this.server.enqueue(new MockResponse().setBody(this.pwnedPasswords).setResponseCode(200));
StepVerifier.create(this.passwordChecker.check("P@ssw0rd"))
.assertNext((check) -> assertThat(check.isCompromised()).isTrue())
.verifyComplete();
assertThat(this.server.takeRequest().getPath()).isEqualTo("/range/21BD1");
}
@Test
void checkWhenPasswordNotLeakedThenNotCompromised() {
this.server.enqueue(new MockResponse().setBody(this.pwnedPasswords).setResponseCode(200));
StepVerifier.create(this.passwordChecker.check("My1nCr3d!bL3P@SS0W0RD"))
.assertNext((check) -> assertThat(check.isCompromised()).isFalse())
.verifyComplete();
}
@Test
void checkWhenNoPasswordsReturnedFromApiCallThenNotCompromised() {
this.server.enqueue(new MockResponse().setResponseCode(200));
StepVerifier.create(this.passwordChecker.check("P@ssw0rd"))
.assertNext((check) -> assertThat(check.isCompromised()).isFalse())
.verifyComplete();
}
@Test
void checkWhenResponseStatusNot200ThenNotCompromised() {
this.server.enqueue(new MockResponse().setResponseCode(503));
StepVerifier.create(this.passwordChecker.check("123456"))
.assertNext((check) -> assertThat(check.isCompromised()).isFalse())
.verifyComplete();
this.server.enqueue(new MockResponse().setResponseCode(404));
StepVerifier.create(this.passwordChecker.check("123456"))
.assertNext((check) -> assertThat(check.isCompromised()).isFalse())
.verifyComplete();
}
}

View File

@@ -1,77 +0,0 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.jackson2;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.skyscreamer.jsonassert.JSONAssert;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.jackson2.AbstractMixinTests;
import org.springframework.security.jackson2.SimpleGrantedAuthorityMixinTests;
import org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Markus Heiden
* @since 6.3
*/
public class SwitchUserGrantedAuthorityMixInTests extends AbstractMixinTests {
// language=JSON
private static final String SWITCH_JSON = """
{
"@class": "org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority",
"role": "switched",
"source": {
"@class": "org.springframework.security.authentication.UsernamePasswordAuthenticationToken",
"principal": "principal",
"credentials": "credentials",
"authenticated": true,
"details": null,
"authorities": %s
}
}
""".formatted(SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON);
private Authentication source;
@BeforeEach
public void setUp() {
this.source = new UsernamePasswordAuthenticationToken("principal", "credentials",
AuthorityUtils.createAuthorityList("ROLE_USER"));
}
@Test
public void serializeWhenPrincipalCredentialsAuthoritiesThenSuccess() throws Exception {
SwitchUserGrantedAuthority expected = new SwitchUserGrantedAuthority("switched", this.source);
String serializedJson = this.mapper.writeValueAsString(expected);
JSONAssert.assertEquals(SWITCH_JSON, serializedJson, true);
}
@Test
public void deserializeWhenSourceIsUsernamePasswordAuthenticationTokenThenSuccess() throws Exception {
SwitchUserGrantedAuthority deserialized = this.mapper.readValue(SWITCH_JSON, SwitchUserGrantedAuthority.class);
assertThat(deserialized).isNotNull();
assertThat(deserialized.getAuthority()).isEqualTo("switched");
assertThat(deserialized.getSource()).isEqualTo(this.source);
}
}

View File

@@ -69,26 +69,9 @@ public class CurrentSecurityContextArgumentResolverTests {
SecurityContextHolder.clearContext();
}
@Test
public void supportsParameterNoAnnotationWrongType() {
assertThat(this.resolver.supportsParameter(showSecurityContextNoAnnotationTypeMismatch())).isFalse();
}
@Test
public void supportsParameterNoAnnotation() {
assertThat(this.resolver.supportsParameter(showSecurityContextNoAnnotation())).isTrue();
}
@Test
public void supportsParameterCustomSecurityContextNoAnnotation() {
assertThat(this.resolver.supportsParameter(showSecurityContextWithCustomSecurityContextNoAnnotation()))
.isTrue();
}
@Test
public void supportsParameterNoAnnotationCustomType() {
assertThat(this.resolver.supportsParameter(showSecurityContextWithCustomSecurityContextNoAnnotation()))
.isTrue();
assertThat(this.resolver.supportsParameter(showSecurityContextNoAnnotation())).isFalse();
}
@Test
@@ -105,24 +88,6 @@ public class CurrentSecurityContextArgumentResolverTests {
assertThat(customSecurityContext.getAuthentication().getPrincipal()).isEqualTo(principal);
}
@Test
public void resolveArgumentWithCustomSecurityContextNoAnnotation() {
String principal = "custom_security_context";
setAuthenticationPrincipalWithCustomSecurityContext(principal);
CustomSecurityContext customSecurityContext = (CustomSecurityContext) this.resolver
.resolveArgument(showSecurityContextWithCustomSecurityContextNoAnnotation(), null, null, null);
assertThat(customSecurityContext.getAuthentication().getPrincipal()).isEqualTo(principal);
}
@Test
public void resolveArgumentWithNoAnnotation() {
String principal = "custom_security_context";
setAuthenticationPrincipal(principal);
SecurityContext securityContext = (SecurityContext) this.resolver
.resolveArgument(showSecurityContextNoAnnotation(), null, null, null);
assertThat(securityContext.getAuthentication().getPrincipal()).isEqualTo(principal);
}
@Test
public void resolveArgumentWithCustomSecurityContextTypeMatch() {
String principal = "custom_security_context_type_match";
@@ -247,12 +212,8 @@ public class CurrentSecurityContextArgumentResolverTests {
.resolveArgument(showCurrentSecurityWithErrorOnInvalidTypeMisMatch(), null, null, null));
}
private MethodParameter showSecurityContextNoAnnotationTypeMismatch() {
return getMethodParameter("showSecurityContextNoAnnotation", String.class);
}
private MethodParameter showSecurityContextNoAnnotation() {
return getMethodParameter("showSecurityContextNoAnnotation", SecurityContext.class);
return getMethodParameter("showSecurityContextNoAnnotation", String.class);
}
private MethodParameter showSecurityContextAnnotation() {
@@ -315,11 +276,6 @@ public class CurrentSecurityContextArgumentResolverTests {
return getMethodParameter("showCurrentSecurityWithErrorOnInvalidTypeMisMatch", String.class);
}
public MethodParameter showSecurityContextWithCustomSecurityContextNoAnnotation() {
return getMethodParameter("showSecurityContextWithCustomSecurityContextNoAnnotation",
CustomSecurityContext.class);
}
private MethodParameter getMethodParameter(String methodName, Class<?>... paramTypes) {
Method method = ReflectionUtils.findMethod(TestController.class, methodName, paramTypes);
return new MethodParameter(method, 0);
@@ -402,12 +358,6 @@ public class CurrentSecurityContextArgumentResolverTests {
@CurrentSecurityWithErrorOnInvalidType String typeMisMatch) {
}
public void showSecurityContextNoAnnotation(SecurityContext context) {
}
public void showSecurityContextWithCustomSecurityContextNoAnnotation(CustomSecurityContext context) {
}
}
static class CustomSecurityContext implements SecurityContext {

View File

@@ -69,14 +69,6 @@ public class CurrentSecurityContextArgumentResolverTests {
ResolvableMethod securityContextMethod = ResolvableMethod.on(getClass()).named("securityContext").build();
ResolvableMethod securityContextNoAnnotationMethod = ResolvableMethod.on(getClass())
.named("securityContextNoAnnotation")
.build();
ResolvableMethod customSecurityContextNoAnnotationMethod = ResolvableMethod.on(getClass())
.named("customSecurityContextNoAnnotation")
.build();
ResolvableMethod securityContextWithAuthentication = ResolvableMethod.on(getClass())
.named("securityContextWithAuthentication")
.build();
@@ -95,19 +87,6 @@ public class CurrentSecurityContextArgumentResolverTests {
.isTrue();
}
@Test
public void supportsParameterCurrentSecurityContextNoAnnotation() {
assertThat(this.resolver
.supportsParameter(this.securityContextNoAnnotationMethod.arg(Mono.class, SecurityContext.class))).isTrue();
}
@Test
public void supportsParameterCurrentCustomSecurityContextNoAnnotation() {
assertThat(this.resolver.supportsParameter(
this.customSecurityContextNoAnnotationMethod.arg(Mono.class, CustomSecurityContext.class)))
.isTrue();
}
@Test
public void supportsParameterWithAuthentication() {
assertThat(this.resolver
@@ -144,40 +123,6 @@ public class CurrentSecurityContextArgumentResolverTests {
ReactiveSecurityContextHolder.clearContext();
}
@Test
public void resolveArgumentWithSecurityContextNoAnnotation() {
MethodParameter parameter = ResolvableMethod.on(getClass())
.named("securityContextNoAnnotation")
.build()
.arg(Mono.class, SecurityContext.class);
Authentication auth = buildAuthenticationWithPrincipal("hello");
Context context = ReactiveSecurityContextHolder.withAuthentication(auth);
Mono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);
SecurityContext securityContext = (SecurityContext) argument.contextWrite(context)
.cast(Mono.class)
.block()
.block();
assertThat(securityContext.getAuthentication()).isSameAs(auth);
ReactiveSecurityContextHolder.clearContext();
}
@Test
public void resolveArgumentWithCustomSecurityContextNoAnnotation() {
MethodParameter parameter = ResolvableMethod.on(getClass())
.named("customSecurityContextNoAnnotation")
.build()
.arg(Mono.class, CustomSecurityContext.class);
Authentication auth = buildAuthenticationWithPrincipal("hello");
Context context = ReactiveSecurityContextHolder.withSecurityContext(Mono.just(new CustomSecurityContext(auth)));
Mono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);
CustomSecurityContext securityContext = (CustomSecurityContext) argument.contextWrite(context)
.cast(Mono.class)
.block()
.block();
assertThat(securityContext.getAuthentication()).isSameAs(auth);
ReactiveSecurityContextHolder.clearContext();
}
@Test
public void resolveArgumentWithCustomSecurityContext() {
MethodParameter parameter = ResolvableMethod.on(getClass())
@@ -405,12 +350,6 @@ public class CurrentSecurityContextArgumentResolverTests {
void securityContext(@CurrentSecurityContext Mono<SecurityContext> monoSecurityContext) {
}
void securityContextNoAnnotation(Mono<SecurityContext> securityContextMono) {
}
void customSecurityContextNoAnnotation(Mono<CustomSecurityContext> securityContextMono) {
}
void customSecurityContext(@CurrentSecurityContext Mono<SecurityContext> monoSecurityContext) {
}

View File

@@ -1,137 +0,0 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.server.authentication;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.web.server.ServerWebExchange;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author DingHao
* @since 6.3
*/
@ExtendWith(MockitoExtension.class)
public class DelegatingServerAuthenticationConverterTests {
DelegatingServerAuthenticationConverter converter = new DelegatingServerAuthenticationConverter(
new ApkServerAuthenticationConverter(), new ServerHttpBasicAuthenticationConverter());
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/");
@Test
public void applyServerHttpBasicAuthenticationConverter() {
Mono<Authentication> result = this.converter.convert(MockServerWebExchange
.from(this.request.header(HttpHeaders.AUTHORIZATION, "Basic dXNlcjpwYXNzd29yZA==").build()));
UsernamePasswordAuthenticationToken authentication = result.cast(UsernamePasswordAuthenticationToken.class)
.block();
assertThat(authentication.getPrincipal()).isEqualTo("user");
assertThat(authentication.getCredentials()).isEqualTo("password");
}
@Test
public void applyApkServerAuthenticationConverter() {
String apk = "123e4567e89b12d3a456426655440000";
Mono<Authentication> result = this.converter
.convert(MockServerWebExchange.from(this.request.header("APK", apk).build()));
ApkTokenAuthenticationToken authentication = result.cast(ApkTokenAuthenticationToken.class).block();
assertThat(authentication.getApk()).isEqualTo(apk);
}
@Test
public void applyServerHttpBasicAuthenticationConverterWhenFirstAuthenticationConverterException() {
this.converter.setContinueOnError(true);
Mono<Authentication> result = this.converter.convert(MockServerWebExchange.from(this.request.header("APK", "")
.header(HttpHeaders.AUTHORIZATION, "Basic dXNlcjpwYXNzd29yZA==")
.build()));
UsernamePasswordAuthenticationToken authentication = result.cast(UsernamePasswordAuthenticationToken.class)
.block();
assertThat(authentication.getPrincipal()).isEqualTo("user");
assertThat(authentication.getCredentials()).isEqualTo("password");
}
@Test
public void applyApkServerAuthenticationConverterThenDelegate2NotInvokedAndError() {
Mono<Authentication> result = this.converter.convert(MockServerWebExchange.from(this.request.header("APK", "")
.header(HttpHeaders.AUTHORIZATION, "Basic dXNlcjpwYXNzd29yZA==")
.build()));
StepVerifier.create(result.cast(ApkTokenAuthenticationToken.class))
.expectError(ApkAuthenticationException.class)
.verify();
}
public static class ApkServerAuthenticationConverter implements ServerAuthenticationConverter {
@Override
public Mono<Authentication> convert(ServerWebExchange exchange) {
return Mono.fromCallable(() -> exchange.getRequest().getHeaders().getFirst("APK")).map((apk) -> {
if (apk.isEmpty()) {
throw new ApkAuthenticationException("apk invalid");
}
return new ApkTokenAuthenticationToken(apk);
});
}
}
public static class ApkTokenAuthenticationToken extends AbstractAuthenticationToken {
private final String apk;
public ApkTokenAuthenticationToken(String apk) {
super(AuthorityUtils.NO_AUTHORITIES);
this.apk = apk;
}
public String getApk() {
return this.apk;
}
@Override
public Object getCredentials() {
return this.getApk();
}
@Override
public Object getPrincipal() {
return this.getApk();
}
}
public static class ApkAuthenticationException extends AuthenticationException {
public ApkAuthenticationException(String msg) {
super(msg);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@
package org.springframework.security.web.server.authentication;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -76,15 +74,9 @@ public class DelegatingServerAuthenticationSuccessHandlerTests {
}
@Test
public void constructorWhenNullListThenIllegalArgumentException() {
assertThatIllegalArgumentException().isThrownBy(() -> new DelegatingServerAuthenticationSuccessHandler(
(List<ServerAuthenticationSuccessHandler>) null));
}
@Test
public void constructorWhenEmptyListThenIllegalArgumentException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new DelegatingServerAuthenticationSuccessHandler(Collections.emptyList()));
public void constructorWhenEmptyThenIllegalArgumentException() {
assertThatIllegalArgumentException().isThrownBy(
() -> new DelegatingServerAuthenticationSuccessHandler(new ServerAuthenticationSuccessHandler[0]));
}
@Test

View File

@@ -1,171 +0,0 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.server.authentication.session;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.http.server.reactive.MockServerHttpResponse;
import org.springframework.mock.web.server.MockWebSession;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.session.ReactiveSessionInformation;
import org.springframework.security.core.session.ReactiveSessionRegistry;
import org.springframework.security.web.server.WebFilterExchange;
import org.springframework.security.web.server.authentication.ConcurrentSessionControlServerAuthenticationSuccessHandler;
import org.springframework.security.web.server.authentication.MaximumSessionsContext;
import org.springframework.security.web.server.authentication.ServerMaximumSessionsExceededHandler;
import org.springframework.security.web.server.authentication.SessionLimit;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilterChain;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
/**
* Tests for {@link ConcurrentSessionControlServerAuthenticationSuccessHandler}.
*
* @author Marcus da Coregio
*/
class ConcurrentSessionControlServerAuthenticationSuccessHandlerTests {
private ConcurrentSessionControlServerAuthenticationSuccessHandler strategy;
ReactiveSessionRegistry sessionRegistry = mock();
ServerWebExchange exchange = mock();
WebFilterChain chain = mock();
ServerMaximumSessionsExceededHandler handler = mock();
ArgumentCaptor<MaximumSessionsContext> contextCaptor = ArgumentCaptor.forClass(MaximumSessionsContext.class);
@BeforeEach
void setup() {
given(this.exchange.getResponse()).willReturn(new MockServerHttpResponse());
given(this.exchange.getRequest()).willReturn(MockServerHttpRequest.get("/").build());
given(this.exchange.getSession()).willReturn(Mono.just(new MockWebSession()));
given(this.handler.handle(any())).willReturn(Mono.empty());
this.strategy = new ConcurrentSessionControlServerAuthenticationSuccessHandler(this.sessionRegistry,
this.handler);
}
@Test
void constructorWhenNullRegistryThenException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new ConcurrentSessionControlServerAuthenticationSuccessHandler(null, this.handler))
.withMessage("sessionRegistry cannot be null");
}
@Test
void constructorWhenNullHandlerThenException() {
assertThatIllegalArgumentException()
.isThrownBy(
() -> new ConcurrentSessionControlServerAuthenticationSuccessHandler(this.sessionRegistry, null))
.withMessage("maximumSessionsExceededHandler cannot be null");
}
@Test
void setMaximumSessionsForAuthenticationWhenNullThenException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.strategy.setSessionLimit(null))
.withMessage("sessionLimit cannot be null");
}
@Test
void onAuthenticationWhenSessionLimitIsUnlimitedThenDoNothing() {
ServerMaximumSessionsExceededHandler handler = mock(ServerMaximumSessionsExceededHandler.class);
this.strategy = new ConcurrentSessionControlServerAuthenticationSuccessHandler(this.sessionRegistry, handler);
this.strategy.setSessionLimit(SessionLimit.UNLIMITED);
this.strategy.onAuthenticationSuccess(null, TestAuthentication.authenticatedUser()).block();
verifyNoInteractions(handler, this.sessionRegistry);
}
@Test
void onAuthenticationWhenMaximumSessionsIsOneAndExceededThenHandlerIsCalled() {
Authentication authentication = TestAuthentication.authenticatedUser();
List<ReactiveSessionInformation> sessions = Arrays.asList(createSessionInformation("100"),
createSessionInformation("101"));
given(this.sessionRegistry.getAllSessions(authentication.getPrincipal()))
.willReturn(Flux.fromIterable(sessions));
this.strategy.onAuthenticationSuccess(new WebFilterExchange(this.exchange, this.chain), authentication).block();
verify(this.handler).handle(this.contextCaptor.capture());
assertThat(this.contextCaptor.getValue().getMaximumSessionsAllowed()).isEqualTo(1);
assertThat(this.contextCaptor.getValue().getSessions()).isEqualTo(sessions);
assertThat(this.contextCaptor.getValue().getAuthentication()).isEqualTo(authentication);
}
@Test
void onAuthenticationWhenMaximumSessionsIsGreaterThanOneAndExceededThenHandlerIsCalled() {
this.strategy.setSessionLimit(SessionLimit.of(5));
Authentication authentication = TestAuthentication.authenticatedUser();
List<ReactiveSessionInformation> sessions = Arrays.asList(createSessionInformation("100"),
createSessionInformation("101"), createSessionInformation("102"), createSessionInformation("103"),
createSessionInformation("104"));
given(this.sessionRegistry.getAllSessions(authentication.getPrincipal()))
.willReturn(Flux.fromIterable(sessions));
this.strategy.onAuthenticationSuccess(new WebFilterExchange(this.exchange, this.chain), authentication).block();
verify(this.handler).handle(this.contextCaptor.capture());
assertThat(this.contextCaptor.getValue().getMaximumSessionsAllowed()).isEqualTo(5);
assertThat(this.contextCaptor.getValue().getSessions()).isEqualTo(sessions);
assertThat(this.contextCaptor.getValue().getAuthentication()).isEqualTo(authentication);
}
@Test
void onAuthenticationWhenMaximumSessionsForUsersAreDifferentThenHandlerIsCalledWhereNeeded() {
Authentication user = TestAuthentication.authenticatedUser();
Authentication admin = TestAuthentication.authenticatedAdmin();
this.strategy.setSessionLimit((authentication) -> {
if (authentication.equals(user)) {
return Mono.just(1);
}
return Mono.just(3);
});
List<ReactiveSessionInformation> userSessions = Arrays.asList(createSessionInformation("100"));
List<ReactiveSessionInformation> adminSessions = Arrays.asList(createSessionInformation("200"),
createSessionInformation("201"));
given(this.sessionRegistry.getAllSessions(user.getPrincipal())).willReturn(Flux.fromIterable(userSessions));
given(this.sessionRegistry.getAllSessions(admin.getPrincipal())).willReturn(Flux.fromIterable(adminSessions));
this.strategy.onAuthenticationSuccess(new WebFilterExchange(this.exchange, this.chain), user).block();
this.strategy.onAuthenticationSuccess(new WebFilterExchange(this.exchange, this.chain), admin).block();
verify(this.handler).handle(this.contextCaptor.capture());
assertThat(this.contextCaptor.getValue().getMaximumSessionsAllowed()).isEqualTo(1);
assertThat(this.contextCaptor.getValue().getSessions()).isEqualTo(userSessions);
assertThat(this.contextCaptor.getValue().getAuthentication()).isEqualTo(user);
}
private ReactiveSessionInformation createSessionInformation(String sessionId) {
return new ReactiveSessionInformation(sessionId, "principal", Instant.now());
}
}

View File

@@ -1,106 +0,0 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.server.authentication.session;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.session.InMemoryReactiveSessionRegistry;
import org.springframework.security.core.session.ReactiveSessionInformation;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link InMemoryReactiveSessionRegistry}.
*/
class InMemoryReactiveSessionRegistryTests {
InMemoryReactiveSessionRegistry sessionRegistry = new InMemoryReactiveSessionRegistry();
Instant now = LocalDate.of(2023, 11, 21).atStartOfDay().toInstant(ZoneOffset.UTC);
@Test
void saveWhenPrincipalThenRegisterPrincipalSession() {
Authentication authentication = TestAuthentication.authenticatedUser();
ReactiveSessionInformation sessionInformation = new ReactiveSessionInformation(authentication.getPrincipal(),
"1234", this.now);
this.sessionRegistry.saveSessionInformation(sessionInformation).block();
List<ReactiveSessionInformation> principalSessions = this.sessionRegistry
.getAllSessions(authentication.getPrincipal())
.collectList()
.block();
assertThat(principalSessions).hasSize(1);
assertThat(this.sessionRegistry.getSessionInformation("1234").block()).isNotNull();
}
@Test
void getAllSessionsWhenMultipleSessionsThenReturnAll() {
Authentication authentication = TestAuthentication.authenticatedUser();
ReactiveSessionInformation sessionInformation1 = new ReactiveSessionInformation(authentication.getPrincipal(),
"1234", this.now);
ReactiveSessionInformation sessionInformation2 = new ReactiveSessionInformation(authentication.getPrincipal(),
"4321", this.now);
ReactiveSessionInformation sessionInformation3 = new ReactiveSessionInformation(authentication.getPrincipal(),
"9876", this.now);
this.sessionRegistry.saveSessionInformation(sessionInformation1).block();
this.sessionRegistry.saveSessionInformation(sessionInformation2).block();
this.sessionRegistry.saveSessionInformation(sessionInformation3).block();
List<ReactiveSessionInformation> sessions = this.sessionRegistry.getAllSessions(authentication.getPrincipal())
.collectList()
.block();
assertThat(sessions).hasSize(3);
assertThat(this.sessionRegistry.getSessionInformation("1234").block()).isNotNull();
assertThat(this.sessionRegistry.getSessionInformation("4321").block()).isNotNull();
assertThat(this.sessionRegistry.getSessionInformation("9876").block()).isNotNull();
}
@Test
void removeSessionInformationThenSessionIsRemoved() {
Authentication authentication = TestAuthentication.authenticatedUser();
ReactiveSessionInformation sessionInformation = new ReactiveSessionInformation(authentication.getPrincipal(),
"1234", this.now);
this.sessionRegistry.saveSessionInformation(sessionInformation).block();
this.sessionRegistry.removeSessionInformation("1234").block();
List<ReactiveSessionInformation> sessions = this.sessionRegistry.getAllSessions(authentication.getName())
.collectList()
.block();
assertThat(this.sessionRegistry.getSessionInformation("1234").block()).isNull();
assertThat(sessions).isEmpty();
}
@Test
void updateLastAccessTimeThenUpdated() {
Authentication authentication = TestAuthentication.authenticatedUser();
ReactiveSessionInformation sessionInformation = new ReactiveSessionInformation(authentication.getPrincipal(),
"1234", this.now);
this.sessionRegistry.saveSessionInformation(sessionInformation).block();
ReactiveSessionInformation saved = this.sessionRegistry.getSessionInformation("1234").block();
assertThat(saved.getLastAccessTime()).isNotNull();
Instant lastAccessTimeBefore = saved.getLastAccessTime();
this.sessionRegistry.updateLastAccessTime("1234").block();
saved = this.sessionRegistry.getSessionInformation("1234").block();
assertThat(saved.getLastAccessTime()).isNotNull();
assertThat(saved.getLastAccessTime()).isAfter(lastAccessTimeBefore);
}
}

View File

@@ -1,115 +0,0 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.server.authentication.session;
import java.time.Instant;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.session.ReactiveSessionInformation;
import org.springframework.security.web.server.authentication.InvalidateLeastUsedServerMaximumSessionsExceededHandler;
import org.springframework.security.web.server.authentication.MaximumSessionsContext;
import org.springframework.web.server.session.InMemoryWebSessionStore;
import org.springframework.web.server.session.WebSessionStore;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
/**
* Tests for {@link InvalidateLeastUsedServerMaximumSessionsExceededHandler}
*
* @author Marcus da Coregio
*/
class InvalidateLeastUsedServerMaximumSessionsExceededHandlerTests {
InvalidateLeastUsedServerMaximumSessionsExceededHandler handler;
WebSessionStore webSessionStore = spy(new InMemoryWebSessionStore());
@BeforeEach
void setup() {
this.handler = new InvalidateLeastUsedServerMaximumSessionsExceededHandler(this.webSessionStore);
}
@Test
void handleWhenInvokedThenInvalidatesLeastRecentlyUsedSessions() {
ReactiveSessionInformation session1 = mock(ReactiveSessionInformation.class);
ReactiveSessionInformation session2 = mock(ReactiveSessionInformation.class);
given(session1.getLastAccessTime()).willReturn(Instant.ofEpochMilli(1700827760010L));
given(session2.getLastAccessTime()).willReturn(Instant.ofEpochMilli(1700827760000L));
given(session2.getSessionId()).willReturn("session2");
given(session2.invalidate()).willReturn(Mono.empty());
MaximumSessionsContext context = new MaximumSessionsContext(mock(Authentication.class),
List.of(session1, session2), 2, null);
this.handler.handle(context).block();
verify(session2).invalidate();
verify(session1).getLastAccessTime(); // used by comparator to sort the sessions
verify(session2).getLastAccessTime(); // used by comparator to sort the sessions
verify(session2).getSessionId(); // used to invalidate session against the
// WebSessionStore
verify(this.webSessionStore).removeSession("session2");
verifyNoMoreInteractions(this.webSessionStore);
verifyNoMoreInteractions(session2);
verifyNoMoreInteractions(session1);
}
@Test
void handleWhenMoreThanOneSessionToInvalidateThenInvalidatesAllOfThem() {
ReactiveSessionInformation session1 = mock(ReactiveSessionInformation.class);
ReactiveSessionInformation session2 = mock(ReactiveSessionInformation.class);
ReactiveSessionInformation session3 = mock(ReactiveSessionInformation.class);
given(session1.getLastAccessTime()).willReturn(Instant.ofEpochMilli(1700827760010L));
given(session2.getLastAccessTime()).willReturn(Instant.ofEpochMilli(1700827760020L));
given(session3.getLastAccessTime()).willReturn(Instant.ofEpochMilli(1700827760030L));
given(session1.invalidate()).willReturn(Mono.empty());
given(session2.invalidate()).willReturn(Mono.empty());
given(session1.getSessionId()).willReturn("session1");
given(session2.getSessionId()).willReturn("session2");
MaximumSessionsContext context = new MaximumSessionsContext(mock(Authentication.class),
List.of(session1, session2, session3), 2, null);
this.handler.handle(context).block();
// @formatter:off
verify(session1).invalidate();
verify(session2).invalidate();
verify(session1).getSessionId();
verify(session2).getSessionId();
verify(session1, atLeastOnce()).getLastAccessTime(); // used by comparator to sort the sessions
verify(session2, atLeastOnce()).getLastAccessTime(); // used by comparator to sort the sessions
verify(session3, atLeastOnce()).getLastAccessTime(); // used by comparator to sort the sessions
verify(this.webSessionStore).removeSession("session1");
verify(this.webSessionStore).removeSession("session2");
verifyNoMoreInteractions(this.webSessionStore);
verifyNoMoreInteractions(session1);
verifyNoMoreInteractions(session2);
verifyNoMoreInteractions(session3);
// @formatter:on
}
}

View File

@@ -1,57 +0,0 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.server.authentication.session;
import java.util.Collections;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.web.authentication.session.SessionAuthenticationException;
import org.springframework.security.web.server.authentication.MaximumSessionsContext;
import org.springframework.security.web.server.authentication.PreventLoginServerMaximumSessionsExceededHandler;
import org.springframework.web.server.WebSession;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link PreventLoginServerMaximumSessionsExceededHandler}.
*
* @author Marcus da Coregio
*/
class PreventLoginServerMaximumSessionsExceededHandlerTests {
@Test
void handleWhenInvokedThenInvalidateWebSessionAndThrowsSessionAuthenticationException() {
PreventLoginServerMaximumSessionsExceededHandler handler = new PreventLoginServerMaximumSessionsExceededHandler();
WebSession webSession = mock();
given(webSession.invalidate()).willReturn(Mono.empty());
MaximumSessionsContext context = new MaximumSessionsContext(TestAuthentication.authenticatedUser(),
Collections.emptyList(), 1, webSession);
StepVerifier.create(handler.handle(context)).expectErrorSatisfies((ex) -> {
assertThat(ex).isInstanceOf(SessionAuthenticationException.class);
assertThat(ex.getMessage()).isEqualTo("Maximum sessions exceeded");
}).verify();
verify(webSession).invalidate();
}
}

View File

@@ -1,84 +0,0 @@
/*
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.server.authentication.session;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import reactor.core.publisher.Mono;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.mock.web.server.MockWebSession;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.session.ReactiveSessionInformation;
import org.springframework.security.core.session.ReactiveSessionRegistry;
import org.springframework.security.web.server.WebFilterExchange;
import org.springframework.security.web.server.authentication.RegisterSessionServerAuthenticationSuccessHandler;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilterChain;
import org.springframework.web.server.WebSession;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
class RegisterSessionServerAuthenticationSuccessHandlerTests {
@InjectMocks
RegisterSessionServerAuthenticationSuccessHandler strategy;
@Mock
ReactiveSessionRegistry sessionRegistry;
@Mock
WebFilterChain filterChain;
WebSession session = new MockWebSession();
ServerWebExchange serverWebExchange = MockServerWebExchange.builder(MockServerHttpRequest.get(""))
.session(this.session)
.build();
@Test
void constructorWhenSessionRegistryNullThenException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new RegisterSessionServerAuthenticationSuccessHandler(null))
.withMessage("sessionRegistry cannot be null");
}
@Test
void onAuthenticationWhenSessionExistsThenSaveSessionInformation() {
given(this.sessionRegistry.saveSessionInformation(any())).willReturn(Mono.empty());
WebFilterExchange webFilterExchange = new WebFilterExchange(this.serverWebExchange, this.filterChain);
Authentication authentication = TestAuthentication.authenticatedUser();
this.strategy.onAuthenticationSuccess(webFilterExchange, authentication).block();
ArgumentCaptor<ReactiveSessionInformation> captor = ArgumentCaptor.forClass(ReactiveSessionInformation.class);
verify(this.sessionRegistry).saveSessionInformation(captor.capture());
assertThat(captor.getValue().getSessionId()).isEqualTo(this.session.getId());
assertThat(captor.getValue().getLastAccessTime()).isEqualTo(this.session.getLastAccessTime());
assertThat(captor.getValue().getPrincipal()).isEqualTo(authentication.getPrincipal());
}
}

View File

@@ -105,10 +105,4 @@ public class IpAddressMatcherTests {
"fe80::21f:5bff:fe33:bd68", 129));
}
@Test
public void invalidAddressThenIllegalArgumentException() {
assertThatIllegalArgumentException().isThrownBy(() -> new IpAddressMatcher("invalid-ip"))
.withMessage("ipAddress must start with a [, :, or a hexadecimal digit");
}
}