Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit c64b086

Browse files
committed
Add SecurityAssertions
This commit introduces a simple, internal test API for verifying aspects of an Authentication, like its name and authorities. Closes gh-17844
1 parent de10e08 commit c64b086

File tree

15 files changed

+180
-108
lines changed

15 files changed

+180
-108
lines changed

‎config/src/test/java/org/springframework/security/config/annotation/authentication/AuthenticationManagerBuilderTests.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.springframework.security.authentication.AuthenticationManager;
3333
import org.springframework.security.authentication.AuthenticationProvider;
3434
import org.springframework.security.authentication.ProviderManager;
35+
import org.springframework.security.authentication.SecurityAssertions;
3536
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
3637
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
3738
import org.springframework.security.config.ObjectPostProcessor;
@@ -44,7 +45,6 @@
4445
import org.springframework.security.config.test.SpringTestContextExtension;
4546
import org.springframework.security.core.Authentication;
4647
import org.springframework.security.core.AuthenticationException;
47-
import org.springframework.security.core.GrantedAuthority;
4848
import org.springframework.security.core.userdetails.PasswordEncodedUser;
4949
import org.springframework.security.core.userdetails.UserDetailsService;
5050
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
@@ -107,8 +107,7 @@ public void getAuthenticationManagerWhenGlobalPasswordEncoderBeanThenUsed() thro
107107
.getAuthenticationManager();
108108
Authentication auth = manager
109109
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("user", "password"));
110-
assertThat(auth.getName()).isEqualTo("user");
111-
assertThat(auth.getAuthorities()).extracting(GrantedAuthority::getAuthority).containsOnly("ROLE_USER");
110+
SecurityAssertions.assertThat(auth).name("user").hasAuthority("ROLE_USER");
112111
}
113112

114113
@Test
@@ -119,8 +118,7 @@ public void getAuthenticationManagerWhenProtectedPasswordEncoderBeanThenUsed() t
119118
.getAuthenticationManager();
120119
Authentication auth = manager
121120
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("user", "password"));
122-
assertThat(auth.getName()).isEqualTo("user");
123-
assertThat(auth.getAuthorities()).extracting(GrantedAuthority::getAuthority).containsOnly("ROLE_USER");
121+
SecurityAssertions.assertThat(auth).name("user").hasAuthority("ROLE_USER");
124122
}
125123

126124
@Test

‎config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java

Lines changed: 21 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import org.springframework.mock.web.MockHttpServletRequest;
4646
import org.springframework.mock.web.MockHttpServletResponse;
4747
import org.springframework.security.authentication.AuthenticationProvider;
48+
import org.springframework.security.authentication.SecurityAssertions;
4849
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
4950
import org.springframework.security.config.Customizer;
5051
import org.springframework.security.config.ObjectPostProcessor;
@@ -217,10 +218,9 @@ public void oauth2Login() throws Exception {
217218
Authentication authentication = this.securityContextRepository
218219
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
219220
.getAuthentication();
220-
assertThat(authentication.getAuthorities()).hasSize(1);
221-
assertThat(authentication.getAuthorities()).first()
222-
.isInstanceOf(OAuth2UserAuthority.class)
223-
.hasToString("OAUTH2_USER");
221+
SecurityAssertions.assertThat(authentication)
222+
.hasAuthority("OAUTH2_USER")
223+
.isInstanceOf(OAuth2UserAuthority.class);
224224
}
225225

226226
@Test
@@ -234,10 +234,9 @@ public void requestWhenCustomSecurityContextHolderStrategyThenUses() throws Exce
234234
Authentication authentication = this.securityContextRepository
235235
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
236236
.getAuthentication();
237-
assertThat(authentication.getAuthorities()).hasSize(1);
238-
assertThat(authentication.getAuthorities()).first()
239-
.isInstanceOf(OAuth2UserAuthority.class)
240-
.hasToString("OAUTH2_USER");
237+
SecurityAssertions.assertThat(authentication)
238+
.hasAuthority("OAUTH2_USER")
239+
.isInstanceOf(OAuth2UserAuthority.class);
241240
SecurityContextHolderStrategy strategy = this.context.getBean(SecurityContextHolderStrategy.class);
242241
verify(strategy, atLeastOnce()).getDeferredContext();
243242
SecurityContextChangedListener listener = this.context.getBean(SecurityContextChangedListener.class);
@@ -255,10 +254,9 @@ public void requestWhenOauth2LoginInLambdaThenAuthenticationContainsOauth2UserAu
255254
Authentication authentication = this.securityContextRepository
256255
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
257256
.getAuthentication();
258-
assertThat(authentication.getAuthorities()).hasSize(1);
259-
assertThat(authentication.getAuthorities()).first()
260-
.isInstanceOf(OAuth2UserAuthority.class)
261-
.hasToString("OAUTH2_USER");
257+
SecurityAssertions.assertThat(authentication)
258+
.hasAuthority("OAUTH2_USER")
259+
.isInstanceOf(OAuth2UserAuthority.class);
262260
}
263261

264262
// gh-6009
@@ -296,9 +294,7 @@ public void oauth2LoginCustomWithConfigurer() throws Exception {
296294
Authentication authentication = this.securityContextRepository
297295
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
298296
.getAuthentication();
299-
assertThat(authentication.getAuthorities()).hasSize(2);
300-
assertThat(authentication.getAuthorities()).first().hasToString("OAUTH2_USER");
301-
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OAUTH2_USER");
297+
SecurityAssertions.assertThat(authentication).hasAuthorities("OAUTH2_USER", "ROLE_OAUTH2_USER");
302298
}
303299

304300
@Test
@@ -317,9 +313,7 @@ public void oauth2LoginCustomWithBeanRegistration() throws Exception {
317313
Authentication authentication = this.securityContextRepository
318314
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
319315
.getAuthentication();
320-
assertThat(authentication.getAuthorities()).hasSize(2);
321-
assertThat(authentication.getAuthorities()).first().hasToString("OAUTH2_USER");
322-
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OAUTH2_USER");
316+
SecurityAssertions.assertThat(authentication).hasAuthorities("OAUTH2_USER", "ROLE_OAUTH2_USER");
323317
}
324318

325319
@Test
@@ -338,9 +332,7 @@ public void oauth2LoginCustomWithUserServiceBeanRegistration() throws Exception
338332
Authentication authentication = this.securityContextRepository
339333
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
340334
.getAuthentication();
341-
assertThat(authentication.getAuthorities()).hasSize(2);
342-
assertThat(authentication.getAuthorities()).first().hasToString("OAUTH2_USER");
343-
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OAUTH2_USER");
335+
SecurityAssertions.assertThat(authentication).hasAuthorities("OAUTH2_USER", "ROLE_OAUTH2_USER");
344336
}
345337

346338
// gh-5488
@@ -361,10 +353,9 @@ public void oauth2LoginConfigLoginProcessingUrl() throws Exception {
361353
Authentication authentication = this.securityContextRepository
362354
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
363355
.getAuthentication();
364-
assertThat(authentication.getAuthorities()).hasSize(1);
365-
assertThat(authentication.getAuthorities()).first()
366-
.isInstanceOf(OAuth2UserAuthority.class)
367-
.hasToString("OAUTH2_USER");
356+
SecurityAssertions.assertThat(authentication)
357+
.hasAuthority("OAUTH2_USER")
358+
.isInstanceOf(OAuth2UserAuthority.class);
368359
}
369360

370361
// gh-5521
@@ -570,10 +561,7 @@ public void oidcLogin() throws Exception {
570561
Authentication authentication = this.securityContextRepository
571562
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
572563
.getAuthentication();
573-
assertThat(authentication.getAuthorities()).hasSize(1);
574-
assertThat(authentication.getAuthorities()).first()
575-
.isInstanceOf(OidcUserAuthority.class)
576-
.hasToString("OIDC_USER");
564+
SecurityAssertions.assertThat(authentication).hasAuthority("OIDC_USER").isInstanceOf(OidcUserAuthority.class);
577565
}
578566

579567
@Test
@@ -593,9 +581,7 @@ public void requestWhenOauth2LoginInLambdaAndOidcThenAuthenticationContainsOidcU
593581
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
594582
.getAuthentication();
595583
assertThat(authentication.getAuthorities()).hasSize(1);
596-
assertThat(authentication.getAuthorities()).first()
597-
.isInstanceOf(OidcUserAuthority.class)
598-
.hasToString("OIDC_USER");
584+
SecurityAssertions.assertThat(authentication).hasAuthority("OIDC_USER").isInstanceOf(OidcUserAuthority.class);
599585
}
600586

601587
@Test
@@ -614,9 +600,7 @@ public void oidcLoginCustomWithConfigurer() throws Exception {
614600
Authentication authentication = this.securityContextRepository
615601
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
616602
.getAuthentication();
617-
assertThat(authentication.getAuthorities()).hasSize(2);
618-
assertThat(authentication.getAuthorities()).first().hasToString("OIDC_USER");
619-
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OIDC_USER");
603+
SecurityAssertions.assertThat(authentication).hasAuthorities("OIDC_USER", "ROLE_OIDC_USER");
620604
}
621605

622606
@Test
@@ -635,9 +619,7 @@ public void oidcLoginCustomWithBeanRegistration() throws Exception {
635619
Authentication authentication = this.securityContextRepository
636620
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
637621
.getAuthentication();
638-
assertThat(authentication.getAuthorities()).hasSize(2);
639-
assertThat(authentication.getAuthorities()).first().hasToString("OIDC_USER");
640-
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OIDC_USER");
622+
SecurityAssertions.assertThat(authentication).hasAuthorities("OIDC_USER", "ROLE_OIDC_USER");
641623
}
642624

643625
@Test
@@ -690,11 +672,7 @@ public void oidcLoginWhenOAuth2ClientBeansConfiguredThenNotShared() throws Excep
690672
Authentication authentication = this.securityContextRepository
691673
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
692674
.getAuthentication();
693-
assertThat(authentication.getAuthorities()).hasSize(1);
694-
assertThat(authentication.getAuthorities()).first()
695-
.isInstanceOf(OidcUserAuthority.class)
696-
.hasToString("OIDC_USER");
697-
675+
SecurityAssertions.assertThat(authentication).hasAuthority("OIDC_USER").isInstanceOf(OidcUserAuthority.class);
698676
// Ensure shared objects set for OAuth2 Client are not used
699677
ClientRegistrationRepository clientRegistrationRepository = this.spring.getContext()
700678
.getBean(ClientRegistrationRepository.class);

‎config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2674,6 +2674,7 @@ String authenticated(Authentication authentication) {
26742674
String requiresReadScope(JwtAuthenticationToken token) {
26752675
return token.getAuthorities()
26762676
.stream()
2677+
.filter((ga) -> ga.getAuthority().startsWith("SCOPE_"))
26772678
.map(GrantedAuthority::getAuthority)
26782679
.collect(Collectors.toList())
26792680
.toString();
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright 2004-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.authentication;
18+
19+
import java.util.Arrays;
20+
import java.util.Collection;
21+
import java.util.List;
22+
import java.util.Set;
23+
import java.util.function.Predicate;
24+
25+
import org.assertj.core.api.AbstractObjectAssert;
26+
import org.assertj.core.api.Assertions;
27+
import org.assertj.core.api.CollectionAssert;
28+
import org.assertj.core.api.Condition;
29+
import org.assertj.core.api.ObjectAssert;
30+
import org.jspecify.annotations.NullMarked;
31+
import org.jspecify.annotations.Nullable;
32+
33+
import org.springframework.security.core.Authentication;
34+
import org.springframework.security.core.GrantedAuthority;
35+
import org.springframework.security.core.authority.AuthorityUtils;
36+
37+
@NullMarked
38+
public final class SecurityAssertions {
39+
40+
private SecurityAssertions() {
41+
42+
}
43+
44+
public static AuthenticationAssert assertThat(@Nullable Authentication authentication) {
45+
Assertions.assertThat(authentication).isNotNull();
46+
return new AuthenticationAssert(authentication);
47+
}
48+
49+
public static final class AuthenticationAssert extends AbstractObjectAssert<AuthenticationAssert, Authentication> {
50+
51+
private final Authentication authentication;
52+
53+
private AuthenticationAssert(Authentication authentication) {
54+
super(authentication, AuthenticationAssert.class);
55+
this.authentication = authentication;
56+
}
57+
58+
public AuthenticationAssert name(String name) {
59+
Assertions.assertThat(this.authentication.getName()).isEqualTo(name);
60+
return this;
61+
}
62+
63+
public ObjectAssert<GrantedAuthority> hasAuthority(String authority) {
64+
Collection<? extends GrantedAuthority> actual = this.authentication.getAuthorities();
65+
for (GrantedAuthority element : actual) {
66+
if (element.getAuthority().equals(authority)) {
67+
return new ObjectAssert<>(element);
68+
}
69+
}
70+
throw new AssertionError(actual + " does not contain " + authority + " as expected");
71+
}
72+
73+
public CollectionAssert<GrantedAuthority> hasAuthorities(String... authorities) {
74+
HasAuthoritiesPredicate test = new HasAuthoritiesPredicate(authorities);
75+
return authorities().has(new Condition<>(test, "contains %s", Arrays.toString(authorities)));
76+
}
77+
78+
public CollectionAssert<GrantedAuthority> authorities() {
79+
return new CollectionAssert<>(this.authentication.getAuthorities());
80+
}
81+
82+
}
83+
84+
private static final class HasAuthoritiesPredicate implements Predicate<Collection<? extends GrantedAuthority>> {
85+
86+
private final Collection<String> expected;
87+
88+
private HasAuthoritiesPredicate(String... expected) {
89+
this.expected = List.of(expected);
90+
}
91+
92+
@Override
93+
public boolean test(Collection<? extends GrantedAuthority> actual) {
94+
Set<String> asString = AuthorityUtils.authorityListToSet(actual);
95+
return asString.containsAll(this.expected);
96+
}
97+
98+
}
99+
100+
}

‎ldap/spring-security-ldap.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ dependencies {
1919
exclude(group: 'org.springframework.data', module: 'spring-data-commons')
2020
}
2121

22-
testImplementation project(':spring-security-test')
22+
testImplementation project(path : ':spring-security-core', configuration : 'tests')
23+
testImplementation project(":spring-security-test")
2324
testImplementation 'org.slf4j:slf4j-api'
2425
testImplementation "org.assertj:assertj-core"
2526
testImplementation "org.junit.jupiter:junit-jupiter-api"

‎ldap/src/test/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProviderTests.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.springframework.security.authentication.DisabledException;
4343
import org.springframework.security.authentication.InternalAuthenticationServiceException;
4444
import org.springframework.security.authentication.LockedException;
45+
import org.springframework.security.authentication.SecurityAssertions;
4546
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
4647
import org.springframework.security.core.Authentication;
4748
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider.ContextFactory;
@@ -357,10 +358,10 @@ private void checkAuthentication(String rootDn, ActiveDirectoryLdapAuthenticatio
357358
.willReturn(new MockNamingEnumeration(sr));
358359
provider.contextFactory = createContextFactoryReturning(this.ctx);
359360
Authentication result = provider.authenticate(this.joe);
360-
assertThat(result.getAuthorities()).isEmpty();
361+
SecurityAssertions.assertThat(result).authorities().doesNotHaveToString("Admin");
361362
dca.addAttributeValue("memberOf", "CN=Admin,CN=Users,DC=mydomain,DC=eu");
362363
result = provider.authenticate(this.joe);
363-
assertThat(result.getAuthorities()).hasSize(1);
364+
SecurityAssertions.assertThat(result).hasAuthority("Admin");
364365
}
365366

366367
static class MockNamingEnumeration implements NamingEnumeration<SearchResult> {

‎oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProviderTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ public void authenticateWhenLoginSuccessThenReturnAuthentication() {
165165
assertThat(authentication.isAuthenticated()).isTrue();
166166
assertThat(authentication.getPrincipal()).isEqualTo(principal);
167167
assertThat(authentication.getCredentials()).isEqualTo("");
168-
assertThat(authentication.getAuthorities()).isEqualTo(authorities);
168+
assertThat(authentication.getAuthorities()).containsAll(authorities);
169169
assertThat(authentication.getClientRegistration()).isEqualTo(this.clientRegistration);
170170
assertThat(authentication.getAuthorizationExchange()).isEqualTo(this.authorizationExchange);
171171
assertThat(authentication.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());

‎oauth2/oauth2-resource-server/spring-security-oauth2-resource-server.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ dependencies {
1414

1515
provided 'jakarta.servlet:jakarta.servlet-api'
1616

17+
testImplementation project(path : ':spring-security-core', configuration : 'tests')
1718
testImplementation project(path: ':spring-security-oauth2-jose', configuration: 'tests')
1819
testImplementation 'com.squareup.okhttp3:mockwebserver'
1920
testImplementation 'com.fasterxml.jackson.core:jackson-databind'
@@ -27,5 +28,6 @@ dependencies {
2728
testImplementation "org.mockito:mockito-junit-jupiter"
2829
testImplementation "org.springframework:spring-test"
2930

31+
3032
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
3133
}

‎oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationConverterTests.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
import org.springframework.core.convert.converter.Converter;
2525
import org.springframework.security.authentication.AbstractAuthenticationToken;
26+
import org.springframework.security.authentication.SecurityAssertions;
2627
import org.springframework.security.core.GrantedAuthority;
2728
import org.springframework.security.core.authority.SimpleGrantedAuthority;
2829
import org.springframework.security.oauth2.jwt.Jwt;
@@ -46,9 +47,7 @@ public class JwtAuthenticationConverterTests {
4647
public void convertWhenDefaultGrantedAuthoritiesConverterSet() {
4748
Jwt jwt = TestJwts.jwt().claim("scope", "message:read message:write").build();
4849
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt);
49-
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
50-
assertThat(authorities).containsExactly(new SimpleGrantedAuthority("SCOPE_message:read"),
51-
new SimpleGrantedAuthority("SCOPE_message:write"));
50+
SecurityAssertions.assertThat(authentication).hasAuthorities("SCOPE_message:read", "SCOPE_message:write");
5251
}
5352

5453
@Test
@@ -65,8 +64,7 @@ public void convertWithOverriddenGrantedAuthoritiesConverter() {
6564
.asList(new SimpleGrantedAuthority("blah"));
6665
this.jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
6766
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt);
68-
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
69-
assertThat(authorities).containsExactly(new SimpleGrantedAuthority("blah"));
67+
SecurityAssertions.assertThat(authentication).hasAuthority("blah");
7068
}
7169

7270
@Test

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /