diff --git a/test/src/main/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurers.java b/test/src/main/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurers.java index f0d7fec53a..975d6ea82c 100644 --- a/test/src/main/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurers.java +++ b/test/src/main/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurers.java @@ -471,7 +471,7 @@ public class SecurityMockServerConfigurers { private OAuth2AccessToken accessToken; private OidcIdToken idToken; private OidcUserInfo userInfo; - private OidcUser oidcUser; + private Supplier oidcUser = this::defaultPrincipal; private Collection authorities; ServerOAuth2AuthorizedClientRepository authorizedClientRepository = @@ -491,6 +491,7 @@ public class SecurityMockServerConfigurers { public OidcLoginMutator authorities(Collection authorities) { Assert.notNull(authorities, "authorities cannot be null"); this.authorities = authorities; + this.oidcUser = this::defaultPrincipal; return this; } @@ -503,6 +504,7 @@ public class SecurityMockServerConfigurers { public OidcLoginMutator authorities(GrantedAuthority... authorities) { Assert.notNull(authorities, "authorities cannot be null"); this.authorities = Arrays.asList(authorities); + this.oidcUser = this::defaultPrincipal; return this; } @@ -517,6 +519,7 @@ public class SecurityMockServerConfigurers { builder.subject("test-subject"); idTokenBuilderConsumer.accept(builder); this.idToken = builder.build(); + this.oidcUser = this::defaultPrincipal; return this; } @@ -530,6 +533,7 @@ public class SecurityMockServerConfigurers { OidcUserInfo.Builder builder = OidcUserInfo.builder(); userInfoBuilderConsumer.accept(builder); this.userInfo = builder.build(); + this.oidcUser = this::defaultPrincipal; return this; } @@ -543,7 +547,7 @@ public class SecurityMockServerConfigurers { * @return the {@link OidcLoginMutator} for further configuration */ public OidcLoginMutator oidcUser(OidcUser oidcUser) { - this.oidcUser = oidcUser; + this.oidcUser = () -> oidcUser; return this; } @@ -601,7 +605,7 @@ public class SecurityMockServerConfigurers { } private OAuth2AuthenticationToken getToken() { - OidcUser oidcUser = getOidcUser(); + OidcUser oidcUser = this.oidcUser.get(); return new OAuth2AuthenticationToken(oidcUser, oidcUser.getAuthorities(), this.clientRegistration.getRegistrationId()); } @@ -634,12 +638,8 @@ public class SecurityMockServerConfigurers { return this.userInfo; } - private OidcUser getOidcUser() { - if (this.oidcUser == null) { - return new DefaultOidcUser(getAuthorities(), getOidcIdToken(), this.userInfo); - } else { - return this.oidcUser; - } + private OidcUser defaultPrincipal() { + return new DefaultOidcUser(getAuthorities(), getOidcIdToken(), this.userInfo); } } } diff --git a/test/src/main/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessors.java b/test/src/main/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessors.java index 854cf09fc9..54de4d0eb7 100644 --- a/test/src/main/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessors.java +++ b/test/src/main/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessors.java @@ -1432,7 +1432,7 @@ public final class SecurityMockMvcRequestPostProcessors { private OAuth2AccessToken accessToken; private OidcIdToken idToken; private OidcUserInfo userInfo; - private OidcUser oidcUser; + private Supplier oidcUser = this::defaultPrincipal; private Collection authorities; private OidcLoginRequestPostProcessor(OAuth2AccessToken accessToken) { @@ -1449,6 +1449,7 @@ public final class SecurityMockMvcRequestPostProcessors { public OidcLoginRequestPostProcessor authorities(Collection authorities) { Assert.notNull(authorities, "authorities cannot be null"); this.authorities = authorities; + this.oidcUser = this::defaultPrincipal; return this; } @@ -1461,6 +1462,7 @@ public final class SecurityMockMvcRequestPostProcessors { public OidcLoginRequestPostProcessor authorities(GrantedAuthority... authorities) { Assert.notNull(authorities, "authorities cannot be null"); this.authorities = Arrays.asList(authorities); + this.oidcUser = this::defaultPrincipal; return this; } @@ -1475,6 +1477,7 @@ public final class SecurityMockMvcRequestPostProcessors { builder.subject("test-subject"); idTokenBuilderConsumer.accept(builder); this.idToken = builder.build(); + this.oidcUser = this::defaultPrincipal; return this; } @@ -1488,20 +1491,19 @@ public final class SecurityMockMvcRequestPostProcessors { OidcUserInfo.Builder builder = OidcUserInfo.builder(); userInfoBuilderConsumer.accept(builder); this.userInfo = builder.build(); + this.oidcUser = this::defaultPrincipal; return this; } /** * Use the provided {@link OidcUser} as the authenticated user. * - * Supplying an {@link OidcUser} will take precedence over {@link #idToken}, {@link #userInfo}, - * and list of {@link GrantedAuthority}s to use. * * @param oidcUser the {@link OidcUser} to use * @return the {@link OidcLoginRequestPostProcessor} for further configuration */ public OidcLoginRequestPostProcessor oidcUser(OidcUser oidcUser) { - this.oidcUser = oidcUser; + this.oidcUser = () -> oidcUser; return this; } @@ -1524,7 +1526,7 @@ public final class SecurityMockMvcRequestPostProcessors { @Override public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) { - OidcUser oidcUser = getOidcUser(); + OidcUser oidcUser = this.oidcUser.get(); return new OAuth2LoginRequestPostProcessor(this.accessToken) .oauth2User(oidcUser) .clientRegistration(this.clientRegistration) @@ -1553,7 +1555,8 @@ public final class SecurityMockMvcRequestPostProcessors { private OidcIdToken getOidcIdToken() { if (this.idToken == null) { - return new OidcIdToken("id-token", null, null, Collections.singletonMap(IdTokenClaimNames.SUB, "test-subject")); + return new OidcIdToken("id-token", null, null, + Collections.singletonMap(IdTokenClaimNames.SUB, "test-subject")); } else { return this.idToken; } @@ -1563,12 +1566,8 @@ public final class SecurityMockMvcRequestPostProcessors { return this.userInfo; } - private OidcUser getOidcUser() { - if (this.oidcUser == null) { - return new DefaultOidcUser(getAuthorities(), getOidcIdToken(), this.userInfo); - } else { - return this.oidcUser; - } + private OidcUser defaultPrincipal() { + return new DefaultOidcUser(getAuthorities(), getOidcIdToken(), this.userInfo); } } diff --git a/test/src/test/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurersOidcLoginTests.java b/test/src/test/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurersOidcLoginTests.java index 3804cb22fb..2fbaaf1e43 100644 --- a/test/src/test/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurersOidcLoginTests.java +++ b/test/src/test/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurersOidcLoginTests.java @@ -27,6 +27,7 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; @@ -35,6 +36,7 @@ import org.springframework.security.oauth2.client.registration.ReactiveClientReg import org.springframework.security.oauth2.client.web.reactive.result.method.annotation.OAuth2AuthorizedClientArgumentResolver; import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository; import org.springframework.security.oauth2.client.web.server.WebSessionServerOAuth2AuthorizedClientRepository; +import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter; import org.springframework.test.web.reactive.server.WebTestClient; @@ -42,6 +44,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.security.oauth2.core.oidc.TestOidcIdTokens.idToken; import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockOidcLogin; import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity; @@ -143,6 +146,35 @@ public class SecurityMockServerConfigurersOidcLoginTests extends AbstractMockSer .containsEntry("email", "email@email"); } + // gh-7794 + @Test + public void oidcLoginWhenOidcUserSpecifiedThenLastCalledTakesPrecedence() throws Exception { + OidcUser oidcUser = new DefaultOidcUser( + AuthorityUtils.createAuthorityList("SCOPE_user"), idToken().build()); + + this.client.mutateWith(mockOidcLogin() + .idToken(i -> i.subject("foo")) + .oidcUser(oidcUser)) + .get().uri("/token") + .exchange() + .expectStatus().isOk(); + + OAuth2AuthenticationToken token = this.controller.token; + assertThat(token.getPrincipal().getAttributes()) + .containsEntry("sub", "subject"); + + this.client.mutateWith(mockOidcLogin() + .oidcUser(oidcUser) + .idToken(i -> i.subject("bar"))) + .get().uri("/token") + .exchange() + .expectStatus().isOk(); + + token = this.controller.token; + assertThat(token.getPrincipal().getAttributes()) + .containsEntry("sub", "bar"); + } + @RestController static class OAuth2LoginController { volatile OAuth2AuthenticationToken token; diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsOidcLoginTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsOidcLoginTests.java index 9d097a74fe..00f4cee255 100644 --- a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsOidcLoginTests.java +++ b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsOidcLoginTests.java @@ -31,12 +31,14 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizedClientRepository; import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; +import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.security.test.context.TestSecurityContextHolder; import org.springframework.test.context.ContextConfiguration; @@ -51,6 +53,7 @@ import org.springframework.web.context.WebApplicationContext; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import static org.mockito.Mockito.mock; +import static org.springframework.security.oauth2.core.oidc.TestOidcIdTokens.idToken; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.oidcLogin; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -126,6 +129,25 @@ public class SecurityMockMvcRequestPostProcessorsOidcLoginTests { .andExpect(content().string("email@email")); } + // gh-7794 + @Test + public void oidcLoginWhenOidcUserSpecifiedThenLastCalledTakesPrecedence() throws Exception { + OidcUser oidcUser = new DefaultOidcUser( + AuthorityUtils.createAuthorityList("SCOPE_user"), idToken().build()); + + this.mvc.perform(get("/id-token/sub") + .with(oidcLogin() + .idToken(i -> i.subject("foo")) + .oidcUser(oidcUser))) + .andExpect(status().isOk()) + .andExpect(content().string("subject")); + this.mvc.perform(get("/id-token/sub") + .with(oidcLogin() + .oidcUser(oidcUser) + .idToken(i -> i.subject("bar")))) + .andExpect(content().string("bar")); + } + @EnableWebSecurity @EnableWebMvc static class OAuth2LoginConfig extends WebSecurityConfigurerAdapter {