diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/model/CreateUserRequest.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/model/CreateUserRequest.java index 3640c71..3584ea6 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/model/CreateUserRequest.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/model/CreateUserRequest.java @@ -25,4 +25,7 @@ public class CreateUserRequest { @Schema(description = "Roles to assign to user.") private Set roles = new HashSet<>(); + @Schema(description = "Whether a set password mail should be sent") + private boolean sendSetPasswordMail; + } diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/GeneralConfigurationService.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/GeneralConfigurationService.java index 15bccbb..ed3a619 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/GeneralConfigurationService.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/GeneralConfigurationService.java @@ -67,6 +67,9 @@ public class GeneralConfigurationService { var realmRepresentation = realm.toRepresentation(); realmRepresentation.setResetPasswordAllowed(generalConfigurationModel.isForgotPasswordFunctionEnabled()); + + realmRepresentation.getAttributes().put("actionTokenGeneratedByUserLifespan.idp-verify-account-via-email", Integer.toString(86400)); + if (!StringUtils.isEmpty(generalConfigurationModel.getAuxiliaryName())) { setDisplayName(realmRepresentation, tenantUserManagementProperties.getApplicationName() + " (" + generalConfigurationModel.getAuxiliaryName() + ")"); } else { diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/UserService.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/UserService.java index 96eb7c6..c83863c 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/UserService.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/UserService.java @@ -8,11 +8,6 @@ import java.util.TreeSet; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import jakarta.ws.rs.ClientErrorException; -import jakarta.ws.rs.NotAuthorizedException; -import jakarta.ws.rs.NotFoundException; -import jakarta.ws.rs.core.Response; - import org.apache.commons.validator.routines.EmailValidator; import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; import org.jboss.resteasy.client.jaxrs.internal.ResteasyClientBuilderImpl; @@ -23,7 +18,6 @@ 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; - import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.annotation.CacheEvict; @@ -48,6 +42,10 @@ import com.knecon.fforesight.tenantusermanagement.model.User; import com.knecon.fforesight.tenantusermanagement.properties.TenantUserManagementProperties; import io.micrometer.common.util.StringUtils; +import jakarta.ws.rs.ClientErrorException; +import jakarta.ws.rs.NotAuthorizedException; +import jakarta.ws.rs.NotFoundException; +import jakarta.ws.rs.core.Response; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -112,10 +110,12 @@ public class UserService { var createdUser = getUserByUsername(username); - try { - sendResetPasswordEmail(createdUser.getUserId()); - } catch (Exception e) { - log.debug("Activation E-mail could not be sent!", e); + if (user.isSendSetPasswordMail()) { + try { + sendResetPasswordEmail(createdUser.getUserId()); + } catch (Exception e) { + log.debug("Set Password E-mail could not be sent!", e); + } } this.rabbitTemplate.convertAndSend(userExchangeName, "user.created", new UserCreatedEvent(createdUser, KeycloakSecurity.getUserId())); @@ -132,9 +132,16 @@ public class UserService { public void checkRankOrderForAssigningRole(Set newRoles, Set currentUserRoles) { var roleMapping = tenantUserManagementProperties.getKcRoleMapping(); - var maxRank = currentUserRoles.stream().map(r -> roleMapping.getRole(r).getRank()).max(Integer::compare).orElse(-1); - var newRolesRank = newRoles.stream().map(r -> roleMapping.getRole(r).getRank()).toList(); - var maxNewRolesRank = newRolesRank.stream().max(Integer::compare).orElse(-1); + var maxRank = currentUserRoles.stream() + .map(r -> roleMapping.getRole(r).getRank()) + .max(Integer::compare) + .orElse(-1); + var newRolesRank = newRoles.stream() + .map(r -> roleMapping.getRole(r).getRank()) + .toList(); + var maxNewRolesRank = newRolesRank.stream() + .max(Integer::compare) + .orElse(-1); if (maxNewRolesRank > maxRank) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Cannot assign this role to that user. Insufficient rights"); @@ -145,9 +152,7 @@ public class UserService { public Set getUserRoles(String userId) { var userResource = getUserResource(userId); - return userResource.roles() - .realmLevel() - .listEffective() + return userResource.roles().realmLevel().listEffective() .stream() .map(RoleRepresentation::getName) .filter(r -> tenantUserManagementProperties.getKcRoleMapping().isValidRole(r)) @@ -177,12 +182,19 @@ public class UserService { }); var userResource = getUserResource(userId); - var userRoles = userResource.roles().realmLevel().listEffective().stream().map(RoleRepresentation::getName).collect(Collectors.toSet()); + var userRoles = userResource.roles().realmLevel().listEffective() + .stream() + .map(RoleRepresentation::getName) + .collect(Collectors.toSet()); validateSufficientRoles(userId, userRoles, newRoles, currentUserRoles); - var currentRolesAsRoleRepresentation = allRoles.stream().map(this::getRoleRepresentation).collect(Collectors.toList()); - var newMappedRoles = newRoles.stream().map(this::getRoleRepresentation).collect(Collectors.toList()); + var currentRolesAsRoleRepresentation = allRoles.stream() + .map(this::getRoleRepresentation) + .collect(Collectors.toList()); + var newMappedRoles = newRoles.stream() + .map(this::getRoleRepresentation) + .collect(Collectors.toList()); userResource.roles().realmLevel().remove(currentRolesAsRoleRepresentation); userResource.roles().realmLevel().add(newMappedRoles); @@ -198,9 +210,16 @@ public class UserService { public void validateSufficientRoles(String userId, Set userRoles, Set newRoles, Set currentUserRoles) { var roleMapping = tenantUserManagementProperties.getKcRoleMapping(); - var maxRank = currentUserRoles.stream().map(r -> roleMapping.getRole(r).getRank()).max(Integer::compare).orElse(-1); - var newRolesRank = newRoles.stream().map(r -> roleMapping.getRole(r).getRank()).toList(); - var maxNewRolesRank = newRolesRank.stream().max(Integer::compare).orElse(-1); + var maxRank = currentUserRoles.stream() + .map(r -> roleMapping.getRole(r).getRank()) + .max(Integer::compare) + .orElse(-1); + var newRolesRank = newRoles.stream() + .map(r -> roleMapping.getRole(r).getRank()) + .toList(); + var maxNewRolesRank = newRolesRank.stream() + .max(Integer::compare) + .orElse(-1); var untouchableRoles = userRoles.stream() .filter(roleMapping::isValidRole) @@ -225,7 +244,10 @@ public class UserService { public Optional getUserById(String userId) { - return userListingService.getAllUsers(TenantContext.getTenantId()).stream().filter(u -> u.getUserId().equalsIgnoreCase(userId)).findAny(); + return userListingService.getAllUsers(TenantContext.getTenantId()) + .stream() + .filter(u -> u.getUserId().equalsIgnoreCase(userId)) + .findAny(); } @@ -272,7 +294,8 @@ public class UserService { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "No id provided."); } try { - return this.getTenantUsersResource().get(userId); + return this.getTenantUsersResource() + .get(userId); } catch (NotFoundException e) { throw new NotFoundException("User with id: " + userId + " does not exist", e); } @@ -294,10 +317,10 @@ public class UserService { .clientId(tenantUserManagementProperties.getApplicationClientId()) .grantType(OAuth2Constants.PASSWORD) .resteasyClient(new ResteasyClientBuilderImpl().connectionTTL(2, TimeUnit.SECONDS) - .hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY) - .connectionPoolSize(tenantUserManagementProperties.getConnectionPoolSize()) - .disableTrustManager() - .build()) + .hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY) + .connectionPoolSize(tenantUserManagementProperties.getConnectionPoolSize()) + .disableTrustManager() + .build()) .build(); try { @@ -364,13 +387,17 @@ public class UserService { private Set getRoles(String id) { - List realmMappings = this.getTenantUsersResource().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<>(); } var allRoles = tenantUserManagementProperties.getKcRoleMapping().getAllRoles(); - return realmMappings.stream().map(RoleRepresentation::getName).filter(allRoles::contains).collect(Collectors.toSet()); + return realmMappings.stream() + .map(RoleRepresentation::getName) + .filter(allRoles::contains) + .collect(Collectors.toSet()); } @@ -457,7 +484,8 @@ public class UserService { this.rabbitTemplate.convertAndSend(userExchangeName, "user.statusChanged", (new UserStatusToggleEvent(toggledUser, KeycloakSecurity.getUserId()))); - return convert(this.getTenantUsersResource().get(userId).toRepresentation()); + return convert(this.getTenantUsersResource() + .get(userId).toRepresentation()); } @@ -474,7 +502,8 @@ public class UserService { request.setType(CredentialRepresentation.PASSWORD); request.setTemporary(resetPasswordRequest.isTemporary()); request.setValue(resetPasswordRequest.getPassword()); - realmService.realm(TenantContext.getTenantId()).users().get(userId).resetPassword(request); + realmService.realm(TenantContext.getTenantId()).users() + .get(userId).resetPassword(request); log.info("User {} resetted password for user {}", KeycloakSecurity.getUserId(), userId); } catch (Exception e) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Could not reset password. It does not match the password policy.", e); @@ -492,7 +521,8 @@ public class UserService { RoleRepresentation realmRole; try { - realmRole = realmService.realm(TenantContext.getTenantId()).roles().get(role).toRepresentation(); + realmRole = realmService.realm(TenantContext.getTenantId()).roles() + .get(role).toRepresentation(); } catch (NotFoundException e) { log.warn("The realm role {} is not found.", role); throw new ResponseStatusException(HttpStatus.NOT_FOUND, "The realm role " + role + " is not found.", e); @@ -504,7 +534,8 @@ public class UserService { private void sendResetPasswordEmail(String userId) { try { - this.getTenantUsersResource().get(userId).executeActionsEmail(Collections.singletonList("UPDATE_PASSWORD"), 86400); + this.getTenantUsersResource() + .get(userId).executeActionsEmail(Collections.singletonList("UPDATE_PASSWORD"), 86400); } catch (Exception e) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Failed to send email.", e); } @@ -519,8 +550,14 @@ public class UserService { var userRoles = getRoles(targetUserId); var roleMapping = tenantUserManagementProperties.getKcRoleMapping(); - var maxRank = currentRoles.stream().map(r -> roleMapping.getRole(r).getRank()).max(Integer::compare).orElse(-1); - var targetRank = userRoles.stream().map(r -> roleMapping.getRole(r).getRank()).max(Integer::compare).orElse(-1); + var maxRank = currentRoles.stream() + .map(r -> roleMapping.getRole(r).getRank()) + .max(Integer::compare) + .orElse(-1); + var targetRank = userRoles.stream() + .map(r -> roleMapping.getRole(r).getRank()) + .max(Integer::compare) + .orElse(-1); return targetRank <= maxRank;