From 4b941397df3d4de38923569ac983f0c35d9276dd Mon Sep 17 00:00:00 2001 From: Timo Bejan Date: Fri, 21 Apr 2023 12:23:56 +0300 Subject: [PATCH] RED-3800 - sync custom permissions and KC users based on CRON and do it more often --- .../controller/ExternalControllerAdvice.java | 34 +++++++++++++ .../jobs/CreateJobsConfiguration.java | 50 +++++++++++++++++++ .../job/KeyCloakUserSyncJob.java | 13 +++-- .../job/SyncUserPermissionsJob.java | 21 +++++--- .../src/main/resources/application.yml | 6 +-- 5 files changed, 111 insertions(+), 13 deletions(-) rename persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/{ => service}/job/KeyCloakUserSyncJob.java (53%) rename persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/{acl/custom => service}/job/SyncUserPermissionsJob.java (51%) diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/ExternalControllerAdvice.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/ExternalControllerAdvice.java index 5451851e8..ad1a2ab9f 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/ExternalControllerAdvice.java +++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/ExternalControllerAdvice.java @@ -1,8 +1,15 @@ package com.iqser.red.persistence.service.v1.external.api.impl.controller; import java.time.OffsetDateTime; +import java.util.Map; import java.util.stream.Collectors; +import org.quartz.JobDataMap; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.Trigger; +import org.quartz.TriggerBuilder; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.http.HttpStatus; @@ -23,11 +30,22 @@ import com.iqser.red.service.persistence.management.v1.processor.exception.BadRe import com.iqser.red.service.persistence.management.v1.processor.exception.ConflictException; import com.iqser.red.service.persistence.management.v1.processor.exception.NotAllowedException; import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException; +import com.iqser.red.service.persistence.management.v1.processor.service.job.AutomaticAnalysisJob; +import com.iqser.red.service.persistence.management.v1.processor.service.job.SyncUserPermissionsJob; +import com.iqser.red.service.persistence.management.v1.processor.utils.multitenancy.TenantContext; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@RequiredArgsConstructor @RestControllerAdvice @Order(Ordered.HIGHEST_PRECEDENCE) public class ExternalControllerAdvice { + private final Scheduler scheduler; + + @ResponseBody @ResponseStatus(value = HttpStatus.NOT_FOUND) @ExceptionHandler(value = NotFoundException.class) @@ -76,6 +94,22 @@ public class ExternalControllerAdvice { } + @ResponseBody + @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) + @ExceptionHandler({org.springframework.security.acls.model.NotFoundException.class}) + public ErrorMessage handleACLNotFound(org.springframework.security.acls.model.NotFoundException e) { + + // in case this error occurs on a rest request / force trigger the sync job + try { + scheduler.triggerJob(new JobKey("SyncUserPermissionsJob"), new JobDataMap(Map.of("tenantId", TenantContext.getTenantId()))); + } catch (SchedulerException ex) { + log.debug("Failed to force trigger SyncUserPermissionsJob", ex); + } + + return new ErrorMessage(OffsetDateTime.now(), e.getMessage()); + } + + @ResponseBody @ResponseStatus(value = HttpStatus.BAD_REQUEST) @ExceptionHandler({MethodArgumentNotValidException.class}) diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/jobs/CreateJobsConfiguration.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/jobs/CreateJobsConfiguration.java index 9dfa92957..6c38fea47 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/jobs/CreateJobsConfiguration.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/jobs/CreateJobsConfiguration.java @@ -14,7 +14,9 @@ import org.springframework.context.annotation.Configuration; import com.iqser.red.service.persistence.management.v1.processor.service.job.AutomaticAnalysisJob; import com.iqser.red.service.persistence.management.v1.processor.service.job.DeletedFilesCleanupJob; import com.iqser.red.service.persistence.management.v1.processor.service.job.DownloadCleanupJob; +import com.iqser.red.service.persistence.management.v1.processor.service.job.KeyCloakUserSyncJob; import com.iqser.red.service.persistence.management.v1.processor.service.job.SendNotificationEmailJob; +import com.iqser.red.service.persistence.management.v1.processor.service.job.SyncUserPermissionsJob; @Configuration public class CreateJobsConfiguration { @@ -91,6 +93,54 @@ public class CreateJobsConfiguration { } + @Bean + public Trigger keyCloakUserSyncJobTrigger() throws ParseException { + + return TriggerBuilder.newTrigger() + .forJob(keyCloakUserSyncJobDetail()) + .withIdentity("KeyCloakUserSyncJobTrigger") + .withDescription("Triggers KeyCloakUserSyncJob every 2 minutes") + .withSchedule(CronScheduleBuilder.cronSchedule(new CronExpression("0 */2 * * * ?"))) + .build(); + } + + + @Bean + public JobDetail keyCloakUserSyncJobDetail() { + + return JobBuilder.newJob() + .ofType(KeyCloakUserSyncJob.class) + .storeDurably() + .withIdentity("KeyCloakUserSyncJob") + .withDescription("Sync KC Users in case of deletions in KC") + .build(); + } + + + @Bean + public Trigger syncUserPermissionsJobTrigger() throws ParseException { + + return TriggerBuilder.newTrigger() + .forJob(syncUserPermissionsJobDetail()) + .withIdentity("SyncUserPermissionsJobTrigger") + .withDescription("Triggers SyncUserPermissionsJob every 2 minutes") + .withSchedule(CronScheduleBuilder.cronSchedule(new CronExpression("0 */2 * * * ?"))) + .build(); + } + + + @Bean + public JobDetail syncUserPermissionsJobDetail() { + + return JobBuilder.newJob() + .ofType(SyncUserPermissionsJob.class) + .storeDurably() + .withIdentity("SyncUserPermissionsJob") + .withDescription("Sync custom permissions for ACL") + .build(); + } + + @Bean public Trigger downloadCleanupJobTrigger() throws ParseException { diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/job/KeyCloakUserSyncJob.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/job/KeyCloakUserSyncJob.java similarity index 53% rename from persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/job/KeyCloakUserSyncJob.java rename to persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/job/KeyCloakUserSyncJob.java index 7cf7637fa..183ea3c0d 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/job/KeyCloakUserSyncJob.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/job/KeyCloakUserSyncJob.java @@ -1,21 +1,26 @@ -package com.iqser.red.service.persistence.management.v1.processor.job; +package com.iqser.red.service.persistence.management.v1.processor.service.job; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; +import com.iqser.red.service.persistence.management.v1.processor.job.KeyCloakUserSyncService; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @Slf4j @Service @RequiredArgsConstructor -public class KeyCloakUserSyncJob { +public class KeyCloakUserSyncJob implements Job { private final KeyCloakUserSyncService keyCloakUserSyncService; - @Scheduled(fixedRate = 1000 * 60, initialDelay = 1000 * 60) - public void syncCustomPermissions() { + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { keyCloakUserSyncService.syncUsersWithKC(); } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/acl/custom/job/SyncUserPermissionsJob.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/job/SyncUserPermissionsJob.java similarity index 51% rename from persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/acl/custom/job/SyncUserPermissionsJob.java rename to persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/job/SyncUserPermissionsJob.java index 2b8ae03a5..4fb45a2e2 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/acl/custom/job/SyncUserPermissionsJob.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/job/SyncUserPermissionsJob.java @@ -1,5 +1,8 @@ -package com.iqser.red.service.persistence.management.v1.processor.acl.custom.job; +package com.iqser.red.service.persistence.management.v1.processor.service.job; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @@ -13,18 +16,24 @@ import lombok.extern.slf4j.Slf4j; @Slf4j @Service @RequiredArgsConstructor -public class SyncUserPermissionsJob { +public class SyncUserPermissionsJob implements Job { private final CustomPermissionService customPermissionService; private final TenantManagementService tenantManagementService; - @Scheduled(fixedRate = 1000 * 60 * 10, initialDelay = 1000 * 60) - public void syncCustomPermissions() { + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + + var singleTenant = context.getJobDetail().getJobDataMap() != null ? context.getJobDetail().getJobDataMap().get("tenantId") : null; tenantManagementService.getTenants().forEach(tenant -> { - TenantContext.setTenantId(tenant.getTenantId()); - customPermissionService.syncAllCustomPermissions(); + // if it's for a single tenant run only for that one, else run it for all tenants + if (tenant.getTenantId().equals(singleTenant) || singleTenant == null) { + TenantContext.setTenantId(tenant.getTenantId()); + customPermissionService.syncAllCustomPermissions(); + TenantContext.clear(); + } }); } diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/application.yml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/application.yml index b4e3f270b..40d674e5c 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/resources/application.yml +++ b/persistence-service-v1/persistence-service-server-v1/src/main/resources/application.yml @@ -97,8 +97,8 @@ multitenancy: password: ${PSQL_PASSWORD:redaction} platform: org.hibernate.dialect.PostgreSQL95Dialect hikari: - maximumPoolSize: 5 - minimum-idle: 5 + maximumPoolSize: 2 + minimum-idle: 2 data-source-properties: cachePrepStmts: true prepStmtCacheSize: 1000 @@ -109,7 +109,7 @@ multitenancy: datasource: driverClassName: org.postgresql.Driver hikari: - maximumPoolSize: 5 + maximumPoolSize: 8 minimum-idle: 5 data-source-properties: cachePrepStmts: true