Merge branch 'RED-5012' into 'main'
RED-5012: delete created user, if no rights to create that user See merge request fforesight/tenant-user-management-service!63
This commit is contained in:
commit
d39ca21c4b
@ -1,26 +1,5 @@
|
||||
package com.knecon.fforesight.tenantusermanagement.service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.ws.rs.ClientErrorException;
|
||||
import javax.ws.rs.NotAuthorizedException;
|
||||
import javax.ws.rs.NotFoundException;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
|
||||
import com.knecon.fforesight.tenantcommons.TenantContext;
|
||||
import com.knecon.fforesight.tenantusermanagement.events.UserCreatedEvent;
|
||||
@ -49,6 +28,24 @@ 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;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import javax.ws.rs.ClientErrorException;
|
||||
import javax.ws.rs.NotAuthorizedException;
|
||||
import javax.ws.rs.NotFoundException;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@ -95,6 +92,8 @@ public class UserService {
|
||||
userRepresentation.setFirstName(user.getFirstName());
|
||||
userRepresentation.setLastName(user.getLastName());
|
||||
|
||||
checkRankOrderForAssigningRole(user.getRoles(), this.getUserRoles(KeycloakSecurity.getUserId()));
|
||||
|
||||
try (var response = this.getTenantUsersResource().create(userRepresentation)) {
|
||||
|
||||
if (response.getStatusInfo().getFamily() != Response.Status.Family.SUCCESSFUL) {
|
||||
@ -126,6 +125,39 @@ public class UserService {
|
||||
}
|
||||
|
||||
|
||||
public void checkRankOrderForAssigningRole(Set<String> newRoles, Set<String> 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);
|
||||
|
||||
if (maxNewRolesRank > maxRank) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Cannot assign this role to that user. Insufficient rights");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Set<String> getUserRoles(String userId) {
|
||||
|
||||
var userResource = getUserResource(userId);
|
||||
return userResource.roles().realmLevel().listEffective().stream()
|
||||
.map(RoleRepresentation::getName)
|
||||
.filter(r -> tenantUserManagementProperties.getKcRoleMapping().isValidRole(r))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
|
||||
@CacheEvict(value = "${commons.keycloak.userCache}", allEntries = true, beforeInvocation = true)
|
||||
public User setRoles(String userId, Set<String> roles) {
|
||||
|
||||
var currentUserRoles = this.getUserRoles(KeycloakSecurity.getUserId());
|
||||
|
||||
var userWithNewRoles = setRoles(userId, roles, currentUserRoles);
|
||||
|
||||
return userWithNewRoles;
|
||||
}
|
||||
|
||||
|
||||
@CacheEvict(value = "${commons.keycloak.userCache}", allEntries = true, beforeInvocation = true)
|
||||
public User setRoles(String userId, Set<String> newRoles, Set<String> currentUserRoles) {
|
||||
|
||||
@ -139,29 +171,7 @@ public class UserService {
|
||||
var userResource = getUserResource(userId);
|
||||
var userRoles = userResource.roles().realmLevel().listEffective().stream().map(RoleRepresentation::getName).collect(Collectors.toSet());
|
||||
|
||||
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 untouchableRoles = userRoles.stream()
|
||||
.filter(roleMapping::isValidRole)
|
||||
.map(roleMapping::getRole)
|
||||
.filter(r -> r.getRank() > maxRank)
|
||||
.map(KCRole::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (maxNewRolesRank > maxRank) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Cannot assign this role to that user. Insufficient rights");
|
||||
}
|
||||
|
||||
if (!newRoles.containsAll(untouchableRoles)) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Cannot modify some roles for this user. Insufficient rights");
|
||||
}
|
||||
|
||||
if (userId.equals(KeycloakSecurity.getUserId()) && maxRank.equals(roleMapping.getMaxRank()) && !maxNewRolesRank.equals(maxRank)) {
|
||||
throw new ResponseStatusException(HttpStatus.CONFLICT, "Cannot remove highest ranking role from self.");
|
||||
}
|
||||
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());
|
||||
@ -175,17 +185,27 @@ public class UserService {
|
||||
return userWithNewRoles;
|
||||
}
|
||||
|
||||
|
||||
@CacheEvict(value = "${commons.keycloak.userCache}", allEntries = true, beforeInvocation = true)
|
||||
public User setRoles(String userId, Set<String> roles) {
|
||||
public void validateSufficientRoles(String userId, Set<String> userRoles, Set<String> newRoles, Set<String> currentUserRoles) {
|
||||
|
||||
var currentUserResource = getUserResource(KeycloakSecurity.getUserId());
|
||||
var currentUserRoles = currentUserResource.roles().realmLevel().listEffective().stream().map(RoleRepresentation::getName).collect(Collectors.toSet());
|
||||
currentUserRoles = currentUserRoles.stream().filter(r -> tenantUserManagementProperties.getKcRoleMapping().isValidRole(r)).collect(Collectors.toSet());
|
||||
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 userWithNewRoles = setRoles(userId, roles, currentUserRoles);
|
||||
var untouchableRoles = userRoles.stream().filter(roleMapping::isValidRole).map(roleMapping::getRole).filter(r -> r.getRank() > maxRank).map(KCRole::getName).collect(Collectors.toSet());
|
||||
|
||||
return userWithNewRoles;
|
||||
if (maxNewRolesRank > maxRank) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Cannot assign this role to that user. Insufficient rights");
|
||||
}
|
||||
|
||||
if (!newRoles.containsAll(untouchableRoles)) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Cannot modify some roles for this user. Insufficient rights");
|
||||
}
|
||||
|
||||
if (userId.equals(KeycloakSecurity.getUserId()) && maxRank.equals(roleMapping.getMaxRank()) && !maxNewRolesRank.equals(maxRank)) {
|
||||
throw new ResponseStatusException(HttpStatus.CONFLICT, "Cannot remove highest ranking role from self.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -201,8 +221,7 @@ public class UserService {
|
||||
var user = this.getUserResource(KeycloakSecurity.getUserId());
|
||||
var userRepresentation = user.toRepresentation();
|
||||
|
||||
if (userRepresentation.getFederatedIdentities() != null && !userRepresentation.getFederatedIdentities().isEmpty() && !updateProfileRequest.getEmail()
|
||||
.equals(userRepresentation.getEmail())) {
|
||||
if (userRepresentation.getFederatedIdentities() != null && !userRepresentation.getFederatedIdentities().isEmpty() && !updateProfileRequest.getEmail().equals(userRepresentation.getEmail())) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "It is not allowed to change the email from a federated identity");
|
||||
}
|
||||
|
||||
@ -253,18 +272,7 @@ public class UserService {
|
||||
|
||||
var changeEmailClient = KeycloakBuilder.builder()
|
||||
|
||||
.serverUrl(tenantUserManagementProperties.getServerUrl())
|
||||
.realm(TenantContext.getTenantId())
|
||||
.username(username)
|
||||
.password(password)
|
||||
.clientId(tenantUserManagementProperties.getApplicationClientId())
|
||||
.grantType(OAuth2Constants.PASSWORD)
|
||||
.resteasyClient(new ResteasyClientBuilderImpl().connectionTTL(2, TimeUnit.SECONDS)
|
||||
.hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY)
|
||||
.connectionPoolSize(tenantUserManagementProperties.getConnectionPoolSize())
|
||||
.disableTrustManager()
|
||||
.build())
|
||||
.build();
|
||||
.serverUrl(tenantUserManagementProperties.getServerUrl()).realm(TenantContext.getTenantId()).username(username).password(password).clientId(tenantUserManagementProperties.getApplicationClientId()).grantType(OAuth2Constants.PASSWORD).resteasyClient(new ResteasyClientBuilderImpl().connectionTTL(2, TimeUnit.SECONDS).hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY).connectionPoolSize(tenantUserManagementProperties.getConnectionPoolSize()).disableTrustManager().build()).build();
|
||||
|
||||
try {
|
||||
changeEmailClient.tokenManager().getAccessTokenString();
|
||||
@ -358,8 +366,7 @@ public class UserService {
|
||||
var user = this.getUserResource(userId);
|
||||
var userRepresentation = user.toRepresentation();
|
||||
|
||||
if (userRepresentation.getFederatedIdentities() != null && !userRepresentation.getFederatedIdentities().isEmpty() && !updateProfileRequest.getEmail()
|
||||
.equals(userRepresentation.getEmail())) {
|
||||
if (userRepresentation.getFederatedIdentities() != null && !userRepresentation.getFederatedIdentities().isEmpty() && !updateProfileRequest.getEmail().equals(userRepresentation.getEmail())) {
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "It is not allowed to change the email from a federated identity");
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user