Skip to content

Commit

Permalink
Auto cleanup uses encryption cleanup now #3250
Browse files Browse the repository at this point in the history
- wrote implementation for encryption cleanup
- schedule auto cleanup uses now also schedule encryption cleanup
- added unit tests
- added integration test for cipher pool data cleanup
  (inside existing JobScenario2IntTest)
  • Loading branch information
de-jcup committed Jul 31, 2024
1 parent af92447 commit 1f6ebf7
Show file tree
Hide file tree
Showing 26 changed files with 1,544 additions and 198 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import com.mercedesbenz.sechub.sharedkernel.messaging.IsSendingSyncMessage;
import com.mercedesbenz.sechub.sharedkernel.messaging.MessageDataKeys;
import com.mercedesbenz.sechub.sharedkernel.messaging.MessageID;
import com.mercedesbenz.sechub.sharedkernel.usecases.encryption.UseCaseAdminStartsEncryptionRotation;
import com.mercedesbenz.sechub.sharedkernel.usecases.encryption.UseCaseAdminFetchesEncryptionStatus;

@Service
public class AdministrationEncryptionStatusService {
Expand All @@ -25,19 +25,19 @@ public class AdministrationEncryptionStatusService {
@Autowired
AuditLogService auditLogService;

@UseCaseAdminStartsEncryptionRotation(@Step(number = 2, name = "Service call", description = "Triggers rotation of encryption via domain message"))
@UseCaseAdminFetchesEncryptionStatus(@Step(number = 1, name = "Service call", description = "Services collects encryption status from domains via event bus", needsRestDoc = true))
public SecHubEncryptionStatus fetchStatus() {
auditLogService.log("starts collecting encryption status");

SecHubEncryptionStatus status = new SecHubEncryptionStatus();
addSchedulerStatus(status);
SecHubEncryptionStatus sechubEncryptionStatus = new SecHubEncryptionStatus();
collectScheduleEncryptionStatus(sechubEncryptionStatus);

return status;
return sechubEncryptionStatus;

}

@IsSendingSyncMessage(MessageID.GET_ENCRYPTION_STATUS_SCHEDULE_DOMAIN)
private void addSchedulerStatus(SecHubEncryptionStatus status) {
private void collectScheduleEncryptionStatus(SecHubEncryptionStatus status) {
DomainMessage message = new DomainMessage(MessageID.GET_ENCRYPTION_STATUS_SCHEDULE_DOMAIN);

DomainMessageSynchronousResult result = domainMessageService.sendSynchron(message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ public static AssertUserJobInfo assertUserJobInfo(TestSecHubJobInfoForUserListPa
return AssertUserJobInfo.assertInfo(page);
}

public static AssertEncryptionStatus assertEncryptionStatus() {
return assertEncryptionStatus(as(SUPER_ADMIN).fetchEncryptionStatus());
}

public static AssertEncryptionStatus assertEncryptionStatus(SecHubEncryptionStatus status) {
return AssertEncryptionStatus.assertEncryptionStatus(status);
}
Expand Down Expand Up @@ -1260,6 +1264,18 @@ public boolean runAndReturnTrueWhenSuccesfulImpl() throws Exception {
});
}

/**
* Starts cipher pool cleanup for scheduler domain directly for test scenario.
* Normally this is done by auto cleanup mechanism only, but with this method it
* is also possible to trigger the cleanup inside integration tests.
*/
public static void startScheduleCipherPoolDataCleanup() {
resetAutoCleanupDays(0);

String url = getURLBuilder().buildIntegrationTestStartScheduleCipherPoolDataCleanup();
getSuperAdminRestHelper().put(url);
}

/**
* Will ensure complete auto cleanup inspector is reset and that auto cleanup is
* set to "wantedFormerDays days" in configuration and also in every domain auto
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import com.mercedesbenz.sechub.sharedkernel.encryption.SecHubCipherAlgorithm;
import com.mercedesbenz.sechub.sharedkernel.encryption.SecHubCipherPasswordSourceType;
import com.mercedesbenz.sechub.sharedkernel.encryption.SecHubEncryptionData;
import com.mercedesbenz.sechub.sharedkernel.encryption.SecHubEncryptionStatus;

public class JobScenario2IntTest {

Expand Down Expand Up @@ -105,7 +104,72 @@ public void a_triggered_job_is_found_in_running_jobs_list_by_admin__when_not_alr
}

@Test
public void a_triggered_job_is_NOT_found_in_running_jobs_list_by_admin__when_already_done_and_encryption_rotation_can_be_done() {
public void job_list_for_done_job__and_encryption_and_cleanup_are_working() {
/* step 1 : job list entries */
UUID doneJobUUID = assertAlreadyDoneJobIsNotListedInAdminJobList();
int scheduleDataSizeBeforeRotate = assertEncryptionStatus().domain("schedule").hasData().getDataSize();

/* step 2 : rotate encryption for job */
triggerEncryptionRotationAndAssertEncryptionIsDone(doneJobUUID);

/* step3: check status must have now one more data */
int scheduleDataSizeAfterRotate = assertEncryptionStatus()./* dump(). */domain("schedule").hasData().getDataSize();
assertThat(scheduleDataSizeAfterRotate).isEqualTo(scheduleDataSizeBeforeRotate + 1); // must be one more...

/*
* step4: rotate encryption for job again - means we ensure former job uses no
* longer the older cipher pool data and next cleanup will at least remove this
* one
*/
triggerEncryptionRotationAndAssertEncryptionIsDone(doneJobUUID);

int scheduleDataSizeAfterRotate2 = assertEncryptionStatus()/* .dump() */.domain("schedule").hasData().getDataSize();
assertThat(scheduleDataSizeAfterRotate2).isEqualTo(scheduleDataSizeBeforeRotate + 2); // must be one more...

/*
* now cleanup cipher pool data (we do not want to wait for auto cleanup...
* takes too long time
*/
startScheduleCipherPoolDataCleanup();

/*
* wait until auto cleanup is done and encryption pool is cleaned
*/
executeRunnableAndAcceptAssertionsMaximumTimes(20, () -> {
int scheduleDataSize3 = assertEncryptionStatus()./* dump(). */domain("schedule").hasData().getDataSize();
LOG.info("Fetched schedule encryption pool size(3): {}", scheduleDataSize3);
assertThat(scheduleDataSize3).isLessThan(scheduleDataSizeAfterRotate2); // must be less

}, 500);
}

private void triggerEncryptionRotationAndAssertEncryptionIsDone(UUID doneJobUUID) {
/* @formatter:off */
/* prepare 3 */
Long formerEncryptionPoolid = fetchScheduleEncryptionPoolIdForJob(doneJobUUID);
LOG.info("Job: {} had encryption pool id: {}", doneJobUUID, formerEncryptionPoolid);

SecHubEncryptionData data = new SecHubEncryptionData();
data.setAlgorithm(SecHubCipherAlgorithm.AES_GCM_SIV_256);
data.setPasswordSourceType(SecHubCipherPasswordSourceType.ENVIRONMENT_VARIABLE);
data.setPasswordSourceData("INTEGRATION_TEST_SECRET_1_AES_256"); // see IntegrationTestEncryptionEnvironmentEntryProvider

/* execution 3 - change encryption */
as(SUPER_ADMIN).rotateEncryption(data);

/* test 3 */
executeRunnableAndAcceptAssertionsMaximumTimes(10, ()->{

Long newEncryptionPoolid = fetchScheduleEncryptionPoolIdForJob(doneJobUUID);
assertThat(newEncryptionPoolid).isNotEqualTo(formerEncryptionPoolid);
LOG.info("Job: {} has now encryption pool id: {}", doneJobUUID, newEncryptionPoolid);

}, 500);
/* @formatter:on */
}

private UUID assertAlreadyDoneJobIsNotListedInAdminJobList() {
/* @formatter:off */
/* prepare */
as(SUPER_ADMIN).assignUserToProject(USER_1, PROJECT_1);

Expand Down Expand Up @@ -134,44 +198,8 @@ public void a_triggered_job_is_NOT_found_in_running_jobs_list_by_admin__when_alr
and().
onJobAdministration().
canNotFindRunningJob(jobUUID); // means events are triggered and handled */

/* execute 2 - fetch encryption status is possible*/
SecHubEncryptionStatus status = as(SUPER_ADMIN).fetchEncryptionStatus();

/* test 2 - fetch encryption status is possible*/
int scheduleDataSize= assertEncryptionStatus(status).
dump().
domain("schedule").
hasData().getDataSize();


/* prepare 3 */
Long formerEncryptionPoolid = fetchScheduleEncryptionPoolIdForJob(jobUUID);
LOG.info("formerEncryptionPoolid: {}", formerEncryptionPoolid);

SecHubEncryptionData data = new SecHubEncryptionData();
data.setAlgorithm(SecHubCipherAlgorithm.AES_GCM_SIV_256);
data.setPasswordSourceType(SecHubCipherPasswordSourceType.ENVIRONMENT_VARIABLE);
data.setPasswordSourceData("INTEGRATION_TEST_SECRET_1_AES_256"); // see IntegrationTestEncryptionEnvironmentEntryProvider

/* execution 3 - change encryption */
as(SUPER_ADMIN).rotateEncryption(data);

/* test 3 */
executeRunnableAndAcceptAssertionsMaximumTimes(10, ()->{

Long newEncryptionPoolid = fetchScheduleEncryptionPoolIdForJob(jobUUID);
LOG.info("newEncryptionPoolid: {}", newEncryptionPoolid);
assertThat(newEncryptionPoolid).isNotEqualTo(formerEncryptionPoolid);

}, 500);
/* @formatter:on */

/* test 4 - status returns now one more data */
SecHubEncryptionStatus status2 = as(SUPER_ADMIN).fetchEncryptionStatus();
int scheduleDataSize2 = assertEncryptionStatus(status2).dump().domain("schedule").hasData().getDataSize();

assertThat(scheduleDataSize2).isEqualTo(scheduleDataSize + 1); // must be one more...
return jobUUID;
/* @formatter:on */
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import com.mercedesbenz.sechub.domain.schedule.access.ScheduleAccessCountService;
import com.mercedesbenz.sechub.domain.schedule.config.SchedulerConfigService;
import com.mercedesbenz.sechub.domain.schedule.encryption.ScheduleCipherPoolCleanupService;
import com.mercedesbenz.sechub.domain.schedule.job.ScheduleSecHubJob;
import com.mercedesbenz.sechub.domain.schedule.job.SecHubJobRepository;
import com.mercedesbenz.sechub.domain.schedule.strategy.SchedulerStrategyFactory;
Expand Down Expand Up @@ -47,12 +48,21 @@ public class IntegrationTestSchedulerRestController {
@Autowired
private SecHubJobRepository jobRepository;

@Autowired
private ScheduleCipherPoolCleanupService scheduleCipherPoolCleanupService;

@RequestMapping(path = APIConstants.API_ANONYMOUS + "integrationtest/autocleanup/inspection/schedule/days", method = RequestMethod.GET, produces = {
MediaType.APPLICATION_JSON_VALUE })
public long fetchScheduleAutoCleanupConfiguredDays() {
return scheduleConfigService.getAutoCleanupInDays();
}

@RequestMapping(path = APIConstants.API_ANONYMOUS + "integrationtest/schedule/cipher-pool-data/cleanup", method = RequestMethod.PUT, produces = {
MediaType.APPLICATION_JSON_VALUE })
public void startScheduleAutoCleanupDirectlyForTesting() {
scheduleCipherPoolCleanupService.cleanupCipherPoolDataIfNecessaryAndPossible();
}

@RequestMapping(path = APIConstants.API_ANONYMOUS + "integrationtest/project/{projectId}/schedule/access/count", method = RequestMethod.GET, produces = {
MediaType.APPLICATION_JSON_VALUE })
public long countProjectAccess(@PathVariable("projectId") String projectId) {
Expand All @@ -68,15 +78,13 @@ public void deleteWaitingJobs() {
@RequestMapping(path = APIConstants.API_ANONYMOUS
+ "integrationtest/schedule/revert/job/{sechubJobUUID}/still-running", method = RequestMethod.PUT, produces = { MediaType.APPLICATION_JSON_VALUE })
public void revertJobAsStillRunning(@PathVariable("sechubJobUUID") UUID sechubJobUUID) {
;
integrationTestSchedulerService.revertJobAsStillRunning(sechubJobUUID);
}

@RequestMapping(path = APIConstants.API_ANONYMOUS
+ "integrationtest/schedule/revert/job/{sechubJobUUID}/still-not-approved", method = RequestMethod.PUT, produces = {
MediaType.APPLICATION_JSON_VALUE })
public void revertJobAsStillNotApproved(@PathVariable("sechubJobUUID") UUID sechubJobUUID) {
;
integrationTestSchedulerService.revertJobAsStillNotApproved(sechubJobUUID);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.mercedesbenz.sechub.domain.schedule;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Service;

@Service
public class ScheduleShutdownService implements ApplicationContextAware {

private static final Logger LOG = LoggerFactory.getLogger(ScheduleShutdownService.class);

private ApplicationContext context;

public void shutdownApplication() {
if (context instanceof ConfigurableApplicationContext) {
LOG.info("Will now trigger shutdown of application context");
((ConfigurableApplicationContext) context).close();
} else {
if (context == null) {
LOG.error("Cannot shutdown application context because context null!");
} else {
LOG.error("Cannot shutdown application context because wrong context:" + context.getClass());
}
}
}

@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
this.context = ctx;

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.springframework.stereotype.Service;

import com.mercedesbenz.sechub.domain.schedule.config.SchedulerConfigService;
import com.mercedesbenz.sechub.domain.schedule.encryption.ScheduleCipherPoolCleanupService;
import com.mercedesbenz.sechub.domain.schedule.job.SecHubJobDataRepository;
import com.mercedesbenz.sechub.domain.schedule.job.SecHubJobRepository;
import com.mercedesbenz.sechub.sharedkernel.Step;
Expand Down Expand Up @@ -37,6 +38,9 @@ public class ScheduleAutoCleanupService {
@Autowired
AutoCleanupResultInspector inspector;

@Autowired
ScheduleCipherPoolCleanupService encryptionPoolCleanupService;

@UseCaseScheduleAutoCleanExecution(@Step(number = 2, name = "Delete old data", description = "deletes old job information"))
public void cleanup() {
/* calculate */
Expand All @@ -61,6 +65,9 @@ public void cleanup() {
);
/* @formatter:on */

/* cleanup encryption */
encryptionPoolCleanupService.cleanupCipherPoolDataIfNecessaryAndPossible();

}

}
Loading

0 comments on commit 1f6ebf7

Please sign in to comment.