Pull request #630: RED-6224: Enabled multitenancy for user cache
Merge in RED/persistence-service from RED-6224 to master * commit 'd392c1dae77a8ac46f243f586154b104867cf736': RED-6224: Enabled multitenancy for user cache
This commit is contained in:
commit
ae86178cc0
@ -1,6 +1,5 @@
|
||||
package com.iqser.red.keycloak.commons;
|
||||
|
||||
import static com.iqser.red.keycloak.commons.UserCacheBuilder.USERS_CACHE;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@ -14,6 +13,7 @@ import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
||||
@ComponentScan
|
||||
public class DefaultKeyCloakCommonsConfiguration {
|
||||
|
||||
public static final String USERS_CACHE = "users";
|
||||
|
||||
@Bean
|
||||
public RedisCacheManagerBuilderCustomizer redisUserCacheManagerBuilderCustomizer() {
|
||||
|
||||
@ -13,12 +13,9 @@ public class RealmService {
|
||||
|
||||
private final KeyCloakAdminClientService keycloak;
|
||||
|
||||
private final KeyCloakSettings settings;
|
||||
public RealmResource realm(String tenantId) {
|
||||
|
||||
|
||||
public RealmResource realm() {
|
||||
|
||||
return keycloak.getAdminClient().realm(settings.getRealm());
|
||||
return keycloak.getAdminClient().realm(tenantId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
package com.iqser.red.keycloak.commons;
|
||||
|
||||
import static com.iqser.red.keycloak.commons.UserCacheBuilder.USERS_CACHE;
|
||||
import static com.iqser.red.keycloak.commons.DefaultKeyCloakCommonsConfiguration.USERS_CACHE;
|
||||
import static com.iqser.red.keycloak.commons.roles.ApplicationRoles.RED_ADMIN_ROLE;
|
||||
import static com.iqser.red.keycloak.commons.roles.ApplicationRoles.RED_MANAGER_ROLE;
|
||||
import static com.iqser.red.keycloak.commons.roles.ApplicationRoles.RED_USER_ADMIN_ROLE;
|
||||
@ -33,18 +33,20 @@ public class UserListingService {
|
||||
private final RetryTemplate retryTemplate = RetryTemplate.builder().maxAttempts(3).exponentialBackoff(1000, 2, 5000).build();
|
||||
|
||||
|
||||
@Cacheable(value = USERS_CACHE)
|
||||
public List<User> getAllUsers() {
|
||||
@Cacheable(value = USERS_CACHE, key = "'tenantId'")
|
||||
public List<User> getAllUsers(String tenantId) {
|
||||
|
||||
return retryTemplate.execute(context -> {
|
||||
List<UserRepresentation> allUsers = realmService.realm().users().search(null, 0, 500);
|
||||
|
||||
var realm = realmService.realm(tenantId);
|
||||
List<UserRepresentation> allUsers = realm.users().search(null, 0, 500);
|
||||
|
||||
Map<String, Set<String>> usersByRole = new HashMap<>();
|
||||
if(!allUsers.isEmpty()) {
|
||||
var realmRoles = realmService.realm().roles().list().stream().map(r -> r.getName().toUpperCase()).collect(Collectors.toSet());
|
||||
var realmRoles = realm.roles().list().stream().map(r -> r.getName().toUpperCase()).collect(Collectors.toSet());
|
||||
for (var role : ApplicationRoles.ROLE_DATA.keySet()) {
|
||||
if(realmRoles.contains(role)) {
|
||||
Set<UserRepresentation> users = realmService.realm().roles().get(role).getRoleUserMembers(0, 500);
|
||||
Set<UserRepresentation> users = realm.roles().get(role).getRoleUserMembers(0, 500);
|
||||
usersByRole.put(role, users.stream().map(UserRepresentation::getId).collect(Collectors.toSet()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,5 @@
|
||||
package com.iqser.red.keycloak.commons.security;
|
||||
|
||||
import static com.iqser.red.keycloak.commons.UserCacheBuilder.USERS_CACHE;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.keycloak.adapters.AdapterTokenStore;
|
||||
@ -21,14 +17,11 @@ import org.keycloak.adapters.springsecurity.authentication.SpringSecurityRequest
|
||||
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
|
||||
import org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
|
||||
@ -7,10 +7,10 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.GeneralConfigurationService;
|
||||
import com.iqser.red.service.persistence.service.v1.api.external.resource.GeneralSettingsResource;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.GeneralConfigurationModel;
|
||||
|
||||
import com.iqser.red.persistence.service.v1.external.api.impl.service.GeneralConfigurationService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.iqser.red.keycloak.commons.KeycloakSecurity;
|
||||
import com.iqser.red.keycloak.commons.RealmService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.SMTPConfigurationService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.utils.multitenancy.TenantContext;
|
||||
import com.iqser.red.service.persistence.service.v1.api.external.resource.SMTPConfigurationResource;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.SMTPConfiguration;
|
||||
|
||||
@ -49,10 +50,10 @@ public class SMTPConfigurationController implements SMTPConfigurationResource {
|
||||
smtpConfigurationService.updateSMTPConfiguration(smtpConfigurationModel);
|
||||
|
||||
// also update in KC
|
||||
var realmRepresentation = realmService.realm().toRepresentation();
|
||||
var realmRepresentation = realmService.realm(TenantContext.getTenantId()).toRepresentation();
|
||||
var propertiesMap = convertSMTPConfigurationModelToMap(smtpConfigurationModel);
|
||||
realmRepresentation.setSmtpServer(propertiesMap);
|
||||
realmService.realm().update(realmRepresentation);
|
||||
realmService.realm(TenantContext.getTenantId()).update(realmRepresentation);
|
||||
|
||||
}
|
||||
|
||||
@ -77,7 +78,7 @@ public class SMTPConfigurationController implements SMTPConfigurationResource {
|
||||
@PreAuthorize("hasAuthority('" + WRITE_SMTP_CONFIGURATION + "')")
|
||||
public void testSMTPConfiguration(@RequestBody SMTPConfiguration smtpConfigurationModel) {
|
||||
|
||||
var currentUserEmail = realmService.realm().users().get(KeycloakSecurity.getUserId()).toRepresentation().getEmail();
|
||||
var currentUserEmail = realmService.realm(TenantContext.getTenantId()).users().get(KeycloakSecurity.getUserId()).toRepresentation().getEmail();
|
||||
|
||||
smtpConfigurationService.testSMTPConfiguration(currentUserEmail, smtpConfigurationModel);
|
||||
}
|
||||
@ -90,9 +91,9 @@ public class SMTPConfigurationController implements SMTPConfigurationResource {
|
||||
smtpConfigurationService.deleteConfiguration();
|
||||
|
||||
// also update in KC
|
||||
var realmRepresentation = realmService.realm().toRepresentation();
|
||||
var realmRepresentation = realmService.realm(TenantContext.getTenantId()).toRepresentation();
|
||||
realmRepresentation.setSmtpServer(new HashMap<>());
|
||||
realmService.realm().update(realmRepresentation);
|
||||
realmService.realm(TenantContext.getTenantId()).update(realmRepresentation);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
package com.iqser.red.persistence.service.v1.external.api.impl.initializer;
|
||||
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.iqser.red.persistence.service.v1.external.api.impl.service.GeneralConfigurationService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.settings.FileManagementServiceSettings;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class RealmInitializer implements ApplicationRunner {
|
||||
|
||||
private final GeneralConfigurationService generalConfigurationService;
|
||||
private final FileManagementServiceSettings fileManagementServiceSettings;
|
||||
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
|
||||
var generalConfiguration = generalConfigurationService.getGeneralConfigurations();
|
||||
log.info("Currently Configured Application Name: {}, default name: {}", generalConfiguration.getDisplayName(), fileManagementServiceSettings.getApplicationName());
|
||||
generalConfigurationService.updateGeneralConfigurations(generalConfigurationService.getGeneralConfigurations());
|
||||
}
|
||||
|
||||
}
|
||||
@ -18,6 +18,7 @@ import com.iqser.red.keycloak.commons.UserListingService;
|
||||
import com.iqser.red.keycloak.commons.model.User;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.acl.AbstractACLService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.acl.RedPermission;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.utils.multitenancy.TenantContext;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.permission.CustomPermissionMappingModel;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.permission.CustomPermissionModel;
|
||||
|
||||
@ -108,7 +109,7 @@ public abstract class ICustomPermissionService<T, ID extends Serializable> exten
|
||||
|
||||
private Set<String> getUserIds() {
|
||||
|
||||
return userListingService.getAllUsers().stream().map(User::getUserId).collect(Collectors.toSet());
|
||||
return userListingService.getAllUsers(TenantContext.getTenantId()).stream().map(User::getUserId).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -30,12 +30,12 @@ public class KeyCloakUserSyncService {
|
||||
|
||||
public void syncUsersWithKC() {
|
||||
|
||||
var allUsers = userListingService.getAllUsers();
|
||||
// all userIds from KC
|
||||
var allUserIds = allUsers.stream().map(User::getUserId).collect(Collectors.toSet());
|
||||
|
||||
tenantManagementService.getTenants().forEach(tenant -> {
|
||||
|
||||
var allUsers = userListingService.getAllUsers(tenant.getTenantId());
|
||||
// all userIds from KC
|
||||
var allUserIds = allUsers.stream().map(User::getUserId).collect(Collectors.toSet());
|
||||
|
||||
TenantContext.setTenantId(tenant.getTenantId());
|
||||
|
||||
var redactionObjectsUserIds = new HashSet<String>();
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package com.iqser.red.persistence.service.v1.external.api.impl.service;
|
||||
package com.iqser.red.service.persistence.management.v1.processor.service;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
@ -7,10 +7,13 @@ import org.springframework.stereotype.Service;
|
||||
import com.iqser.red.keycloak.commons.KeyCloakSettings;
|
||||
import com.iqser.red.keycloak.commons.RealmService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.settings.FileManagementServiceSettings;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.utils.multitenancy.TenantContext;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.GeneralConfigurationModel;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class GeneralConfigurationService {
|
||||
@ -19,10 +22,17 @@ public class GeneralConfigurationService {
|
||||
private final FileManagementServiceSettings fileManagementServiceSettings;
|
||||
private final KeyCloakSettings keyCloakSettings;
|
||||
|
||||
public void initGeneralConfiguration(String tenantId){
|
||||
TenantContext.setTenantId(tenantId);
|
||||
var generalConfiguration = getGeneralConfigurations();
|
||||
log.info("Currently Configured Application Name: {}, default name: {}", generalConfiguration.getDisplayName(), fileManagementServiceSettings.getApplicationName());
|
||||
updateGeneralConfigurations(getGeneralConfigurations());
|
||||
TenantContext.clear();
|
||||
}
|
||||
|
||||
public GeneralConfigurationModel getGeneralConfigurations() {
|
||||
|
||||
var realm = realmService.realm().toRepresentation();
|
||||
var realm = realmService.realm(TenantContext.getTenantId()).toRepresentation();
|
||||
var auxiliaryName = realm.getDisplayNameHtml();
|
||||
|
||||
if (!fileManagementServiceSettings.getApplicationName().equals(auxiliaryName)) {
|
||||
@ -45,7 +55,7 @@ public class GeneralConfigurationService {
|
||||
|
||||
public void updateGeneralConfigurations(GeneralConfigurationModel generalConfigurationModel) {
|
||||
|
||||
var realm = realmService.realm();
|
||||
var realm = realmService.realm(TenantContext.getTenantId());
|
||||
|
||||
var clientRepresentations = realm.clients().findByClientId(keyCloakSettings.getClientId());
|
||||
for (var client : clientRepresentations) {
|
||||
@ -0,0 +1,29 @@
|
||||
package com.iqser.red.service.persistence.management.v1.processor.service;
|
||||
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class KeyCloakRoleManagerApplicationRunner implements ApplicationRunner {
|
||||
|
||||
private final TenantManagementService tenantManagementService;
|
||||
private final KeyCloakRoleManagerService keyCloakRoleManagerService;
|
||||
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
|
||||
tenantManagementService.getTenants().forEach(tenant -> {
|
||||
|
||||
keyCloakRoleManagerService.updateRoles(tenant.getTenantId());
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package com.iqser.red.keycloak.commons;
|
||||
package com.iqser.red.service.persistence.management.v1.processor.service;
|
||||
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.GET_RSS;
|
||||
import static com.iqser.red.keycloak.commons.roles.ApplicationRoles.RED_ADMIN_ROLE;
|
||||
@ -13,12 +13,12 @@ import java.util.stream.Collectors;
|
||||
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.iqser.red.keycloak.commons.KeyCloakSettings;
|
||||
import com.iqser.red.keycloak.commons.RealmService;
|
||||
import com.iqser.red.keycloak.commons.roles.ApplicationRoles;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@ -27,23 +27,23 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class KeyCloakRoleManager implements ApplicationRunner {
|
||||
public class KeyCloakRoleManagerService {
|
||||
|
||||
private final RealmService realmService;
|
||||
|
||||
private final KeyCloakSettings settings;
|
||||
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
public void updateRoles(String tenantId) {
|
||||
|
||||
var realm = realmService.realm(tenantId);
|
||||
|
||||
log.info("Running KeyCloak Role Manager, managing client: {} with system client {}", settings.getApplicationClientId(), settings.getClientId());
|
||||
var existingRoles = realmService.realm().roles().list().stream().map(RoleRepresentation::getName).collect(Collectors.toList());
|
||||
var existingRoles = realm.roles().list().stream().map(RoleRepresentation::getName).collect(Collectors.toList());
|
||||
|
||||
log.info("Existing KC roles: {}", existingRoles);
|
||||
|
||||
var redactionClientRepresentation = getRedactionClientRepresentation();
|
||||
var redactionClient = realmService.realm().clients().get(redactionClientRepresentation.getId());
|
||||
var redactionClientRepresentation = getRedactionClientRepresentation(tenantId);
|
||||
var redactionClient = realm.clients().get(redactionClientRepresentation.getId());
|
||||
var clientRoles = redactionClient.roles().list().stream().map(RoleRepresentation::getName).collect(Collectors.toList());
|
||||
|
||||
var allRoles = ApplicationRoles.ROLE_DATA.values().stream().flatMap(Set::stream).collect(Collectors.toSet());
|
||||
@ -58,8 +58,8 @@ public class KeyCloakRoleManager implements ApplicationRunner {
|
||||
clientRoles.forEach(clientRole -> {
|
||||
try {
|
||||
redactionClient.roles().deleteRole(clientRole);
|
||||
}catch (Exception e){
|
||||
log.warn("Failed to delete client role: {}",clientRole);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to delete client role: {}", clientRole);
|
||||
}
|
||||
});
|
||||
|
||||
@ -86,43 +86,41 @@ public class KeyCloakRoleManager implements ApplicationRunner {
|
||||
log.info("Application Role: {} doesn't exist, creating it now", applicationRole);
|
||||
var role = new RoleRepresentation(applicationRole, applicationRole, false);
|
||||
role.setComposite(true);
|
||||
realmService.realm().roles().create(role);
|
||||
realm.roles().create(role);
|
||||
|
||||
}
|
||||
var applicationRoleResource = realmService.realm().roles().get(applicationRole);
|
||||
Set<RoleRepresentation> composites = realmService.realm()
|
||||
.rolesById()
|
||||
var applicationRoleResource = realm.roles().get(applicationRole);
|
||||
Set<RoleRepresentation> composites = realm.rolesById()
|
||||
.getClientRoleComposites(applicationRoleResource.toRepresentation().getId(), redactionClient.toRepresentation().getId());
|
||||
|
||||
log.info("Deleting previous composites for application role {}", applicationRole);
|
||||
realmService.realm().rolesById().deleteComposites(applicationRoleResource.toRepresentation().getId(), new ArrayList<>(composites));
|
||||
realm.rolesById().deleteComposites(applicationRoleResource.toRepresentation().getId(), new ArrayList<>(composites));
|
||||
var relevantClientRoles = allClientRoles.stream().filter(role -> ApplicationRoles.ROLE_DATA.get(applicationRole).contains(role.getName())).collect(Collectors.toList());
|
||||
log.info("Writing new composites for application role {}", applicationRole);
|
||||
realmService.realm().rolesById().addComposites(applicationRoleResource.toRepresentation().getId(), relevantClientRoles);
|
||||
realm.rolesById().addComposites(applicationRoleResource.toRepresentation().getId(), relevantClientRoles);
|
||||
log.info("Finished application role {}", applicationRole);
|
||||
}
|
||||
|
||||
// add RED_USER Realm Role to RED_MANAGER
|
||||
var redUserRole = realmService.realm().roles().get(RED_USER_ROLE);
|
||||
var redManagerRole = realmService.realm().roles().get(RED_MANAGER_ROLE);
|
||||
var redUserRole = realm.roles().get(RED_USER_ROLE);
|
||||
var redManagerRole = realm.roles().get(RED_MANAGER_ROLE);
|
||||
|
||||
realmService.realm().rolesById().addComposites(redManagerRole.toRepresentation().getId(), Lists.newArrayList(redUserRole.toRepresentation()));
|
||||
realm.rolesById().addComposites(redManagerRole.toRepresentation().getId(), Lists.newArrayList(redUserRole.toRepresentation()));
|
||||
|
||||
// add RED_USER_ADMIN Realm Role to RED_ADMIN
|
||||
var redAdminRole = realmService.realm().roles().get(RED_ADMIN_ROLE);
|
||||
var redUserAdminRole = realmService.realm().roles().get(RED_USER_ADMIN_ROLE);
|
||||
var redAdminRole = realm.roles().get(RED_ADMIN_ROLE);
|
||||
var redUserAdminRole = realm.roles().get(RED_USER_ADMIN_ROLE);
|
||||
|
||||
realmService.realm().rolesById().addComposites(redAdminRole.toRepresentation().getId(), Lists.newArrayList(redUserAdminRole.toRepresentation()));
|
||||
realm.rolesById().addComposites(redAdminRole.toRepresentation().getId(), Lists.newArrayList(redUserAdminRole.toRepresentation()));
|
||||
|
||||
log.info("Finished KC Role Manager");
|
||||
log.info("Finished KC Role Manager for tenant {}", tenantId);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private ClientRepresentation getRedactionClientRepresentation() {
|
||||
private ClientRepresentation getRedactionClientRepresentation(String tenantId) {
|
||||
|
||||
String applicationClientId = settings.getApplicationClientId();
|
||||
var clientRepresentationIterator = realmService.realm().clients().findByClientId(applicationClientId).iterator();
|
||||
var clientRepresentationIterator = realmService.realm(tenantId).clients().findByClientId(applicationClientId).iterator();
|
||||
|
||||
if (clientRepresentationIterator.hasNext()) {
|
||||
return clientRepresentationIterator.next();
|
||||
@ -63,19 +63,23 @@ public class TenantManagementService {
|
||||
private final ResourceLoader resourceLoader;
|
||||
private final TenantRepository tenantRepository;
|
||||
private final AsyncMigrationStarterService asyncMigrationStarterService;
|
||||
private final GeneralConfigurationService generalConfigurationService;
|
||||
private final KeyCloakRoleManagerService keyCloakRoleManagerService;
|
||||
|
||||
|
||||
public TenantManagementService(EncryptionDecryptionService encryptionService,
|
||||
@Qualifier("tenantLiquibaseProperties") LiquibaseProperties liquibaseProperties,
|
||||
ResourceLoader resourceLoader,
|
||||
TenantRepository tenantRepository,
|
||||
AsyncMigrationStarterService asyncMigrationStarterService) {
|
||||
AsyncMigrationStarterService asyncMigrationStarterService, GeneralConfigurationService generalConfigurationService, KeyCloakRoleManagerService keyCloakRoleManagerService) {
|
||||
|
||||
this.encryptionService = encryptionService;
|
||||
this.liquibaseProperties = liquibaseProperties;
|
||||
this.resourceLoader = resourceLoader;
|
||||
this.tenantRepository = tenantRepository;
|
||||
this.asyncMigrationStarterService = asyncMigrationStarterService;
|
||||
this.generalConfigurationService = generalConfigurationService;
|
||||
this.keyCloakRoleManagerService = keyCloakRoleManagerService;
|
||||
}
|
||||
|
||||
|
||||
@ -141,6 +145,8 @@ public class TenantManagementService {
|
||||
|
||||
tenantRepository.save(tenantEntity);
|
||||
|
||||
generalConfigurationService.initGeneralConfiguration(tenantRequest.getTenantId());
|
||||
keyCloakRoleManagerService.updateRoles(tenantRequest.getTenantId());
|
||||
asyncMigrationStarterService.runForTenant(tenantRequest.getTenantId());
|
||||
|
||||
} else {
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
package com.iqser.red.keycloak.commons;
|
||||
package com.iqser.red.service.persistence.management.v1.processor.service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.iqser.red.keycloak.commons.UserListingService;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@ -12,15 +14,14 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@RequiredArgsConstructor
|
||||
public class UserCacheBuilder {
|
||||
|
||||
public static final String USERS_CACHE = "users";
|
||||
|
||||
private final UserListingService userService;
|
||||
private final TenantManagementService tenantManagementService;
|
||||
|
||||
|
||||
@PostConstruct
|
||||
protected void postConstruct() {
|
||||
|
||||
userService.getAllUsers();
|
||||
tenantManagementService.getTenants().forEach(tenant -> userService.getAllUsers(tenant.getTenantId()));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
package com.iqser.red.service.persistence.management.v1.processor.service;
|
||||
|
||||
import static com.iqser.red.keycloak.commons.UserCacheBuilder.USERS_CACHE;
|
||||
import static com.iqser.red.keycloak.commons.DefaultKeyCloakCommonsConfiguration.USERS_CACHE;
|
||||
import static com.iqser.red.keycloak.commons.roles.ApplicationRoles.RED_ADMIN_ROLE;
|
||||
import static com.iqser.red.keycloak.commons.roles.ApplicationRoles.RED_MANAGER_ROLE;
|
||||
import static com.iqser.red.keycloak.commons.roles.ApplicationRoles.RED_USER_ADMIN_ROLE;
|
||||
@ -52,6 +52,7 @@ import com.iqser.red.service.persistence.management.v1.processor.exception.Confl
|
||||
import com.iqser.red.service.persistence.management.v1.processor.exception.NotAllowedException;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.NotificationPersistenceService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.utils.multitenancy.TenantContext;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.CreateUserRequest;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.ResetPasswordRequest;
|
||||
@ -91,7 +92,7 @@ public class UserService {
|
||||
@CacheEvict(value = USERS_CACHE, allEntries = true, beforeInvocation = true)
|
||||
public User createUser(CreateUserRequest user) {
|
||||
|
||||
if (!realmService.realm().users().search(user.getEmail()).isEmpty()) {
|
||||
if (!realmService.realm(TenantContext.getTenantId()).users().search(user.getEmail()).isEmpty()) {
|
||||
throw new ConflictException("User with this username already exists");
|
||||
}
|
||||
|
||||
@ -108,7 +109,7 @@ public class UserService {
|
||||
userRepresentation.setFirstName(user.getFirstName());
|
||||
userRepresentation.setLastName(user.getLastName());
|
||||
|
||||
try (var response = realmService.realm().users().create(userRepresentation)) {
|
||||
try (var response = realmService.realm(TenantContext.getTenantId()).users().create(userRepresentation)) {
|
||||
|
||||
if (response.getStatusInfo().getFamily() != Response.Status.Family.SUCCESSFUL) {
|
||||
if (response.getStatusInfo().getStatusCode() == 409) {
|
||||
@ -148,7 +149,7 @@ public class UserService {
|
||||
|
||||
private User getUserByUsername(String username) {
|
||||
|
||||
var userList = realmService.realm().users().search(username);
|
||||
var userList = realmService.realm(TenantContext.getTenantId()).users().search(username);
|
||||
if (userList.isEmpty()) {
|
||||
throw new NotFoundException("User with this username already exists");
|
||||
}
|
||||
@ -160,7 +161,7 @@ public class UserService {
|
||||
private void sendResetPasswordEmail(String userId) {
|
||||
|
||||
try {
|
||||
realmService.realm().users().get(userId).executeActionsEmail(Collections.singletonList("UPDATE_PASSWORD"), 86400);
|
||||
realmService.realm(TenantContext.getTenantId()).users().get(userId).executeActionsEmail(Collections.singletonList("UPDATE_PASSWORD"), 86400);
|
||||
} catch (Exception e) {
|
||||
throw new BadRequestException("Failed to send email", e);
|
||||
}
|
||||
@ -251,7 +252,7 @@ public class UserService {
|
||||
throw new BadRequestException("No id provided.");
|
||||
}
|
||||
try {
|
||||
return realmService.realm().users().get(userId);
|
||||
return realmService.realm(TenantContext.getTenantId()).users().get(userId);
|
||||
} catch (NotFoundException e) {
|
||||
throw new NotFoundException("User with id: " + userId + " does not exist", e);
|
||||
}
|
||||
@ -274,7 +275,7 @@ public class UserService {
|
||||
|
||||
RoleRepresentation realmRole;
|
||||
try {
|
||||
realmRole = realmService.realm().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 NotFoundException("The realm role " + role + " is not found.", e);
|
||||
@ -285,7 +286,7 @@ public class UserService {
|
||||
|
||||
private Set<String> getRoles(String id) {
|
||||
|
||||
List<RoleRepresentation> realmMappings = realmService.realm().users().get(id).roles().getAll().getRealmMappings();
|
||||
List<RoleRepresentation> realmMappings = realmService.realm(TenantContext.getTenantId()).users().get(id).roles().getAll().getRealmMappings();
|
||||
if (realmMappings == null) {
|
||||
log.warn("User with id=" + id + " contains null role mappings.");
|
||||
return new TreeSet<>();
|
||||
@ -338,20 +339,20 @@ public class UserService {
|
||||
|
||||
public Optional<User> getUserById(String userId) {
|
||||
|
||||
return userListingService.getAllUsers().stream().filter(u -> u.getUserId().equalsIgnoreCase(userId)).findAny();
|
||||
return userListingService.getAllUsers(TenantContext.getTenantId()).stream().filter(u -> u.getUserId().equalsIgnoreCase(userId)).findAny();
|
||||
}
|
||||
|
||||
|
||||
public List<User> getUsersByIds(Collection<String> userIds) {
|
||||
|
||||
return userListingService.getAllUsers().stream().filter(u -> userIds.contains(u.getUserId())).collect(Collectors.toList());
|
||||
return userListingService.getAllUsers(TenantContext.getTenantId()).stream().filter(u -> userIds.contains(u.getUserId())).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
@CacheEvict(value = USERS_CACHE, allEntries = true, beforeInvocation = true)
|
||||
public void updateMyProfile(UpdateMyProfileRequest updateProfileRequest) {
|
||||
|
||||
var user = realmService.realm().users().get(KeycloakSecurity.getUserId());
|
||||
var user = realmService.realm(TenantContext.getTenantId()).users().get(KeycloakSecurity.getUserId());
|
||||
var userRepresentation = user.toRepresentation();
|
||||
|
||||
if (userRepresentation.getFederatedIdentities() != null && !userRepresentation.getFederatedIdentities().isEmpty() && !updateProfileRequest.getEmail()
|
||||
@ -396,7 +397,7 @@ public class UserService {
|
||||
var changeEmailClient = KeycloakBuilder.builder()
|
||||
|
||||
.serverUrl(keyCloakSettings.getServerUrl())
|
||||
.realm(keyCloakSettings.getRealm())
|
||||
.realm(TenantContext.getTenantId())
|
||||
.username(username)
|
||||
.password(password)
|
||||
.clientId(keyCloakSettings.getClientId())
|
||||
@ -442,7 +443,7 @@ public class UserService {
|
||||
|
||||
removeUserFromDossiers(userId, UserRemovalModel.PERMANENT);
|
||||
|
||||
realmService.realm().users().get(userId).remove();
|
||||
realmService.realm(TenantContext.getTenantId()).users().get(userId).remove();
|
||||
customPermissionService.syncAllCustomPermissions();
|
||||
|
||||
auditPersistenceService.audit(AuditRequest.builder()
|
||||
@ -460,7 +461,7 @@ public class UserService {
|
||||
*/
|
||||
public Set<String> removeDeletedUsers(@NotNull Set<String> userIds) {
|
||||
|
||||
var users = userListingService.getAllUsers();
|
||||
var users = userListingService.getAllUsers(TenantContext.getTenantId());
|
||||
Set<String> deletedUsers = new HashSet<>();
|
||||
|
||||
for (String userId : userIds) {
|
||||
@ -468,7 +469,7 @@ public class UserService {
|
||||
log.info("Will delete {} user", userId);
|
||||
|
||||
removeUserFromDossiers(userId, UserRemovalModel.PERMANENT);
|
||||
realmService.realm().users().delete(userId);
|
||||
realmService.realm(TenantContext.getTenantId()).users().delete(userId);
|
||||
|
||||
auditPersistenceService.audit(AuditRequest.builder().objectId(userId).category(AuditCategory.USER.name()).message("User removed automatically").build());
|
||||
deletedUsers.add(userId);
|
||||
@ -483,7 +484,7 @@ public class UserService {
|
||||
@CacheEvict(value = USERS_CACHE, allEntries = true, beforeInvocation = true)
|
||||
public void updateProfile(String userId, UpdateProfileRequest updateProfileRequest) {
|
||||
|
||||
var user = realmService.realm().users().get(userId);
|
||||
var user = realmService.realm(TenantContext.getTenantId()).users().get(userId);
|
||||
var userRepresentation = user.toRepresentation();
|
||||
|
||||
if (userRepresentation.getFederatedIdentities() != null && !userRepresentation.getFederatedIdentities().isEmpty() && !updateProfileRequest.getEmail()
|
||||
@ -515,7 +516,7 @@ public class UserService {
|
||||
|
||||
public User activateProfile(String userId, boolean isActive) {
|
||||
|
||||
var user = realmService.realm().users().get(userId);
|
||||
var user = realmService.realm(TenantContext.getTenantId()).users().get(userId);
|
||||
var userRepresentation = user.toRepresentation();
|
||||
|
||||
userRepresentation.setEnabled(isActive);
|
||||
@ -534,7 +535,7 @@ public class UserService {
|
||||
.details(Map.of("Profile activated", isActive))
|
||||
.build());
|
||||
|
||||
return convert(realmService.realm().users().get(userId).toRepresentation());
|
||||
return convert(realmService.realm(TenantContext.getTenantId()).users().get(userId).toRepresentation());
|
||||
}
|
||||
|
||||
|
||||
@ -545,7 +546,7 @@ public class UserService {
|
||||
request.setType("password");
|
||||
request.setTemporary(resetPasswordRequest.isTemporary());
|
||||
request.setValue(resetPasswordRequest.getPassword());
|
||||
realmService.realm().users().get(userId).resetPassword(request);
|
||||
realmService.realm(TenantContext.getTenantId()).users().get(userId).resetPassword(request);
|
||||
} catch (Exception e) {
|
||||
throw new BadRequestException("Failed to send email", e);
|
||||
}
|
||||
@ -554,7 +555,7 @@ public class UserService {
|
||||
|
||||
public List<User> getAllUsers() {
|
||||
|
||||
return userListingService.getAllUsers();
|
||||
return userListingService.getAllUsers(TenantContext.getTenantId());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -230,13 +230,15 @@ public abstract class AbstractPersistenceServerServiceTest {
|
||||
|
||||
public void createUsers() {
|
||||
|
||||
TenantContext.setTenantId("redaction");
|
||||
|
||||
when(keyCloakAdminClientService.getAdminClient()).thenReturn(KeyCloakTestContainer.getInstance().getKeycloakAdminClient());
|
||||
|
||||
userService.evictUserCache();
|
||||
var allUsers = userService.getAllUsers();
|
||||
|
||||
if (allUsers.isEmpty()) {
|
||||
TenantContext.setTenantId("redaction");
|
||||
|
||||
var admin1 = createUser("manageradmin1@test.com");
|
||||
var admin2 = createUser("manageradmin2@test.com");
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ import java.util.concurrent.TimeUnit;
|
||||
import javax.ws.rs.NotAuthorizedException;
|
||||
|
||||
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
|
||||
import org.jboss.resteasy.client.jaxrs.internal.ResteasyClientBuilderImpl;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.admin.client.KeycloakBuilder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -12,8 +13,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import com.iqser.red.keycloak.commons.KeyCloakSettings;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.exception.AuthenticationFailedException;
|
||||
|
||||
import org.jboss.resteasy.client.jaxrs.internal.ResteasyClientBuilderImpl;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.utils.multitenancy.TenantContext;
|
||||
|
||||
@Service
|
||||
public class TokenService {
|
||||
@ -33,7 +33,7 @@ public class TokenService {
|
||||
var tokenClient = KeycloakBuilder.builder()
|
||||
|
||||
.serverUrl(keyCloakSettings.getServerUrl())
|
||||
.realm(keyCloakSettings.getRealm())
|
||||
.realm(TenantContext.getTenantId())
|
||||
.username(username)
|
||||
.password(password)
|
||||
.clientId(keyCloakSettings.getClientId())
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user