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)
- fixed failing tests
- added rest documentation
  • Loading branch information
de-jcup committed Aug 1, 2024
1 parent af92447 commit 2fed3a2
Show file tree
Hide file tree
Showing 43 changed files with 2,060 additions and 597 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"))
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 @@ -44,7 +44,7 @@ public class EncryptionAdministrationRestController {
AdministrationEncryptionStatusService administrationStatusService;

@Autowired
SecHubEncryptionDataValidator rotationDataValidator;
SecHubEncryptionDataValidator encryptionDataValidator;

/* @formatter:off */
@UseCaseAdminStartsEncryptionRotation(@Step(number=1,name="Rest call",description="Admin triggers rotation of encryption via REST", needsRestDoc =true))
Expand All @@ -64,7 +64,7 @@ public SecHubEncryptionStatus fetchEncryptionStatus() {

@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(rotationDataValidator);
binder.setValidator(encryptionDataValidator);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,5 @@ public static String extractTag(String apiEndpoint) {

return tag;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
// SPDX-License-Identifier: MIT
package com.mercedesbenz.sechub.restdoc;

import static com.mercedesbenz.sechub.docgen.util.RestDocFactory.*;
import static com.mercedesbenz.sechub.restdoc.RestDocumentation.*;
import static com.mercedesbenz.sechub.test.SecHubTestURLBuilder.*;
import static org.mockito.Mockito.*;
import static org.springframework.restdocs.headers.HeaderDocumentation.*;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*;
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import java.lang.annotation.Annotation;
import java.time.LocalDateTime;
import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import com.mercedesbenz.sechub.commons.model.job.ExecutionState;
import com.mercedesbenz.sechub.domain.administration.encryption.AdministrationEncryptionRotationService;
import com.mercedesbenz.sechub.domain.administration.encryption.AdministrationEncryptionStatusService;
import com.mercedesbenz.sechub.domain.administration.encryption.EncryptionAdministrationRestController;
import com.mercedesbenz.sechub.domain.administration.job.JobAdministrationRestController;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
import com.mercedesbenz.sechub.sharedkernel.RoleConstants;
import com.mercedesbenz.sechub.sharedkernel.configuration.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.encryption.SecHubCipherAlgorithm;
import com.mercedesbenz.sechub.sharedkernel.encryption.SecHubCipherPasswordSourceType;
import com.mercedesbenz.sechub.sharedkernel.encryption.SecHubDomainEncryptionData;
import com.mercedesbenz.sechub.sharedkernel.encryption.SecHubDomainEncryptionData.PasswordSourceData;
import com.mercedesbenz.sechub.sharedkernel.encryption.SecHubDomainEncryptionStatus;
import com.mercedesbenz.sechub.sharedkernel.encryption.SecHubEncryptionData;
import com.mercedesbenz.sechub.sharedkernel.encryption.SecHubEncryptionDataValidator;
import com.mercedesbenz.sechub.sharedkernel.encryption.SecHubEncryptionStatus;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
import com.mercedesbenz.sechub.sharedkernel.usecases.encryption.UseCaseAdminFetchesEncryptionStatus;
import com.mercedesbenz.sechub.sharedkernel.usecases.encryption.UseCaseAdminStartsEncryptionRotation;
import com.mercedesbenz.sechub.test.ExampleConstants;
import com.mercedesbenz.sechub.test.TestIsNecessaryForDocumentation;
import com.mercedesbenz.sechub.test.TestPortProvider;

@RunWith(SpringRunner.class)
@WebMvcTest(JobAdministrationRestController.class)
@ContextConfiguration(classes = { EncryptionAdministrationRestController.class, SecHubEncryptionDataValidator.class,
EncryptionAdministrationRestControllerRestDocTest.SimpleTestConfiguration.class })
@WithMockUser(roles = RoleConstants.ROLE_SUPERADMIN)
@ActiveProfiles({ Profiles.TEST, Profiles.ADMIN_ACCESS })
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
public class EncryptionAdministrationRestControllerRestDocTest implements TestIsNecessaryForDocumentation {

private static final int PORT_USED = TestPortProvider.DEFAULT_INSTANCE.getRestDocTestPort();

@Autowired
private MockMvc mockMvc;

@MockBean
AdministrationEncryptionRotationService encryptionRotationService;

@MockBean
AdministrationEncryptionStatusService encryptionStatusService;

@Before
public void before() {

}

@Test
@UseCaseRestDoc(useCase = UseCaseAdminStartsEncryptionRotation.class)
public void restdoc_admin_starts_encryption_rotation() throws Exception {

/* prepare */

SecHubEncryptionData data = new SecHubEncryptionData();
data.setAlgorithm(SecHubCipherAlgorithm.AES_GCM_SIV_256);
data.setPasswordSourceType(SecHubCipherPasswordSourceType.ENVIRONMENT_VARIABLE);
data.setPasswordSourceData("SECRET_1");

String apiEndpoint = https(PORT_USED).buildAdminStartsEncryptionRotation();
Class<? extends Annotation> useCase = UseCaseAdminStartsEncryptionRotation.class;

/* execute + test @formatter:off */

this.mockMvc.perform(
post(apiEndpoint).
contentType(MediaType.APPLICATION_JSON_VALUE).
content(data.toFormattedJSON()).
header(AuthenticationHelper.HEADER_NAME, AuthenticationHelper.getHeaderValue())
).
andExpect(status().isOk()).
andDo(defineRestService().
with().
useCaseData(useCase).
tag(extractTag(apiEndpoint)).
and().
document(
requestHeaders(

)
));

/* @formatter:on */
}

@Test
@UseCaseRestDoc(useCase = UseCaseAdminFetchesEncryptionStatus.class)
public void restdoc_admin_fetches_encryption_status() throws Exception {

/* prepare */

SecHubEncryptionStatus status = createEncryptionStatusExample();

when(encryptionStatusService.fetchStatus()).thenReturn(status);

String apiEndpoint = https(PORT_USED).buildAdminFetchesEncryptionStatus();
Class<? extends Annotation> useCase = UseCaseAdminFetchesEncryptionStatus.class;

/* execute + test @formatter:off */
String domains = SecHubEncryptionStatus.PROPERTY_DOMAINS+"[].";
String domainData = domains+SecHubDomainEncryptionStatus.PROPERTY_DATA+"[].";

this.mockMvc.perform(
get(apiEndpoint).
contentType(MediaType.APPLICATION_JSON_VALUE).
header(AuthenticationHelper.HEADER_NAME, AuthenticationHelper.getHeaderValue())
).
andExpect(status().isOk()).
andDo(defineRestService().
with().
useCaseData(useCase).
tag(extractTag(apiEndpoint)).
responseSchema(OpenApiSchema.ENCRYPTION_STATUS.getSchema()).
and().
document(
requestHeaders(

),
responseFields(
fieldWithPath(SecHubEncryptionStatus.PROPERTY_TYPE).description("The type description of the json content"),
fieldWithPath(domains+SecHubDomainEncryptionStatus.PROPERTY_NAME).description("Name of the domain which will provide this encryption data elements"),
fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_ID).description("Unique identifier"),
fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_ALGORITHM).description("Algorithm used for encryption"),
fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_PASSWORDSOURCE+"."+ PasswordSourceData.PROPERTY_TYPE).description("Type of password source. Can be "+List.of(SecHubCipherPasswordSourceType.values())),
fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_PASSWORDSOURCE+"."+ PasswordSourceData.PROPERTY_DATA).description("Data for password source. If type is "+SecHubCipherPasswordSourceType.ENVIRONMENT_VARIABLE+" then it is the the name of the environment variable."),
fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_USAGE).description("Map containing information about usage of this encryption"),
fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_USAGE+".*").description("Key value data"),
fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_CREATED).description("Creation timestamp"),
fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_CREATED_FROM).description("User id of admin who created the encryption entry")
)
));

/* @formatter:on */
}

private SecHubEncryptionStatus createEncryptionStatusExample() {
SecHubEncryptionStatus status = new SecHubEncryptionStatus();
SecHubDomainEncryptionStatus scheduleDomainEncryptionStatus = new SecHubDomainEncryptionStatus();
scheduleDomainEncryptionStatus.setName("schedule");

// create some example domain encryption data like in really
SecHubDomainEncryptionData scheduleDomainEncryptionData = new SecHubDomainEncryptionData();
scheduleDomainEncryptionData.setAlgorithm(SecHubCipherAlgorithm.AES_GCM_SIV_256);
scheduleDomainEncryptionData.setCreated(LocalDateTime.of(2024, 8, 1, 9, 26));
scheduleDomainEncryptionData.setCreatedFrom("admin-username");
scheduleDomainEncryptionData.setId("1");
scheduleDomainEncryptionData.getPasswordSource().setType(SecHubCipherPasswordSourceType.ENVIRONMENT_VARIABLE);
scheduleDomainEncryptionData.getPasswordSource().setData("SECRET_1");

long value = 1;
for (ExecutionState state : ExecutionState.values()) {
scheduleDomainEncryptionData.getUsage().put("job.state." + state.name().toLowerCase(), value++);
}

scheduleDomainEncryptionStatus.getData().add(scheduleDomainEncryptionData);
status.getDomains().add(scheduleDomainEncryptionStatus);
return status;
}

@EnableAutoConfiguration
public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ public void restdoc_restart_job_hard() throws Exception {
}

// see
// https://docs.spring.io/spring-restdocs/docs/current/reference/html5/#documenting-your-api-request-response-payloads-fields-json
// https://docs.spring.io/spring-restdocs/docs/current/reference/htmlsingle/#documenting-your-api-request-response-payloads-fields-json
private static String inArray(String field) {
return "[]." + field;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ enum OpenApiSchema {

PROJECT_JOB_LIST("ProjectJobList"),

ENCRYPTION_STATUS("EncryptionStatus"),

;

private final Schema schema;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1311,12 +1311,12 @@ public String tryToCreateJobByJson(TestProject project, String sechubConfigAsStr

public String rotateEncryption(SecHubEncryptionData data) {

String url = getUrlBuilder().buildRotateEncryption();
String url = getUrlBuilder().buildAdminStartsEncryptionRotation();
return getRestHelper().postJson(url, data.toFormattedJSON());
}

public SecHubEncryptionStatus fetchEncryptionStatus() {
String url = getUrlBuilder().buildFetchEncryptionStatus();
String url = getUrlBuilder().buildAdminFetchesEncryptionStatus();
String json = getRestHelper().getJSON(url);
return SecHubEncryptionStatus.fromString(json);
}
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
Loading

0 comments on commit 2fed3a2

Please sign in to comment.