Add SecurityAssertions

This commit introduces a simple, internal test API for
verifying aspects of an Authentication, like its name
and authorities.

Closes gh-17844
This commit is contained in:
Josh Cummings 2025-09-03 16:33:16 -06:00
parent de10e08348
commit c64b086878
No known key found for this signature in database
GPG Key ID: 869B37A20E876129
15 changed files with 180 additions and 108 deletions

View File

@ -32,6 +32,7 @@ import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.SecurityAssertions;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.ObjectPostProcessor; import org.springframework.security.config.ObjectPostProcessor;
@ -44,7 +45,6 @@ import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension; import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.PasswordEncodedUser; import org.springframework.security.core.userdetails.PasswordEncodedUser;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.NoOpPasswordEncoder;
@ -107,8 +107,7 @@ public class AuthenticationManagerBuilderTests {
.getAuthenticationManager(); .getAuthenticationManager();
Authentication auth = manager Authentication auth = manager
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("user", "password")); .authenticate(UsernamePasswordAuthenticationToken.unauthenticated("user", "password"));
assertThat(auth.getName()).isEqualTo("user"); SecurityAssertions.assertThat(auth).name("user").hasAuthority("ROLE_USER");
assertThat(auth.getAuthorities()).extracting(GrantedAuthority::getAuthority).containsOnly("ROLE_USER");
} }
@Test @Test
@ -119,8 +118,7 @@ public class AuthenticationManagerBuilderTests {
.getAuthenticationManager(); .getAuthenticationManager();
Authentication auth = manager Authentication auth = manager
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("user", "password")); .authenticate(UsernamePasswordAuthenticationToken.unauthenticated("user", "password"));
assertThat(auth.getName()).isEqualTo("user"); SecurityAssertions.assertThat(auth).name("user").hasAuthority("ROLE_USER");
assertThat(auth.getAuthorities()).extracting(GrantedAuthority::getAuthority).containsOnly("ROLE_USER");
} }
@Test @Test

View File

@ -45,6 +45,7 @@ import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.SecurityAssertions;
import org.springframework.security.authentication.event.AuthenticationSuccessEvent; import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
import org.springframework.security.config.Customizer; import org.springframework.security.config.Customizer;
import org.springframework.security.config.ObjectPostProcessor; import org.springframework.security.config.ObjectPostProcessor;
@ -217,10 +218,9 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response)) .loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication(); .getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(1); SecurityAssertions.assertThat(authentication)
assertThat(authentication.getAuthorities()).first() .hasAuthority("OAUTH2_USER")
.isInstanceOf(OAuth2UserAuthority.class) .isInstanceOf(OAuth2UserAuthority.class);
.hasToString("OAUTH2_USER");
} }
@Test @Test
@ -234,10 +234,9 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response)) .loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication(); .getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(1); SecurityAssertions.assertThat(authentication)
assertThat(authentication.getAuthorities()).first() .hasAuthority("OAUTH2_USER")
.isInstanceOf(OAuth2UserAuthority.class) .isInstanceOf(OAuth2UserAuthority.class);
.hasToString("OAUTH2_USER");
SecurityContextHolderStrategy strategy = this.context.getBean(SecurityContextHolderStrategy.class); SecurityContextHolderStrategy strategy = this.context.getBean(SecurityContextHolderStrategy.class);
verify(strategy, atLeastOnce()).getDeferredContext(); verify(strategy, atLeastOnce()).getDeferredContext();
SecurityContextChangedListener listener = this.context.getBean(SecurityContextChangedListener.class); SecurityContextChangedListener listener = this.context.getBean(SecurityContextChangedListener.class);
@ -255,10 +254,9 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response)) .loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication(); .getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(1); SecurityAssertions.assertThat(authentication)
assertThat(authentication.getAuthorities()).first() .hasAuthority("OAUTH2_USER")
.isInstanceOf(OAuth2UserAuthority.class) .isInstanceOf(OAuth2UserAuthority.class);
.hasToString("OAUTH2_USER");
} }
// gh-6009 // gh-6009
@ -296,9 +294,7 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response)) .loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication(); .getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(2); SecurityAssertions.assertThat(authentication).hasAuthorities("OAUTH2_USER", "ROLE_OAUTH2_USER");
assertThat(authentication.getAuthorities()).first().hasToString("OAUTH2_USER");
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OAUTH2_USER");
} }
@Test @Test
@ -317,9 +313,7 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response)) .loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication(); .getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(2); SecurityAssertions.assertThat(authentication).hasAuthorities("OAUTH2_USER", "ROLE_OAUTH2_USER");
assertThat(authentication.getAuthorities()).first().hasToString("OAUTH2_USER");
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OAUTH2_USER");
} }
@Test @Test
@ -338,9 +332,7 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response)) .loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication(); .getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(2); SecurityAssertions.assertThat(authentication).hasAuthorities("OAUTH2_USER", "ROLE_OAUTH2_USER");
assertThat(authentication.getAuthorities()).first().hasToString("OAUTH2_USER");
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OAUTH2_USER");
} }
// gh-5488 // gh-5488
@ -361,10 +353,9 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response)) .loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication(); .getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(1); SecurityAssertions.assertThat(authentication)
assertThat(authentication.getAuthorities()).first() .hasAuthority("OAUTH2_USER")
.isInstanceOf(OAuth2UserAuthority.class) .isInstanceOf(OAuth2UserAuthority.class);
.hasToString("OAUTH2_USER");
} }
// gh-5521 // gh-5521
@ -570,10 +561,7 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response)) .loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication(); .getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(1); SecurityAssertions.assertThat(authentication).hasAuthority("OIDC_USER").isInstanceOf(OidcUserAuthority.class);
assertThat(authentication.getAuthorities()).first()
.isInstanceOf(OidcUserAuthority.class)
.hasToString("OIDC_USER");
} }
@Test @Test
@ -593,9 +581,7 @@ public class OAuth2LoginConfigurerTests {
.loadContext(new HttpRequestResponseHolder(this.request, this.response)) .loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication(); .getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(1); assertThat(authentication.getAuthorities()).hasSize(1);
assertThat(authentication.getAuthorities()).first() SecurityAssertions.assertThat(authentication).hasAuthority("OIDC_USER").isInstanceOf(OidcUserAuthority.class);
.isInstanceOf(OidcUserAuthority.class)
.hasToString("OIDC_USER");
} }
@Test @Test
@ -614,9 +600,7 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response)) .loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication(); .getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(2); SecurityAssertions.assertThat(authentication).hasAuthorities("OIDC_USER", "ROLE_OIDC_USER");
assertThat(authentication.getAuthorities()).first().hasToString("OIDC_USER");
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OIDC_USER");
} }
@Test @Test
@ -635,9 +619,7 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response)) .loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication(); .getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(2); SecurityAssertions.assertThat(authentication).hasAuthorities("OIDC_USER", "ROLE_OIDC_USER");
assertThat(authentication.getAuthorities()).first().hasToString("OIDC_USER");
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OIDC_USER");
} }
@Test @Test
@ -690,11 +672,7 @@ public class OAuth2LoginConfigurerTests {
Authentication authentication = this.securityContextRepository Authentication authentication = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(this.request, this.response)) .loadContext(new HttpRequestResponseHolder(this.request, this.response))
.getAuthentication(); .getAuthentication();
assertThat(authentication.getAuthorities()).hasSize(1); SecurityAssertions.assertThat(authentication).hasAuthority("OIDC_USER").isInstanceOf(OidcUserAuthority.class);
assertThat(authentication.getAuthorities()).first()
.isInstanceOf(OidcUserAuthority.class)
.hasToString("OIDC_USER");
// Ensure shared objects set for OAuth2 Client are not used // Ensure shared objects set for OAuth2 Client are not used
ClientRegistrationRepository clientRegistrationRepository = this.spring.getContext() ClientRegistrationRepository clientRegistrationRepository = this.spring.getContext()
.getBean(ClientRegistrationRepository.class); .getBean(ClientRegistrationRepository.class);

View File

@ -2674,6 +2674,7 @@ public class OAuth2ResourceServerConfigurerTests {
String requiresReadScope(JwtAuthenticationToken token) { String requiresReadScope(JwtAuthenticationToken token) {
return token.getAuthorities() return token.getAuthorities()
.stream() .stream()
.filter((ga) -> ga.getAuthority().startsWith("SCOPE_"))
.map(GrantedAuthority::getAuthority) .map(GrantedAuthority::getAuthority)
.collect(Collectors.toList()) .collect(Collectors.toList())
.toString(); .toString();

View File

@ -0,0 +1,100 @@
/*
* Copyright 2004-present 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.authentication;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import org.assertj.core.api.AbstractObjectAssert;
import org.assertj.core.api.Assertions;
import org.assertj.core.api.CollectionAssert;
import org.assertj.core.api.Condition;
import org.assertj.core.api.ObjectAssert;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
@NullMarked
public final class SecurityAssertions {
private SecurityAssertions() {
}
public static AuthenticationAssert assertThat(@Nullable Authentication authentication) {
Assertions.assertThat(authentication).isNotNull();
return new AuthenticationAssert(authentication);
}
public static final class AuthenticationAssert extends AbstractObjectAssert<AuthenticationAssert, Authentication> {
private final Authentication authentication;
private AuthenticationAssert(Authentication authentication) {
super(authentication, AuthenticationAssert.class);
this.authentication = authentication;
}
public AuthenticationAssert name(String name) {
Assertions.assertThat(this.authentication.getName()).isEqualTo(name);
return this;
}
public ObjectAssert<GrantedAuthority> hasAuthority(String authority) {
Collection<? extends GrantedAuthority> actual = this.authentication.getAuthorities();
for (GrantedAuthority element : actual) {
if (element.getAuthority().equals(authority)) {
return new ObjectAssert<>(element);
}
}
throw new AssertionError(actual + " does not contain " + authority + " as expected");
}
public CollectionAssert<GrantedAuthority> hasAuthorities(String... authorities) {
HasAuthoritiesPredicate test = new HasAuthoritiesPredicate(authorities);
return authorities().has(new Condition<>(test, "contains %s", Arrays.toString(authorities)));
}
public CollectionAssert<GrantedAuthority> authorities() {
return new CollectionAssert<>(this.authentication.getAuthorities());
}
}
private static final class HasAuthoritiesPredicate implements Predicate<Collection<? extends GrantedAuthority>> {
private final Collection<String> expected;
private HasAuthoritiesPredicate(String... expected) {
this.expected = List.of(expected);
}
@Override
public boolean test(Collection<? extends GrantedAuthority> actual) {
Set<String> asString = AuthorityUtils.authorityListToSet(actual);
return asString.containsAll(this.expected);
}
}
}

View File

@ -19,7 +19,8 @@ dependencies {
exclude(group: 'org.springframework.data', module: 'spring-data-commons') exclude(group: 'org.springframework.data', module: 'spring-data-commons')
} }
testImplementation project(':spring-security-test') testImplementation project(path : ':spring-security-core', configuration : 'tests')
testImplementation project(":spring-security-test")
testImplementation 'org.slf4j:slf4j-api' testImplementation 'org.slf4j:slf4j-api'
testImplementation "org.assertj:assertj-core" testImplementation "org.assertj:assertj-core"
testImplementation "org.junit.jupiter:junit-jupiter-api" testImplementation "org.junit.jupiter:junit-jupiter-api"

View File

@ -42,6 +42,7 @@ import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.DisabledException; import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.InternalAuthenticationServiceException; import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.LockedException; import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.SecurityAssertions;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider.ContextFactory; import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider.ContextFactory;
@ -357,10 +358,10 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
.willReturn(new MockNamingEnumeration(sr)); .willReturn(new MockNamingEnumeration(sr));
provider.contextFactory = createContextFactoryReturning(this.ctx); provider.contextFactory = createContextFactoryReturning(this.ctx);
Authentication result = provider.authenticate(this.joe); Authentication result = provider.authenticate(this.joe);
assertThat(result.getAuthorities()).isEmpty(); SecurityAssertions.assertThat(result).authorities().doesNotHaveToString("Admin");
dca.addAttributeValue("memberOf", "CN=Admin,CN=Users,DC=mydomain,DC=eu"); dca.addAttributeValue("memberOf", "CN=Admin,CN=Users,DC=mydomain,DC=eu");
result = provider.authenticate(this.joe); result = provider.authenticate(this.joe);
assertThat(result.getAuthorities()).hasSize(1); SecurityAssertions.assertThat(result).hasAuthority("Admin");
} }
static class MockNamingEnumeration implements NamingEnumeration<SearchResult> { static class MockNamingEnumeration implements NamingEnumeration<SearchResult> {

View File

@ -165,7 +165,7 @@ public class OAuth2LoginAuthenticationProviderTests {
assertThat(authentication.isAuthenticated()).isTrue(); assertThat(authentication.isAuthenticated()).isTrue();
assertThat(authentication.getPrincipal()).isEqualTo(principal); assertThat(authentication.getPrincipal()).isEqualTo(principal);
assertThat(authentication.getCredentials()).isEqualTo(""); assertThat(authentication.getCredentials()).isEqualTo("");
assertThat(authentication.getAuthorities()).isEqualTo(authorities); assertThat(authentication.getAuthorities()).containsAll(authorities);
assertThat(authentication.getClientRegistration()).isEqualTo(this.clientRegistration); assertThat(authentication.getClientRegistration()).isEqualTo(this.clientRegistration);
assertThat(authentication.getAuthorizationExchange()).isEqualTo(this.authorizationExchange); assertThat(authentication.getAuthorizationExchange()).isEqualTo(this.authorizationExchange);
assertThat(authentication.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken()); assertThat(authentication.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());

View File

@ -14,6 +14,7 @@ dependencies {
provided 'jakarta.servlet:jakarta.servlet-api' provided 'jakarta.servlet:jakarta.servlet-api'
testImplementation project(path : ':spring-security-core', configuration : 'tests')
testImplementation project(path: ':spring-security-oauth2-jose', configuration: 'tests') testImplementation project(path: ':spring-security-oauth2-jose', configuration: 'tests')
testImplementation 'com.squareup.okhttp3:mockwebserver' testImplementation 'com.squareup.okhttp3:mockwebserver'
testImplementation 'com.fasterxml.jackson.core:jackson-databind' testImplementation 'com.fasterxml.jackson.core:jackson-databind'
@ -27,5 +28,6 @@ dependencies {
testImplementation "org.mockito:mockito-junit-jupiter" testImplementation "org.mockito:mockito-junit-jupiter"
testImplementation "org.springframework:spring-test" testImplementation "org.springframework:spring-test"
testRuntimeOnly 'org.junit.platform:junit-platform-launcher' testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
} }

View File

@ -23,6 +23,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.SecurityAssertions;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.Jwt;
@ -46,9 +47,7 @@ public class JwtAuthenticationConverterTests {
public void convertWhenDefaultGrantedAuthoritiesConverterSet() { public void convertWhenDefaultGrantedAuthoritiesConverterSet() {
Jwt jwt = TestJwts.jwt().claim("scope", "message:read message:write").build(); Jwt jwt = TestJwts.jwt().claim("scope", "message:read message:write").build();
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt); AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt);
Collection<GrantedAuthority> authorities = authentication.getAuthorities(); SecurityAssertions.assertThat(authentication).hasAuthorities("SCOPE_message:read", "SCOPE_message:write");
assertThat(authorities).containsExactly(new SimpleGrantedAuthority("SCOPE_message:read"),
new SimpleGrantedAuthority("SCOPE_message:write"));
} }
@Test @Test
@ -65,8 +64,7 @@ public class JwtAuthenticationConverterTests {
.asList(new SimpleGrantedAuthority("blah")); .asList(new SimpleGrantedAuthority("blah"));
this.jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter); this.jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt); AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt);
Collection<GrantedAuthority> authorities = authentication.getAuthorities(); SecurityAssertions.assertThat(authentication).hasAuthority("blah");
assertThat(authorities).containsExactly(new SimpleGrantedAuthority("blah"));
} }
@Test @Test

View File

@ -17,11 +17,13 @@
package org.springframework.security.oauth2.server.resource.authentication; package org.springframework.security.oauth2.server.resource.authentication;
import java.util.Arrays; import java.util.Arrays;
import java.util.function.Predicate;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.authentication.SecurityAssertions;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.Jwt;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -48,7 +50,7 @@ public class JwtBearerTokenAuthenticationConverterTests {
BearerTokenAuthentication bearerToken = (BearerTokenAuthentication) token; BearerTokenAuthentication bearerToken = (BearerTokenAuthentication) token;
assertThat(bearerToken.getToken().getTokenValue()).isEqualTo("token-value"); assertThat(bearerToken.getToken().getTokenValue()).isEqualTo("token-value");
assertThat(bearerToken.getTokenAttributes()).containsOnlyKeys("claim"); assertThat(bearerToken.getTokenAttributes()).containsOnlyKeys("claim");
assertThat(bearerToken.getAuthorities()).isEmpty(); assertThat(bearerToken.getAuthorities()).noneMatch(isScope());
} }
@Test @Test
@ -62,8 +64,7 @@ public class JwtBearerTokenAuthenticationConverterTests {
AbstractAuthenticationToken token = this.converter.convert(jwt); AbstractAuthenticationToken token = this.converter.convert(jwt);
assertThat(token).isInstanceOf(BearerTokenAuthentication.class); assertThat(token).isInstanceOf(BearerTokenAuthentication.class);
BearerTokenAuthentication bearerToken = (BearerTokenAuthentication) token; BearerTokenAuthentication bearerToken = (BearerTokenAuthentication) token;
assertThat(bearerToken.getAuthorities()).containsExactly(new SimpleGrantedAuthority("SCOPE_message:read"), SecurityAssertions.assertThat(bearerToken).hasAuthorities("SCOPE_message:read", "SCOPE_message:write");
new SimpleGrantedAuthority("SCOPE_message:write"));
} }
@Test @Test
@ -77,8 +78,11 @@ public class JwtBearerTokenAuthenticationConverterTests {
AbstractAuthenticationToken token = this.converter.convert(jwt); AbstractAuthenticationToken token = this.converter.convert(jwt);
assertThat(token).isInstanceOf(BearerTokenAuthentication.class); assertThat(token).isInstanceOf(BearerTokenAuthentication.class);
BearerTokenAuthentication bearerToken = (BearerTokenAuthentication) token; BearerTokenAuthentication bearerToken = (BearerTokenAuthentication) token;
assertThat(bearerToken.getAuthorities()).containsExactly(new SimpleGrantedAuthority("SCOPE_message:read"), SecurityAssertions.assertThat(bearerToken).hasAuthorities("SCOPE_message:read", "SCOPE_message:write");
new SimpleGrantedAuthority("SCOPE_message:write")); }
static Predicate<GrantedAuthority> isScope() {
return (a) -> a.getAuthority().startsWith("SCOPE_");
} }
} }

View File

@ -23,10 +23,10 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import org.springframework.security.authentication.SecurityAssertions;
import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.jwt.BadJwtException; import org.springframework.security.oauth2.jwt.BadJwtException;
import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.Jwt;
@ -137,11 +137,7 @@ public class JwtReactiveAuthenticationManagerTests {
Authentication authentication = this.manager.authenticate(token).block(); Authentication authentication = this.manager.authenticate(token).block();
assertThat(authentication).isNotNull(); assertThat(authentication).isNotNull();
assertThat(authentication.isAuthenticated()).isTrue(); assertThat(authentication.isAuthenticated()).isTrue();
// @formatter:off SecurityAssertions.assertThat(authentication).hasAuthorities("SCOPE_message:read", "SCOPE_message:write");
assertThat(authentication.getAuthorities())
.extracting(GrantedAuthority::getAuthority)
.containsOnly("SCOPE_message:read", "SCOPE_message:write");
// @formatter:on
} }
} }

View File

@ -21,12 +21,15 @@ import java.time.Instant;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.function.Predicate;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.SecurityAssertions;
import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal; import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames; import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
import org.springframework.security.oauth2.core.TestOAuth2AuthenticatedPrincipals; import org.springframework.security.oauth2.core.TestOAuth2AuthenticatedPrincipals;
@ -75,10 +78,7 @@ public class OpaqueTokenAuthenticationProviderTests {
.containsEntry(OAuth2TokenIntrospectionClaimNames.SUB, "Z5O3upPC88QrAjx00dis") .containsEntry(OAuth2TokenIntrospectionClaimNames.SUB, "Z5O3upPC88QrAjx00dis")
.containsEntry(OAuth2TokenIntrospectionClaimNames.USERNAME, "jdoe") .containsEntry(OAuth2TokenIntrospectionClaimNames.USERNAME, "jdoe")
.containsEntry("extension_field", "twenty-seven"); .containsEntry("extension_field", "twenty-seven");
assertThat(result.getAuthorities()) SecurityAssertions.assertThat(result).hasAuthorities("SCOPE_read", "SCOPE_write", "SCOPE_dolphin");
.extracting("authority")
.containsExactly("SCOPE_read", "SCOPE_write",
"SCOPE_dolphin");
// @formatter:on // @formatter:on
} }
@ -97,7 +97,7 @@ public class OpaqueTokenAuthenticationProviderTests {
.isNotNull() .isNotNull()
.doesNotContainKey(OAuth2TokenIntrospectionClaimNames.SCOPE); .doesNotContainKey(OAuth2TokenIntrospectionClaimNames.SCOPE);
// @formatter:on // @formatter:on
assertThat(result.getAuthorities()).isEmpty(); SecurityAssertions.assertThat(result).authorities().noneMatch(isScope());
} }
@Test @Test
@ -146,4 +146,8 @@ public class OpaqueTokenAuthenticationProviderTests {
verifyNoMoreInteractions(introspector, authenticationConverter); verifyNoMoreInteractions(introspector, authenticationConverter);
} }
static Predicate<GrantedAuthority> isScope() {
return (a) -> a.getAuthority().startsWith("SCOPE_");
}
} }

View File

@ -21,13 +21,16 @@ import java.time.Instant;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.function.Predicate;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.SecurityAssertions;
import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal; import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames; import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
import org.springframework.security.oauth2.core.TestOAuth2AuthenticatedPrincipals; import org.springframework.security.oauth2.core.TestOAuth2AuthenticatedPrincipals;
@ -76,10 +79,7 @@ public class OpaqueTokenReactiveAuthenticationManagerTests {
.containsEntry(OAuth2TokenIntrospectionClaimNames.SUB, "Z5O3upPC88QrAjx00dis") .containsEntry(OAuth2TokenIntrospectionClaimNames.SUB, "Z5O3upPC88QrAjx00dis")
.containsEntry(OAuth2TokenIntrospectionClaimNames.USERNAME, "jdoe") .containsEntry(OAuth2TokenIntrospectionClaimNames.USERNAME, "jdoe")
.containsEntry("extension_field", "twenty-seven"); .containsEntry("extension_field", "twenty-seven");
assertThat(result.getAuthorities()) SecurityAssertions.assertThat(result).hasAuthorities("SCOPE_read", "SCOPE_write", "SCOPE_dolphin");
.extracting("authority")
.containsExactly("SCOPE_read", "SCOPE_write",
"SCOPE_dolphin");
// @formatter:on // @formatter:on
} }
@ -94,7 +94,7 @@ public class OpaqueTokenReactiveAuthenticationManagerTests {
assertThat(result.getPrincipal()).isInstanceOf(OAuth2IntrospectionAuthenticatedPrincipal.class); assertThat(result.getPrincipal()).isInstanceOf(OAuth2IntrospectionAuthenticatedPrincipal.class);
Map<String, Object> attributes = ((OAuth2AuthenticatedPrincipal) result.getPrincipal()).getAttributes(); Map<String, Object> attributes = ((OAuth2AuthenticatedPrincipal) result.getPrincipal()).getAttributes();
assertThat(attributes).isNotNull().doesNotContainKey(OAuth2TokenIntrospectionClaimNames.SCOPE); assertThat(attributes).isNotNull().doesNotContainKey(OAuth2TokenIntrospectionClaimNames.SCOPE);
assertThat(result.getAuthorities()).isEmpty(); SecurityAssertions.assertThat(result).authorities().noneMatch(isScope());
} }
@Test @Test
@ -145,4 +145,8 @@ public class OpaqueTokenReactiveAuthenticationManagerTests {
verifyNoMoreInteractions(introspector, authenticationConverter); verifyNoMoreInteractions(introspector, authenticationConverter);
} }
static Predicate<GrantedAuthority> isScope() {
return (a) -> a.getAuthority().startsWith("SCOPE_");
}
} }

View File

@ -17,19 +17,17 @@
package org.springframework.security.oauth2.server.resource.authentication; package org.springframework.security.oauth2.server.resource.authentication;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.function.Predicate;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.SecurityAssertions;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.TestJwts; import org.springframework.security.oauth2.jwt.TestJwts;
import static org.assertj.core.api.Assertions.assertThat;
/** /**
* Tests for {@link ReactiveJwtAuthenticationConverterAdapter} * Tests for {@link ReactiveJwtAuthenticationConverterAdapter}
* *
@ -46,40 +44,28 @@ public class ReactiveJwtAuthenticationConverterAdapterTests {
public void convertWhenTokenHasScopeAttributeThenTranslatedToAuthorities() { public void convertWhenTokenHasScopeAttributeThenTranslatedToAuthorities() {
Jwt jwt = TestJwts.jwt().claim("scope", "message:read message:write").build(); Jwt jwt = TestJwts.jwt().claim("scope", "message:read message:write").build();
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block(); AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
Collection<GrantedAuthority> authorities = authentication.getAuthorities(); SecurityAssertions.assertThat(authentication).hasAuthorities("SCOPE_message:read", "SCOPE_message:write");
// @formatter:off
assertThat(authorities)
.containsExactly(new SimpleGrantedAuthority("SCOPE_message:read"),
new SimpleGrantedAuthority("SCOPE_message:write"));
// @formatter:on
} }
@Test @Test
public void convertWhenTokenHasEmptyScopeAttributeThenTranslatedToNoAuthorities() { public void convertWhenTokenHasEmptyScopeAttributeThenTranslatedToNoAuthorities() {
Jwt jwt = TestJwts.jwt().claim("scope", "").build(); Jwt jwt = TestJwts.jwt().claim("scope", "").build();
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block(); AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
Collection<GrantedAuthority> authorities = authentication.getAuthorities(); SecurityAssertions.assertThat(authentication).authorities().noneMatch(isScope());
assertThat(authorities).containsExactly();
} }
@Test @Test
public void convertWhenTokenHasScpAttributeThenTranslatedToAuthorities() { public void convertWhenTokenHasScpAttributeThenTranslatedToAuthorities() {
Jwt jwt = TestJwts.jwt().claim("scp", Arrays.asList("message:read", "message:write")).build(); Jwt jwt = TestJwts.jwt().claim("scp", Arrays.asList("message:read", "message:write")).build();
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block(); AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
Collection<GrantedAuthority> authorities = authentication.getAuthorities(); SecurityAssertions.assertThat(authentication).hasAuthorities("SCOPE_message:read", "SCOPE_message:write");
// @formatter:off
assertThat(authorities)
.containsExactly(new SimpleGrantedAuthority("SCOPE_message:read"),
new SimpleGrantedAuthority("SCOPE_message:write"));
// @formatter:on
} }
@Test @Test
public void convertWhenTokenHasEmptyScpAttributeThenTranslatedToNoAuthorities() { public void convertWhenTokenHasEmptyScpAttributeThenTranslatedToNoAuthorities() {
Jwt jwt = TestJwts.jwt().claim("scp", Arrays.asList()).build(); Jwt jwt = TestJwts.jwt().claim("scp", Arrays.asList()).build();
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block(); AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
Collection<GrantedAuthority> authorities = authentication.getAuthorities(); SecurityAssertions.assertThat(authentication).authorities().noneMatch(isScope());
assertThat(authorities).containsExactly();
} }
@Test @Test
@ -89,12 +75,7 @@ public class ReactiveJwtAuthenticationConverterAdapterTests {
.claim("scope", "missive:read missive:write") .claim("scope", "missive:read missive:write")
.build(); .build();
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block(); AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
Collection<GrantedAuthority> authorities = authentication.getAuthorities(); SecurityAssertions.assertThat(authentication).hasAuthorities("SCOPE_missive:read", "SCOPE_missive:write");
// @formatter:off
assertThat(authorities)
.containsExactly(new SimpleGrantedAuthority("SCOPE_missive:read"),
new SimpleGrantedAuthority("SCOPE_missive:write"));
// @formatter:on
} }
@Test @Test
@ -106,8 +87,11 @@ public class ReactiveJwtAuthenticationConverterAdapterTests {
.build(); .build();
// @formatter:on // @formatter:on
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block(); AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
Collection<GrantedAuthority> authorities = authentication.getAuthorities(); SecurityAssertions.assertThat(authentication).authorities().noneMatch(isScope());
assertThat(authorities).containsExactly(); }
static Predicate<GrantedAuthority> isScope() {
return (a) -> a.getAuthority().startsWith("SCOPE_");
} }
} }

View File

@ -71,6 +71,7 @@ import org.opensaml.xmlsec.encryption.impl.EncryptedDataBuilder;
import org.opensaml.xmlsec.signature.support.SignatureConstants; import org.opensaml.xmlsec.signature.support.SignatureConstants;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.SecurityAssertions;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.AuthorityUtils;
@ -734,7 +735,7 @@ public class OpenSaml5AuthenticationProviderTests {
Response response = TestOpenSamlObjects.signedResponseWithOneAssertion(); Response response = TestOpenSamlObjects.signedResponseWithOneAssertion();
Saml2AuthenticationToken token = token(response, verifying(registration())); Saml2AuthenticationToken token = token(response, verifying(registration()));
Authentication authentication = provider.authenticate(token); Authentication authentication = provider.authenticate(token);
assertThat(AuthorityUtils.authorityListToSet(authentication.getAuthorities())).containsExactly("CUSTOM"); SecurityAssertions.assertThat(authentication).hasAuthority("CUSTOM");
verify(grantedAuthoritiesConverter).convert(any()); verify(grantedAuthoritiesConverter).convert(any());
} }