diff --git a/generators/server/__snapshots__/generator.spec.js.snap b/generators/server/__snapshots__/generator.spec.js.snap index fcc9043b521a..e03f1d726dc7 100644 --- a/generators/server/__snapshots__/generator.spec.js.snap +++ b/generators/server/__snapshots__/generator.spec.js.snap @@ -44,6 +44,9 @@ exports[`generator - server composing databaseType option no with jwt should mat "src/main/java/com/mycompany/myapp/config/CRLFLogConverter.java": { "stateCleared": "modified", }, + "src/main/java/com/mycompany/myapp/config/Constants.java": { + "stateCleared": "modified", + }, "src/main/java/com/mycompany/myapp/config/DateTimeFormatConfiguration.java": { "stateCleared": "modified", }, @@ -513,6 +516,9 @@ exports[`generator - server composing databaseType option no with session should "src/main/java/com/mycompany/myapp/config/CRLFLogConverter.java": { "stateCleared": "modified", }, + "src/main/java/com/mycompany/myapp/config/Constants.java": { + "stateCleared": "modified", + }, "src/main/java/com/mycompany/myapp/config/DateTimeFormatConfiguration.java": { "stateCleared": "modified", }, diff --git a/generators/server/templates/src/main/java/_package_/config/Constants.java.ejs b/generators/server/templates/src/main/java/_package_/config/Constants.java.ejs index 4a95d2bddd1e..a35b2a6041ed 100644 --- a/generators/server/templates/src/main/java/_package_/config/Constants.java.ejs +++ b/generators/server/templates/src/main/java/_package_/config/Constants.java.ejs @@ -31,9 +31,7 @@ public final class Constants { <%_ if (generateBuiltInUserEntity || databaseTypeSql || databaseTypeMongodb || databaseTypeCouchbase) { _%> public static final String SYSTEM = "system"; <%_ } _%> -<%_ if (generateUserManagement || authenticationTypeOauth2) { _%> public static final String DEFAULT_LANGUAGE = "<%= nativeLanguage %>"; -<%_ } _%> <%_ if (databaseTypeCouchbase) { _%> public static final String ID_DELIMITER = "__"; <%_ } _%> diff --git a/generators/server/templates/src/main/java/_package_/security/SecurityUtils.java.ejs b/generators/server/templates/src/main/java/_package_/security/SecurityUtils.java.ejs index ec18b2503949..56b5d79ca71e 100644 --- a/generators/server/templates/src/main/java/_package_/security/SecurityUtils.java.ejs +++ b/generators/server/templates/src/main/java/_package_/security/SecurityUtils.java.ejs @@ -17,6 +17,10 @@ limitations under the License. -%> package <%= packageName %>.security; +<%_ if (generateAuthenticationApi && (authenticationTypeOauth2 || authenticationTypeJwt)) { _%> + +import <%= packageName %>.config.Constants; +<%_ } _%> import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; @@ -35,23 +39,32 @@ import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; -import java.util.*; -import java.util.stream.Collectors; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; <%_ if (reactive) { _%> import reactor.core.publisher.Mono; <%_ } else { _%> import java.util.stream.Stream; <%_ } _%> <%_ } else { _%> +import java.util.Arrays; <%_ if (reactive) { _%> import reactor.core.publisher.Mono; -import java.util.Arrays; <%_ } else { _%> -import java.util.Arrays; -import java.util.Optional; import java.util.stream.Stream; <%_ } _%> <%_ } _%> +<%_ if (authenticationTypeOauth2 || authenticationTypeJwt || !reactive) { _%> +import java.util.Optional; +<%_ } _%> +<%_ if (authenticationTypeOauth2 || authenticationTypeJwt) { _%> +import org.springframework.security.oauth2.core.oidc.StandardClaimNames; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; +<%_ } _%> /** * Utility class for Spring Security. @@ -230,4 +243,51 @@ public final class SecurityUtils { .collect(Collectors.toList()); } <%_ } _%> +<%_ if (generateAuthenticationApi && ((authenticationTypeOauth2 && !syncUserWithIdp) || (authenticationTypeJwt && skipUserManagement))) { _%> + + public static Map extractDetailsFromTokenAttributes(Map attributes) { + Map details = new HashMap<>(); + + details.put("activated", Optional.ofNullable(attributes.get(StandardClaimNames.EMAIL_VERIFIED)).orElse(true)); + Optional.ofNullable(attributes.get("uid")).ifPresent(id -> details.put("id", id)); + Optional.ofNullable(attributes.get(StandardClaimNames.FAMILY_NAME)).ifPresent(lastName -> details.put("lastName", lastName)); + Optional.ofNullable(attributes.get(StandardClaimNames.PICTURE)).ifPresent(imageUrl -> details.put("imageUrl", imageUrl)); + + Optional.ofNullable(attributes.get(StandardClaimNames.GIVEN_NAME)).ifPresentOrElse( + firstName -> details.put("firstName", firstName), + () -> Optional.ofNullable(attributes.get(StandardClaimNames.NAME)).ifPresent(firstName -> details.put("firstName", firstName)) + ); + + if (attributes.get(StandardClaimNames.EMAIL) != null) { + details.put("email", attributes.get(StandardClaimNames.EMAIL)); + } else { + String sub = String.valueOf(attributes.get(StandardClaimNames.SUB)); + String preferredUsername = (String) attributes.get(StandardClaimNames.PREFERRED_USERNAME); + if (sub.contains("|") && (preferredUsername != null && preferredUsername.contains("@"))) { + // special handling for Auth0 + details.put("email", preferredUsername); + } else { + details.put("email", sub); + } + } + + if (attributes.get("langKey") != null) { + details.put("langKey", attributes.get("langKey")); + } else if (attributes.get(StandardClaimNames.LOCALE) != null) { + // trim off country code if it exists + String locale = (String) attributes.get(StandardClaimNames.LOCALE); + if (locale.contains("_")) { + locale = locale.substring(0, locale.indexOf('_')); + } else if (locale.contains("-")) { + locale = locale.substring(0, locale.indexOf('-')); + } + details.put("langKey", locale.toLowerCase()); + } else { + // set langKey to default if not specified by IdP + details.put("langKey", Constants.DEFAULT_LANGUAGE); + } + + return details; + } +<%_ } _%> } diff --git a/generators/server/templates/src/main/java/_package_/web/rest/AccountResource_skipUserManagement.java.ejs b/generators/server/templates/src/main/java/_package_/web/rest/AccountResource_skipUserManagement.java.ejs index e311084b1953..405800d93885 100644 --- a/generators/server/templates/src/main/java/_package_/web/rest/AccountResource_skipUserManagement.java.ejs +++ b/generators/server/templates/src/main/java/_package_/web/rest/AccountResource_skipUserManagement.java.ejs @@ -18,6 +18,11 @@ -%> package <%= packageName %>.web.rest; +import <%= packageName %>.security.SecurityUtils; +<%_ if (generateBuiltInAuthorityEntity) { _%> +import <%= packageName %>.domain.Authority; + +<%_ } _%> <%_ if (reactive) { _%> import reactor.core.publisher.Mono; <%_ } else { _%> @@ -47,7 +52,7 @@ import org.springframework.security.core.context.SecurityContextHolder; <%_ } _%> <%_ } _%> -import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonAnyGetter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.GrantedAuthority; @@ -55,6 +60,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -157,12 +163,21 @@ public class AccountResource { private static class UserVM { private String login; +<%_ if (generateBuiltInAuthorityEntity) { _%> + private Set authorities; +<%_ } else { _%> private Set authorities; +<%_ } _%> +<%_ if (authenticationTypeOauth2 || authenticationTypeJwt) { _%> + private Map details; +<%_ } _%> - @JsonCreator - UserVM(String login, Set authorities) { + UserVM(String login, Set authorities<% if (authenticationTypeOauth2 || authenticationTypeJwt) { %>, Map details<% } %>) { this.login = login; this.authorities = authorities; +<%_ if (authenticationTypeOauth2 || authenticationTypeJwt) { _%> + this.details = details; +<%_ } _%> } public boolean isActivated() { @@ -176,22 +191,41 @@ public class AccountResource { public String getLogin() { return login; } +<%_ if (authenticationTypeOauth2 || authenticationTypeJwt) { _%> + + @JsonAnyGetter + public Map getDetails() { + return details; + } +<%_ } _%> } <%_ if (authenticationTypeOauth2 || authenticationTypeJwt) { _%> - private UserVM getUserFromAuthentication(AbstractAuthenticationToken authToken) { - if ( + private static UserVM getUserFromAuthentication(AbstractAuthenticationToken authToken) { + Map attributes; + if (authToken instanceof JwtAuthenticationToken) { + attributes = ((JwtAuthenticationToken) authToken).getTokenAttributes(); <%_ if (authenticationTypeOauth2) { _%> - !(authToken instanceof OAuth2AuthenticationToken) && + } else if (authToken instanceof OAuth2AuthenticationToken) { + attributes = ((OAuth2AuthenticationToken) authToken).getPrincipal().getAttributes(); <%_ } _%> - !(authToken instanceof JwtAuthenticationToken) - ) { + } else { throw new IllegalArgumentException("AuthenticationToken is not OAuth2 or JWT!"); } return new UserVM( authToken.getName(), - authToken.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toSet()) + authToken.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + <%_ if (generateBuiltInAuthorityEntity) { _%> + .map(authority -> { + Authority auth = new Authority(); + auth.setName(authority); + return auth; + }) + <%_ } _%> + .collect(Collectors.toSet()), + SecurityUtils.extractDetailsFromTokenAttributes(attributes) ); } <%_ } _%> diff --git a/generators/spring-boot/files.ts b/generators/spring-boot/files.ts index 3cc15793e569..ff72674215b5 100644 --- a/generators/spring-boot/files.ts +++ b/generators/spring-boot/files.ts @@ -413,12 +413,6 @@ export const baseServerFiles = { ], }, { - condition: generator => - generator.generateUserManagement || - generator.authenticationTypeOauth2 || - generator.databaseTypeSql || - generator.databaseTypeMongodb || - generator.databaseTypeCouchbase, path: `${SERVER_MAIN_SRC_DIR}_package_/`, renameTo: moveToJavaPackageSrcDir, templates: ['config/Constants.java'], diff --git a/generators/spring-data-cassandra/__snapshots__/generator.spec.ts.snap b/generators/spring-data-cassandra/__snapshots__/generator.spec.ts.snap index 6f58a0ac64eb..4447fad2e902 100644 --- a/generators/spring-data-cassandra/__snapshots__/generator.spec.ts.snap +++ b/generators/spring-data-cassandra/__snapshots__/generator.spec.ts.snap @@ -520,6 +520,9 @@ exports[`generator - cassandra microservice-jwt-reactive(false)-maven-enableTran "src/main/java/tech/jhipster/config/CRLFLogConverter.java": { "stateCleared": "modified", }, + "src/main/java/tech/jhipster/config/Constants.java": { + "stateCleared": "modified", + }, "src/main/java/tech/jhipster/config/DatabaseConfiguration.java": { "stateCleared": "modified", }, @@ -858,6 +861,9 @@ exports[`generator - cassandra microservice-jwt-reactive(true)-gradle-enableTran "src/main/java/com/mycompany/config/CRLFLogConverter.java": { "stateCleared": "modified", }, + "src/main/java/com/mycompany/config/Constants.java": { + "stateCleared": "modified", + }, "src/main/java/com/mycompany/config/DatabaseConfiguration.java": { "stateCleared": "modified", }, @@ -2016,6 +2022,9 @@ exports[`generator - cassandra monolith-jwt-reactive(true)-gradle-enableTranslat "src/main/java/com/mycompany/config/CRLFLogConverter.java": { "stateCleared": "modified", }, + "src/main/java/com/mycompany/config/Constants.java": { + "stateCleared": "modified", + }, "src/main/java/com/mycompany/config/DatabaseConfiguration.java": { "stateCleared": "modified", }, @@ -3545,6 +3554,9 @@ exports[`generator - cassandra monolith-session-reactive(true)-gradle-enableTran "src/main/java/com/mycompany/config/CRLFLogConverter.java": { "stateCleared": "modified", }, + "src/main/java/com/mycompany/config/Constants.java": { + "stateCleared": "modified", + }, "src/main/java/com/mycompany/config/DatabaseConfiguration.java": { "stateCleared": "modified", }, diff --git a/generators/spring-data-elasticsearch/__snapshots__/generator.spec.ts.snap b/generators/spring-data-elasticsearch/__snapshots__/generator.spec.ts.snap index 193b5a32c18e..d84885ad3a16 100644 --- a/generators/spring-data-elasticsearch/__snapshots__/generator.spec.ts.snap +++ b/generators/spring-data-elasticsearch/__snapshots__/generator.spec.ts.snap @@ -969,6 +969,9 @@ exports[`generator - elasticsearch microservice-jwt-reactive(true)-gradle-enable "src/main/java/com/mycompany/config/CRLFLogConverter.java": { "stateCleared": "modified", }, + "src/main/java/com/mycompany/config/Constants.java": { + "stateCleared": "modified", + }, "src/main/java/com/mycompany/config/DatabaseConfiguration.java": { "stateCleared": "modified", }, @@ -4088,6 +4091,9 @@ exports[`generator - elasticsearch monolith-session-reactive(true)-gradle-enable "src/main/java/com/mycompany/config/CRLFLogConverter.java": { "stateCleared": "modified", }, + "src/main/java/com/mycompany/config/Constants.java": { + "stateCleared": "modified", + }, "src/main/java/com/mycompany/config/DatabaseConfiguration.java": { "stateCleared": "modified", }, diff --git a/generators/spring-data-neo4j/__snapshots__/generator.spec.ts.snap b/generators/spring-data-neo4j/__snapshots__/generator.spec.ts.snap index c97028f1907e..c0dd3464b836 100644 --- a/generators/spring-data-neo4j/__snapshots__/generator.spec.ts.snap +++ b/generators/spring-data-neo4j/__snapshots__/generator.spec.ts.snap @@ -532,6 +532,9 @@ exports[`generator - neo4j microservice-jwt-reactive(false)-maven-enableTranslat "src/main/java/tech/jhipster/config/CRLFLogConverter.java": { "stateCleared": "modified", }, + "src/main/java/tech/jhipster/config/Constants.java": { + "stateCleared": "modified", + }, "src/main/java/tech/jhipster/config/DatabaseConfiguration.java": { "stateCleared": "modified", }, @@ -855,6 +858,9 @@ exports[`generator - neo4j microservice-jwt-reactive(true)-gradle-enableTranslat "src/main/java/com/mycompany/config/CRLFLogConverter.java": { "stateCleared": "modified", }, + "src/main/java/com/mycompany/config/Constants.java": { + "stateCleared": "modified", + }, "src/main/java/com/mycompany/config/DatabaseConfiguration.java": { "stateCleared": "modified", }, @@ -1995,6 +2001,9 @@ exports[`generator - neo4j monolith-jwt-reactive(true)-gradle-enableTranslation( "src/main/java/com/mycompany/config/CRLFLogConverter.java": { "stateCleared": "modified", }, + "src/main/java/com/mycompany/config/Constants.java": { + "stateCleared": "modified", + }, "src/main/java/com/mycompany/config/DatabaseConfiguration.java": { "stateCleared": "modified", }, @@ -3491,6 +3500,9 @@ exports[`generator - neo4j monolith-session-reactive(true)-gradle-enableTranslat "src/main/java/com/mycompany/config/CRLFLogConverter.java": { "stateCleared": "modified", }, + "src/main/java/com/mycompany/config/Constants.java": { + "stateCleared": "modified", + }, "src/main/java/com/mycompany/config/DatabaseConfiguration.java": { "stateCleared": "modified", },