RED-3800 - sync custom permissions and KC users based on CRON and do it more often

This commit is contained in:
Timo Bejan 2023-04-21 12:23:56 +03:00
parent 6220a85c76
commit 4b941397df
5 changed files with 111 additions and 13 deletions

View File

@ -1,8 +1,15 @@
package com.iqser.red.persistence.service.v1.external.api.impl.controller; package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.util.Map;
import java.util.stream.Collectors; 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.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus; 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.ConflictException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotAllowedException; 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.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 @RestControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE) @Order(Ordered.HIGHEST_PRECEDENCE)
public class ExternalControllerAdvice { public class ExternalControllerAdvice {
private final Scheduler scheduler;
@ResponseBody @ResponseBody
@ResponseStatus(value = HttpStatus.NOT_FOUND) @ResponseStatus(value = HttpStatus.NOT_FOUND)
@ExceptionHandler(value = NotFoundException.class) @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 @ResponseBody
@ResponseStatus(value = HttpStatus.BAD_REQUEST) @ResponseStatus(value = HttpStatus.BAD_REQUEST)
@ExceptionHandler({MethodArgumentNotValidException.class}) @ExceptionHandler({MethodArgumentNotValidException.class})

View File

@ -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.AutomaticAnalysisJob;
import com.iqser.red.service.persistence.management.v1.processor.service.job.DeletedFilesCleanupJob; 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.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.SendNotificationEmailJob;
import com.iqser.red.service.persistence.management.v1.processor.service.job.SyncUserPermissionsJob;
@Configuration @Configuration
public class CreateJobsConfiguration { 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 @Bean
public Trigger downloadCleanupJobTrigger() throws ParseException { public Trigger downloadCleanupJobTrigger() throws ParseException {

View File

@ -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.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.management.v1.processor.job.KeyCloakUserSyncService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class KeyCloakUserSyncJob { public class KeyCloakUserSyncJob implements Job {
private final KeyCloakUserSyncService keyCloakUserSyncService; private final KeyCloakUserSyncService keyCloakUserSyncService;
@Scheduled(fixedRate = 1000 * 60, initialDelay = 1000 * 60) @Override
public void syncCustomPermissions() { public void execute(JobExecutionContext context) throws JobExecutionException {
keyCloakUserSyncService.syncUsersWithKC(); keyCloakUserSyncService.syncUsersWithKC();
} }

View File

@ -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.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -13,18 +16,24 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class SyncUserPermissionsJob { public class SyncUserPermissionsJob implements Job {
private final CustomPermissionService customPermissionService; private final CustomPermissionService customPermissionService;
private final TenantManagementService tenantManagementService; private final TenantManagementService tenantManagementService;
@Scheduled(fixedRate = 1000 * 60 * 10, initialDelay = 1000 * 60) @Override
public void syncCustomPermissions() { public void execute(JobExecutionContext context) throws JobExecutionException {
var singleTenant = context.getJobDetail().getJobDataMap() != null ? context.getJobDetail().getJobDataMap().get("tenantId") : null;
tenantManagementService.getTenants().forEach(tenant -> { tenantManagementService.getTenants().forEach(tenant -> {
TenantContext.setTenantId(tenant.getTenantId()); // if it's for a single tenant run only for that one, else run it for all tenants
customPermissionService.syncAllCustomPermissions(); if (tenant.getTenantId().equals(singleTenant) || singleTenant == null) {
TenantContext.setTenantId(tenant.getTenantId());
customPermissionService.syncAllCustomPermissions();
TenantContext.clear();
}
}); });
} }

View File

@ -97,8 +97,8 @@ multitenancy:
password: ${PSQL_PASSWORD:redaction} password: ${PSQL_PASSWORD:redaction}
platform: org.hibernate.dialect.PostgreSQL95Dialect platform: org.hibernate.dialect.PostgreSQL95Dialect
hikari: hikari:
maximumPoolSize: 5 maximumPoolSize: 2
minimum-idle: 5 minimum-idle: 2
data-source-properties: data-source-properties:
cachePrepStmts: true cachePrepStmts: true
prepStmtCacheSize: 1000 prepStmtCacheSize: 1000
@ -109,7 +109,7 @@ multitenancy:
datasource: datasource:
driverClassName: org.postgresql.Driver driverClassName: org.postgresql.Driver
hikari: hikari:
maximumPoolSize: 5 maximumPoolSize: 8
minimum-idle: 5 minimum-idle: 5
data-source-properties: data-source-properties:
cachePrepStmts: true cachePrepStmts: true