diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/UserService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/UserService.java index ef5fc22e0..2891cabf0 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/UserService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/UserService.java @@ -31,6 +31,7 @@ import org.jboss.resteasy.client.jaxrs.internal.ResteasyClientBuilderImpl; import org.keycloak.OAuth2Constants; import org.keycloak.admin.client.KeycloakBuilder; import org.keycloak.admin.client.resource.UserResource; +import org.keycloak.admin.client.resource.UsersResource; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserRepresentation; @@ -92,24 +93,29 @@ public class UserService { @CacheEvict(value = USERS_CACHE, allEntries = true, beforeInvocation = true) public User createUser(CreateUserRequest user) { - if (!realmService.realm(TenantContext.getTenantId()).users().search(user.getEmail()).isEmpty()) { + String username = StringUtils.isEmpty(user.getUsername()) ? user.getEmail() : user.getUsername(); + if (!this.getTenantUsersResource().search(username).isEmpty()) { throw new ConflictException("User with this username already exists"); } if (!EmailValidator.getInstance().isValid(user.getEmail())) { throw new BadRequestException("Email address format is not valid"); } + // also search by email in case the username was provided at creation + if (!StringUtils.isEmpty(user.getUsername()) && !this.getTenantUsersResource().searchByEmail(user.getEmail(), true).isEmpty()) { + throw new ConflictException("User with this email already exists"); + } validateRoles(user.getRoles()); UserRepresentation userRepresentation = new UserRepresentation(); - userRepresentation.setUsername(user.getEmail()); + userRepresentation.setUsername(username); userRepresentation.setEmail(user.getEmail()); userRepresentation.setEnabled(true); userRepresentation.setFirstName(user.getFirstName()); userRepresentation.setLastName(user.getLastName()); - try (var response = realmService.realm(TenantContext.getTenantId()).users().create(userRepresentation)) { + try (var response = this.getTenantUsersResource().create(userRepresentation)) { if (response.getStatusInfo().getFamily() != Response.Status.Family.SUCCESSFUL) { if (response.getStatusInfo().getStatusCode() == 409) { @@ -121,7 +127,7 @@ public class UserService { throw new BadRequestException("Cannot create user ... "); } - var createdUser = getUserByUsername(user.getEmail()); + var createdUser = getUserByUsername(username); try { sendResetPasswordEmail(createdUser.getUserId()); @@ -142,14 +148,18 @@ public class UserService { customPermissionService.syncAllCustomPermissions(); - return getUserByUsername(user.getEmail()); + return getUserByUsername(username); } } + private UsersResource getTenantUsersResource() { + return realmService.realm(TenantContext.getTenantId()).users(); + } + private User getUserByUsername(String username) { - var userList = realmService.realm(TenantContext.getTenantId()).users().search(username); + var userList = this.getTenantUsersResource().search(username); if (userList.isEmpty()) { throw new NotFoundException("User with this username already exists"); } @@ -161,7 +171,7 @@ public class UserService { private void sendResetPasswordEmail(String userId) { try { - realmService.realm(TenantContext.getTenantId()).users().get(userId).executeActionsEmail(Collections.singletonList("UPDATE_PASSWORD"), 86400); + this.getTenantUsersResource().get(userId).executeActionsEmail(Collections.singletonList("UPDATE_PASSWORD"), 86400); } catch (Exception e) { throw new BadRequestException("Failed to send email", e); } @@ -252,7 +262,7 @@ public class UserService { throw new BadRequestException("No id provided."); } try { - return realmService.realm(TenantContext.getTenantId()).users().get(userId); + return this.getTenantUsersResource().get(userId); } catch (NotFoundException e) { throw new NotFoundException("User with id: " + userId + " does not exist", e); } @@ -286,7 +296,7 @@ public class UserService { private Set getRoles(String id) { - List realmMappings = realmService.realm(TenantContext.getTenantId()).users().get(id).roles().getAll().getRealmMappings(); + List realmMappings = this.getTenantUsersResource().get(id).roles().getAll().getRealmMappings(); if (realmMappings == null) { log.warn("User with id=" + id + " contains null role mappings."); return new TreeSet<>(); @@ -352,7 +362,7 @@ public class UserService { @CacheEvict(value = USERS_CACHE, allEntries = true, beforeInvocation = true) public void updateMyProfile(UpdateMyProfileRequest updateProfileRequest) { - var user = realmService.realm(TenantContext.getTenantId()).users().get(KeycloakSecurity.getUserId()); + var user = this.getUserResource(KeycloakSecurity.getUserId()); var userRepresentation = user.toRepresentation(); if (userRepresentation.getFederatedIdentities() != null && !userRepresentation.getFederatedIdentities().isEmpty() && !updateProfileRequest.getEmail() @@ -367,7 +377,7 @@ public class UserService { userRepresentation.setFirstName(updateProfileRequest.getFirstName()); userRepresentation.setLastName(updateProfileRequest.getLastName()); userRepresentation.setEmail(updateProfileRequest.getEmail()); - userRepresentation.setUsername(updateProfileRequest.getEmail()); + this.setUsername(userRepresentation, updateProfileRequest.getEmail()); try { user.update(userRepresentation); @@ -387,6 +397,12 @@ public class UserService { .build()); } + private void setUsername(UserRepresentation userRepresentation, String emailToSet) { + // update the username only if none was provided at creation and in this case the email and username are the same + if (userRepresentation.getUsername().equals(userRepresentation.getEmail())) { + userRepresentation.setUsername(emailToSet); + } + } private void validatePassword(String username, String password) { @@ -484,7 +500,7 @@ public class UserService { @CacheEvict(value = USERS_CACHE, allEntries = true, beforeInvocation = true) public void updateProfile(String userId, UpdateProfileRequest updateProfileRequest) { - var user = realmService.realm(TenantContext.getTenantId()).users().get(userId); + var user = this.getUserResource(userId); var userRepresentation = user.toRepresentation(); if (userRepresentation.getFederatedIdentities() != null && !userRepresentation.getFederatedIdentities().isEmpty() && !updateProfileRequest.getEmail() @@ -499,6 +515,7 @@ public class UserService { userRepresentation.setFirstName(updateProfileRequest.getFirstName()); userRepresentation.setLastName(updateProfileRequest.getLastName()); userRepresentation.setEmail(updateProfileRequest.getEmail()); + this.setUsername(userRepresentation, updateProfileRequest.getEmail()); user.update(userRepresentation); @@ -516,7 +533,7 @@ public class UserService { public User activateProfile(String userId, boolean isActive) { - var user = realmService.realm(TenantContext.getTenantId()).users().get(userId); + var user = this.getUserResource(userId); var userRepresentation = user.toRepresentation(); userRepresentation.setEnabled(isActive); @@ -535,7 +552,7 @@ public class UserService { .details(Map.of("Profile activated", isActive)) .build()); - return convert(realmService.realm(TenantContext.getTenantId()).users().get(userId).toRepresentation()); + return convert(this.getTenantUsersResource().get(userId).toRepresentation()); } diff --git a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/CreateUserRequest.java b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/CreateUserRequest.java index 77a34b2a5..5fad6f6c4 100644 --- a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/CreateUserRequest.java +++ b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/CreateUserRequest.java @@ -12,6 +12,9 @@ public class CreateUserRequest { @Schema(description = "Email of user.") private String email; + @Schema(description = "Username of user.") + private String username; + @Schema(description = "First name of user.") private String firstName;