From ced92282f9da873fa42fb89f6506575334ff7efe Mon Sep 17 00:00:00 2001 From: Andre Blanke Date: Sun, 15 Dec 2024 23:42:36 +0100 Subject: [PATCH] Add builders for DefaultOAuth2User and DefaultOidcUser --- .../oidc/userinfo/OidcUserRequestUtils.java | 12 +- .../userinfo/DefaultOAuth2UserService.java | 6 +- .../DefaultReactiveOAuth2UserService.java | 6 +- .../core/oidc/user/DefaultOidcUser.java | 109 +++++++++++++----- .../core/oidc/user/OidcUserAuthority.java | 2 +- .../oauth2/core/user/DefaultOAuth2User.java | 72 +++++++++++- .../server/SecurityMockServerConfigurers.java | 13 ++- .../SecurityMockMvcRequestPostProcessors.java | 13 ++- 8 files changed, 183 insertions(+), 50 deletions(-) diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserRequestUtils.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserRequestUtils.java index f41c0cee3b6..29cfec14b37 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserRequestUtils.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserRequestUtils.java @@ -29,6 +29,7 @@ import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority; +import org.springframework.security.oauth2.core.user.DefaultOAuth2User; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; @@ -91,12 +92,15 @@ static OidcUser getUser(OidcUserRequest userRequest, OidcUserInfo userInfo) { for (String scope : token.getScopes()) { authorities.add(new SimpleGrantedAuthority("SCOPE_" + scope)); } + DefaultOidcUser.Builder userBuilder = new DefaultOidcUser.Builder(); if (StringUtils.hasText(userNameAttributeName)) { - Map attributes = OidcUserAuthority.collectClaims(userRequest.getIdToken(), userInfo); - String name = (String) attributes.get(userNameAttributeName); - return new DefaultOidcUser(name, userRequest.getIdToken(), userInfo, authorities); + userBuilder.nameAttributeKey(userNameAttributeName); } - return new DefaultOidcUser(userRequest.getIdToken(), userInfo, authorities); + return userBuilder + .idToken(userRequest.getIdToken()) + .userInfo(userInfo) + .authorities(authorities) + .build(); } private OidcUserRequestUtils() { diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultOAuth2UserService.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultOAuth2UserService.java index 5bc9595d8fd..cfda3944e24 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultOAuth2UserService.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultOAuth2UserService.java @@ -96,7 +96,11 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic OAuth2AccessToken token = userRequest.getAccessToken(); Map attributes = this.attributesConverter.convert(userRequest).convert(response.getBody()); Collection authorities = getAuthorities(token, attributes, userNameAttributeName); - return new DefaultOAuth2User(attributes.get(userNameAttributeName).toString(), attributes, authorities); + return new DefaultOAuth2User.Builder() + .nameAttributeKey(userNameAttributeName) + .attributes(attributes) + .authorities(authorities) + .build(); } /** diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserService.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserService.java index e934b34ecab..c3cf83b26cd 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserService.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserService.java @@ -138,7 +138,11 @@ public Mono loadUser(OAuth2UserRequest userRequest) throws OAuth2Aut authorities.add(new SimpleGrantedAuthority("SCOPE_" + scope)); } - return new DefaultOAuth2User(attrs.get(userNameAttributeName).toString(), attrs, authorities); + return new DefaultOAuth2User.Builder() + .nameAttributeKey(userNameAttributeName) + .attributes(attrs) + .authorities(authorities) + .build(); }) .onErrorMap((ex) -> (ex instanceof UnsupportedMediaTypeException || ex.getCause() instanceof UnsupportedMediaTypeException), (ex) -> { diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/user/DefaultOidcUser.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/user/DefaultOidcUser.java index e7ed67e59ee..b52ef2b32cd 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/user/DefaultOidcUser.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/user/DefaultOidcUser.java @@ -56,15 +56,6 @@ public DefaultOidcUser(Collection authorities, OidcI this(authorities, idToken, IdTokenClaimNames.SUB); } - /** - * Constructs a {@code DefaultOidcUser} using the provided parameters. - * @param idToken the {@link OidcIdToken ID Token} containing claims about the user - * @param authorities the authorities granted to the user - */ - public DefaultOidcUser(OidcIdToken idToken, Collection authorities) { - this(null, idToken, authorities); - } - /** * Constructs a {@code DefaultOidcUser} using the provided parameters. * @param authorities the authorities granted to the user @@ -78,16 +69,6 @@ public DefaultOidcUser(Collection authorities, OidcI this(authorities, idToken, null, nameAttributeKey); } - /** - * Constructs a {@code DefaultOidcUser} using the provided parameters. - * @param name the name of the user - * @param idToken the {@link OidcIdToken ID Token} containing claims about the user - * @param authorities the authorities granted to the user - */ - public DefaultOidcUser(String name, OidcIdToken idToken, Collection authorities) { - this(name, idToken, null, authorities); - } - /** * Constructs a {@code DefaultOidcUser} using the provided parameters. * @param authorities the authorities granted to the user @@ -101,18 +82,6 @@ public DefaultOidcUser(Collection authorities, OidcI this(authorities, idToken, userInfo, IdTokenClaimNames.SUB); } - /** - * Constructs a {@code DefaultOidcUser} using the provided parameters. - * @param authorities the authorities granted to the user - * @param idToken the {@link OidcIdToken ID Token} containing claims about the user - * @param userInfo the {@link OidcUserInfo UserInfo} containing claims about the user, - * may be {@code null} - */ - public DefaultOidcUser(OidcIdToken idToken, OidcUserInfo userInfo, - Collection authorities) { - this(null, idToken, userInfo, authorities); - } - /** * Constructs a {@code DefaultOidcUser} using the provided parameters. * @param authorities the authorities granted to the user @@ -160,4 +129,82 @@ public OidcUserInfo getUserInfo() { return this.userInfo; } + public static class Builder { + + private String name; + + private String nameAttributeKey; + + private OidcIdToken idToken; + + private OidcUserInfo userInfo; + + private Collection authorities; + + /** + * Sets the name of the user. + * @param name the name of the user + * @return the {@link Builder} + */ + public Builder name(String name) { + this.name = name; + return this; + } + + /** + * Sets the key used to access the user's "name" from the user attributes if no "name" is + * provided. + * @param nameAttributeKey the key used to access the user's "name" from the user attributes. + * @return the {@link Builder} + */ + public Builder nameAttributeKey(String nameAttributeKey) { + this.nameAttributeKey = nameAttributeKey; + return this; + } + + /** + * Sets the {@link OidcIdToken ID Token} containing claims about the user. + * @param idToken the {@link OidcIdToken ID Token} containing claims about the user. + * @return the {@link Builder} + */ + public Builder idToken(OidcIdToken idToken) { + this.idToken = idToken; + return this; + } + + /** + * Sets the {@link OidcUserInfo UserInfo} containing claims about the user. + * @param userInfo the {@link OidcUserInfo UserInfo} containing claims about the user. + * @return the {@link Builder} + */ + public Builder userInfo(OidcUserInfo userInfo) { + this.userInfo = userInfo; + return this; + } + + /** + * Sets the authorities granted to the user. + * @param authorities the authorities granted to the user + * @return the {@link Builder} + */ + public Builder authorities(Collection authorities) { + this.authorities = authorities; + return this; + } + + /** + * Builds a new {@link DefaultOidcUser}. + * @return a {@link DefaultOidcUser} + */ + public DefaultOidcUser build() { + String name = this.name; + if (name == null) { + Map attributes = OidcUserAuthority.collectClaims(this.idToken, userInfo); + name = getNameFromAttributes(attributes, this.nameAttributeKey); + } + return new DefaultOidcUser(name, idToken, userInfo, authorities); + } + + } + } diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/user/OidcUserAuthority.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/user/OidcUserAuthority.java index 7503fdf10c7..793e2127a96 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/user/OidcUserAuthority.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/user/OidcUserAuthority.java @@ -145,7 +145,7 @@ public int hashCode() { return result; } - public static Map collectClaims(OidcIdToken idToken, OidcUserInfo userInfo) { + static Map collectClaims(OidcIdToken idToken, OidcUserInfo userInfo) { Assert.notNull(idToken, "idToken cannot be null"); Map claims = new HashMap<>(); if (userInfo != null) { diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/DefaultOAuth2User.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/DefaultOAuth2User.java index 186588e842c..03618a7fa96 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/DefaultOAuth2User.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/DefaultOAuth2User.java @@ -35,11 +35,6 @@ /** * The default implementation of an {@link OAuth2User}. * - *

- * User attribute names are not standardized between providers, and therefore it is - * required to supply the user's "name" or "name" attribute to one of - * the constructors. - * * @author Joe Grandja * @author EddĂș MelĂ©ndez * @author Park Hyojong @@ -146,11 +141,76 @@ public String toString() { return sb.toString(); } - private static String getNameFromAttributes(Map attributes, String nameAttributeKey) { + protected static String getNameFromAttributes(Map attributes, String nameAttributeKey) { Assert.hasText(nameAttributeKey, "nameAttributeKey cannot be empty"); Assert.notNull(attributes.get(nameAttributeKey), "Attribute value for '" + nameAttributeKey + "' cannot be null"); return attributes.get(nameAttributeKey).toString(); } + /** + * A builder for {@link DefaultOAuth2User}. + */ + public static class Builder { + + private String name; + + private String nameAttributeKey; + + private Map attributes; + + private Collection authorities; + + /** + * Sets the name of the user. + * @param name the name of the user + * @return the {@link Builder} + */ + public Builder name(String name) { + this.name = name; + return this; + } + + /** + * Sets the key used to access the user's "name" from the user attributes if no "name" is + * provided. + * @param nameAttributeKey the key used to access the user's "name" from the user attributes. + * @return the {@link Builder} + */ + public Builder nameAttributeKey(String nameAttributeKey) { + this.nameAttributeKey = nameAttributeKey; + return this; + } + + /** + * Sets the attributes about the user. + * @param attributes the attributes about the user + * @return the {@link Builder} + */ + public Builder attributes(Map attributes) { + this.attributes = attributes; + return this; + } + + /** + * Sets the authorities granted to the user. + * @param authorities the authorities granted to the user + * @return the {@link Builder} + */ + public Builder authorities(Collection authorities) { + this.authorities = authorities; + return this; + } + + /** + * Builds a new {@link DefaultOAuth2User}. + * @return a {@link DefaultOAuth2User} + */ + public DefaultOAuth2User build() { + String name = this.name != null ? this.name : getNameFromAttributes(this.attributes, this.nameAttributeKey); + return new DefaultOAuth2User(name, this.attributes, this.authorities); + } + + } + } 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 41fd86fea2c..430e6c71844 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 @@ -848,8 +848,11 @@ private Map defaultAttributes() { } private OAuth2User defaultPrincipal() { - String name = this.attributes.get().get(this.nameAttributeKey).toString(); - return new DefaultOAuth2User(name, this.attributes.get(), this.authorities.get()); + return new DefaultOAuth2User.Builder() + .nameAttributeKey(this.nameAttributeKey) + .attributes(this.attributes.get()) + .authorities(this.authorities.get()) + .build(); } } @@ -1024,7 +1027,11 @@ private OidcUserInfo getOidcUserInfo() { } private OidcUser defaultPrincipal() { - return new DefaultOidcUser(getOidcIdToken(), this.userInfo, getAuthorities()); + return new DefaultOidcUser.Builder() + .idToken(getOidcIdToken()) + .userInfo(this.userInfo) + .authorities(getAuthorities()) + .build(); } } 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 247a6ed6d28..7e6631443e7 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 @@ -1390,8 +1390,11 @@ private Map defaultAttributes() { } private OAuth2User defaultPrincipal() { - String name = this.attributes.get().get(this.nameAttributeKey).toString(); - return new DefaultOAuth2User(name, this.attributes.get(), this.authorities.get()); + return new DefaultOAuth2User.Builder() + .nameAttributeKey(this.nameAttributeKey) + .attributes(this.attributes.get()) + .authorities(this.authorities.get()) + .build(); } } @@ -1534,7 +1537,11 @@ private OidcUserInfo getOidcUserInfo() { } private OidcUser defaultPrincipal() { - return new DefaultOidcUser(getOidcIdToken(), this.userInfo, getAuthorities()); + return new DefaultOidcUser.Builder() + .idToken(getOidcIdToken()) + .userInfo(this.userInfo) + .authorities(getAuthorities()) + .build(); } }