v2.1.x; added APS API support
This commit is contained in:
3
pom.xml
3
pom.xml
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<groupId>com.inteligr8.activiti</groupId>
|
<groupId>com.inteligr8.activiti</groupId>
|
||||||
<artifactId>auth-activiti-app-ext</artifactId>
|
<artifactId>auth-activiti-app-ext</artifactId>
|
||||||
<version>2.0-SNAPSHOT</version>
|
<version>2.1-SNAPSHOT</version>
|
||||||
|
|
||||||
<name>Authentication & Authorization for APS</name>
|
<name>Authentication & Authorization for APS</name>
|
||||||
<description>An Alfresco Process Service App extension providing improved authentication and authorization support.</description>
|
<description>An Alfresco Process Service App extension providing improved authentication and authorization support.</description>
|
||||||
@@ -45,6 +45,7 @@
|
|||||||
|
|
||||||
<!-- for RAD -->
|
<!-- for RAD -->
|
||||||
<tomcat-rad.version>10-2.2</tomcat-rad.version>
|
<tomcat-rad.version>10-2.2</tomcat-rad.version>
|
||||||
|
<aps.hotswap.enabled>false</aps.hotswap.enabled>
|
||||||
<aps.tomcat.opts.base>-Dspring.main.allow-circular-references=true \
|
<aps.tomcat.opts.base>-Dspring.main.allow-circular-references=true \
|
||||||
-Dhibernate.dialect=org.hibernate.dialect.PostgreSQLDialect \
|
-Dhibernate.dialect=org.hibernate.dialect.PostgreSQLDialect \
|
||||||
-Dauth-ext.external.id=keycloak \
|
-Dauth-ext.external.id=keycloak \
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
package com.inteligr8.activiti.auth.oauth;
|
package com.inteligr8.activiti.auth.oauth;
|
||||||
|
|
||||||
|
import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -10,11 +12,16 @@ import org.springframework.context.annotation.Bean;
|
|||||||
import org.springframework.context.annotation.Conditional;
|
import org.springframework.context.annotation.Conditional;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
|
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
|
||||||
|
import com.activiti.security.ProtectedPaths;
|
||||||
import com.activiti.security.identity.service.config.IdentityServiceEnabledCondition;
|
import com.activiti.security.identity.service.config.IdentityServiceEnabledCondition;
|
||||||
|
import com.inteligr8.activiti.auth.service.JwtAuthenticationProvider;
|
||||||
import com.nimbusds.oauth2.sdk.ParseException;
|
import com.nimbusds.oauth2.sdk.ParseException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,6 +40,9 @@ public class IdentityServiceConfigurationOverride {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private ApplicationContext appContext;
|
private ApplicationContext appContext;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JwtAuthenticationProvider jwtAuthenticationProvider;
|
||||||
|
|
||||||
@Bean("inteligr8.clientRegistrationRepository")
|
@Bean("inteligr8.clientRegistrationRepository")
|
||||||
@Primary
|
@Primary
|
||||||
public ClientRegistrationRepository clientRegistrationRepository() {
|
public ClientRegistrationRepository clientRegistrationRepository() {
|
||||||
@@ -51,7 +61,7 @@ public class IdentityServiceConfigurationOverride {
|
|||||||
|
|
||||||
@Bean(OVERRIDE_CLIENT_REGISTRATION_BEANNAME)
|
@Bean(OVERRIDE_CLIENT_REGISTRATION_BEANNAME)
|
||||||
@Primary
|
@Primary
|
||||||
public ClientRegistration clientRegistration1() throws ParseException, InterruptedException {
|
public ClientRegistration clientRegistration() throws ParseException, InterruptedException {
|
||||||
this.logger.trace("clientRegistration()");
|
this.logger.trace("clientRegistration()");
|
||||||
ClientRegistration clientRegistration = this.appContext.getBean(OOTB_CLIENT_REGISTRATION_BEANNAME, ClientRegistration.class);
|
ClientRegistration clientRegistration = this.appContext.getBean(OOTB_CLIENT_REGISTRATION_BEANNAME, ClientRegistration.class);
|
||||||
|
|
||||||
@@ -62,4 +72,27 @@ public class IdentityServiceConfigurationOverride {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slightly lower priority than the one provided OOTB. This
|
||||||
|
* allows for the bean injection of the JwtAuthenticationConverter.
|
||||||
|
*
|
||||||
|
* A lower priority means it is applied last. This means it replaces the
|
||||||
|
* JwtAuthenticationConverter provided by Alfresco OOTB.
|
||||||
|
*
|
||||||
|
* @see com.activiti.security.identity.service.config.IdentityServiceConfigurationApi#identityServiceApiWebSecurity
|
||||||
|
*/
|
||||||
|
@Bean("inteligr8.identityServiceApiWebSecurity")
|
||||||
|
@Order(-5)
|
||||||
|
public SecurityFilterChain identityServiceApiWebSecurity(HttpSecurity http) throws Exception {
|
||||||
|
http
|
||||||
|
.securityMatcher(antMatcher(ProtectedPaths.API_URL_PATH + "/**"))
|
||||||
|
.oauth2ResourceServer(oauth2 ->
|
||||||
|
oauth2.jwt(jwtConfigurer -> {
|
||||||
|
jwtConfigurer.jwtAuthenticationConverter(this.jwtAuthenticationProvider.create());
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -30,7 +30,10 @@ import org.springframework.beans.factory.annotation.Value;
|
|||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.data.util.Pair;
|
import org.springframework.data.util.Pair;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.security.oauth2.core.ClaimAccessor;
|
||||||
|
import org.springframework.security.oauth2.core.oidc.StandardClaimNames;
|
||||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.activiti.domain.idm.Group;
|
import com.activiti.domain.idm.Group;
|
||||||
@@ -129,28 +132,36 @@ public class GroupSyncService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void sync(OidcUser oidcUser) {
|
public void sync(OidcUser oidcUser) {
|
||||||
if (!oidcUser.hasClaim("groups")) {
|
this.sync(oidcUser.getEmail(), oidcUser);
|
||||||
this.logger.warn("There is no 'groups' claim to synchronize: {}", oidcUser.getEmail());
|
}
|
||||||
this.logger.debug("The claims available: {}", oidcUser.getClaims().keySet());
|
|
||||||
|
public void sync(Jwt jwt) {
|
||||||
|
this.sync(jwt.getClaim(StandardClaimNames.EMAIL), jwt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sync(String email, ClaimAccessor claims) {
|
||||||
|
if (!claims.hasClaim("groups")) {
|
||||||
|
this.logger.warn("There is no 'groups' claim to synchronize: {}", email);
|
||||||
|
this.logger.debug("The claims available: {}", claims.getClaims().keySet());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<String> oidcGroups = new HashSet<>(oidcUser.getClaimAsStringList("groups"));
|
Set<String> oidcGroups = new HashSet<>(claims.getClaimAsStringList("groups"));
|
||||||
this.logger.trace("Incoming OIDC groups: {}: {}", oidcUser.getEmail(), oidcGroups);
|
this.logger.trace("Incoming OIDC groups: {}: {}", email, oidcGroups);
|
||||||
|
|
||||||
oidcGroups = this.filterGroups(oidcGroups);
|
oidcGroups = this.filterGroups(oidcGroups);
|
||||||
Set<String> translatedGroups = this.translateGroups(oidcGroups);
|
Set<String> translatedGroups = this.translateGroups(oidcGroups);
|
||||||
|
|
||||||
this.logger.debug("Filtered/translated OIDC groups: {}: {}", oidcUser.getEmail(), translatedGroups);
|
this.logger.debug("Filtered/translated OIDC groups: {}: {}", email, translatedGroups);
|
||||||
|
|
||||||
long tenantId = this.tenantFinderService.findTenantId();
|
long tenantId = this.tenantFinderService.findTenantId();
|
||||||
|
|
||||||
// check Activiti groups
|
// check Activiti groups
|
||||||
User user = this.userService.findUserByEmailAndTenantId(oidcUser.getEmail(), tenantId);
|
User user = this.userService.findUserByEmailAndTenantId(email, tenantId);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
user = this.userService.findUserByEmail(oidcUser.getEmail());
|
user = this.userService.findUserByEmail(email);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
throw new UsernameNotFoundException("The user could not be found: " + oidcUser.getEmail());
|
throw new UsernameNotFoundException("The user could not be found: " + email);
|
||||||
}
|
}
|
||||||
User userWithGroups = this.userService.getUser(user.getId(), true);
|
User userWithGroups = this.userService.getUser(user.getId(), true);
|
||||||
this.logger.debug("Discovered user belongs to {} APS groups: {}", userWithGroups.getGroups().size(), user.getExternalId());
|
this.logger.debug("Discovered user belongs to {} APS groups: {}", userWithGroups.getGroups().size(), user.getExternalId());
|
||||||
|
@@ -0,0 +1,11 @@
|
|||||||
|
package com.inteligr8.activiti.auth.service;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
|
||||||
|
public interface JwtAuthenticationProvider {
|
||||||
|
|
||||||
|
Converter<Jwt, AbstractAuthenticationToken> create();
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,50 @@
|
|||||||
|
package com.inteligr8.activiti.auth.service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
|
||||||
|
import com.activiti.security.identity.service.config.JwtAuthenticationToken;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
|
||||||
|
public class SyncingJwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
private final UserDetailsService userDetailsService;
|
||||||
|
private final UserSyncService userSyncService;
|
||||||
|
private final GroupSyncService groupSyncService;
|
||||||
|
|
||||||
|
public SyncingJwtAuthenticationConverter(UserDetailsService userDetailsService, UserSyncService userSyncService, GroupSyncService groupSyncService) {
|
||||||
|
this.userDetailsService = userDetailsService;
|
||||||
|
this.userSyncService = userSyncService;
|
||||||
|
this.groupSyncService = groupSyncService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractAuthenticationToken convert(Jwt source) {
|
||||||
|
this.logger.trace("convert({}, {})", source.getId(), source.getClaim("email"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.logger.debug("jwt: {}", new ObjectMapper().registerModule(new JavaTimeModule()).writeValueAsString(source));
|
||||||
|
} catch (JsonProcessingException jpe) {
|
||||||
|
this.logger.error("error", jpe);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.userSyncService.sync(source);
|
||||||
|
this.groupSyncService.sync(source);
|
||||||
|
|
||||||
|
UserDetails springUser = this.userDetailsService.loadUserByUsername(source.getClaim("email"));
|
||||||
|
return new JwtAuthenticationToken(
|
||||||
|
springUser,
|
||||||
|
new ArrayList<>(springUser.getAuthorities()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,27 @@
|
|||||||
|
package com.inteligr8.activiti.auth.service;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SyncingJwtAuthenticationProvider implements JwtAuthenticationProvider {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserSyncService userSyncService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private GroupSyncService groupSyncService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Converter<Jwt, AbstractAuthenticationToken> create() {
|
||||||
|
return new SyncingJwtAuthenticationConverter(this.userDetailsService, this.userSyncService, this.groupSyncService);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -20,12 +20,16 @@ import com.activiti.security.identity.service.config.IdentityServiceKeycloakProp
|
|||||||
* Activiti Identity Service configuration is enabled. When it isn't
|
* Activiti Identity Service configuration is enabled. When it isn't
|
||||||
* enabled, it will still serve as the default OIDC user service for
|
* enabled, it will still serve as the default OIDC user service for
|
||||||
* Spring Security.
|
* Spring Security.
|
||||||
|
*
|
||||||
|
* This is only executed with non-API authentication and authorization use
|
||||||
|
* cases. API authentication/authorization uses the
|
||||||
|
* `SyncingJwtAuthenitcationConverter`.
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
@Primary
|
@Primary
|
||||||
public class OIDCUserService extends OidcUserService {
|
public class SyncingUserService extends OidcUserService {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(OIDCUserService.class);
|
private final Logger logger = LoggerFactory.getLogger(SyncingUserService.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserDetailsService userDetailsService;
|
private UserDetailsService userDetailsService;
|
@@ -9,7 +9,10 @@ import org.springframework.beans.factory.annotation.Value;
|
|||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.security.oauth2.core.ClaimAccessor;
|
||||||
|
import org.springframework.security.oauth2.core.oidc.StandardClaimNames;
|
||||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.activiti.domain.idm.Group;
|
import com.activiti.domain.idm.Group;
|
||||||
@@ -49,7 +52,7 @@ public class UserSyncService {
|
|||||||
protected boolean clearNewUserGroups;
|
protected boolean clearNewUserGroups;
|
||||||
|
|
||||||
public void sync(OidcUser oidcUser) {
|
public void sync(OidcUser oidcUser) {
|
||||||
UserDetails springUser = this.loadSpringUser(oidcUser);
|
UserDetails springUser = this.loadSpringUser(oidcUser.getEmail(), oidcUser.getGivenName(), oidcUser.getFamilyName(), oidcUser);
|
||||||
if (this.logger.isTraceEnabled()) {
|
if (this.logger.isTraceEnabled()) {
|
||||||
this.logger.trace("Loaded Spring Security user: {}: {}", springUser.getUsername(), springUser.getAuthorities());
|
this.logger.trace("Loaded Spring Security user: {}: {}", springUser.getUsername(), springUser.getAuthorities());
|
||||||
} else {
|
} else {
|
||||||
@@ -57,42 +60,58 @@ public class UserSyncService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserDetails loadSpringUser(OidcUser oidcUser) throws UsernameNotFoundException {
|
public void sync(Jwt jwt) {
|
||||||
|
String email = jwt.getClaim(StandardClaimNames.EMAIL);
|
||||||
|
if (email == null)
|
||||||
|
throw new IllegalArgumentException("An '" + StandardClaimNames.EMAIL + "' claim is required");
|
||||||
|
|
||||||
|
String givenName = jwt.getClaim(StandardClaimNames.GIVEN_NAME);
|
||||||
|
String familyName = jwt.getClaim(StandardClaimNames.FAMILY_NAME);
|
||||||
|
|
||||||
|
UserDetails springUser = this.loadSpringUser(email, givenName, familyName, jwt);
|
||||||
|
if (this.logger.isTraceEnabled()) {
|
||||||
|
this.logger.trace("Loaded Spring Security user: {}: {}", springUser.getUsername(), springUser.getAuthorities());
|
||||||
|
} else {
|
||||||
|
this.logger.debug("Loaded Spring Security user: {}", springUser.getUsername());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserDetails loadSpringUser(String email, String givenName, String familyName, ClaimAccessor claims) throws UsernameNotFoundException {
|
||||||
try {
|
try {
|
||||||
UserDetails springUser = this.userDetailsService.loadUserByUsername(oidcUser.getEmail());
|
UserDetails springUser = this.userDetailsService.loadUserByUsername(email);
|
||||||
this.logger.debug("Loaded APS user: {} => {}", oidcUser.getEmail(), springUser.getUsername());
|
this.logger.debug("Loaded APS user: {} => {}", email, springUser.getUsername());
|
||||||
return springUser;
|
return springUser;
|
||||||
} catch (UsernameNotFoundException unfe) {
|
} catch (UsernameNotFoundException unfe) {
|
||||||
this.logger.debug("User does not exist: {}", unfe.getMessage());
|
this.logger.debug("User does not exist: {}", unfe.getMessage());
|
||||||
if (!this.createMissingUser)
|
if (!this.createMissingUser)
|
||||||
throw unfe;
|
throw unfe;
|
||||||
|
|
||||||
if (this.requiredGroup != null && (!oidcUser.hasClaim("groups") || !oidcUser.getClaimAsStringList("groups").contains(this.requiredGroup))) {
|
if (this.requiredGroup != null && (!claims.hasClaim("groups") || !claims.getClaimAsStringList("groups").contains(this.requiredGroup))) {
|
||||||
this.logger.info("User does not exist and does not have the required OIDC group to be created: {} ", oidcUser.getEmail(), this.requiredGroup);
|
this.logger.info("User does not exist and does not have the required OIDC group to be created: {} ", email, this.requiredGroup);
|
||||||
throw unfe;
|
throw unfe;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug("User does not exist; will attempt to create: {}", oidcUser.getEmail());
|
this.logger.debug("User does not exist; will attempt to create: {}", email);
|
||||||
User apsUser = this.createApsUser(oidcUser);
|
User apsUser = this.createApsUser(email, givenName, familyName);
|
||||||
if (this.clearNewUserGroups) {
|
if (this.clearNewUserGroups) {
|
||||||
apsUser = this.userService.getUser(apsUser.getId(), true);
|
apsUser = this.userService.getUser(apsUser.getId(), true);
|
||||||
if (this.logger.isDebugEnabled())
|
if (this.logger.isDebugEnabled())
|
||||||
this.logger.debug("User is new; clearing default groups: {}: {}", oidcUser.getEmail(), apsUser.getGroups().stream().map(group -> group.getName()).toList());
|
this.logger.debug("User is new; clearing default groups: {}: {}", email, apsUser.getGroups().stream().map(group -> group.getName()).toList());
|
||||||
this.deleteApsUserGroups(apsUser);
|
this.deleteApsUserGroups(apsUser);
|
||||||
}
|
}
|
||||||
return this.userDetailsService.loadByUserId(apsUser.getId());
|
return this.userDetailsService.loadByUserId(apsUser.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private User createApsUser(OidcUser oidcUser) {
|
private User createApsUser(String email, String givenName, String familyName) {
|
||||||
long tenantId = this.tenantFinderService.findTenantId();
|
long tenantId = this.tenantFinderService.findTenantId();
|
||||||
|
|
||||||
User user = this.userService.createNewUserFromExternalStore(
|
User user = this.userService.createNewUserFromExternalStore(
|
||||||
oidcUser.getEmail(),
|
email,
|
||||||
oidcUser.getGivenName(),
|
givenName,
|
||||||
oidcUser.getFamilyName(),
|
familyName,
|
||||||
tenantId,
|
tenantId,
|
||||||
oidcUser.getEmail(),
|
email,
|
||||||
this.externalIdmSource,
|
this.externalIdmSource,
|
||||||
new Date());
|
new Date());
|
||||||
this.logger.info("Created user: {} => {}", user.getId(), user.getEmail());
|
this.logger.info("Created user: {} => {}", user.getId(), user.getEmail());
|
||||||
|
Reference in New Issue
Block a user