Skip to content

Commit

Permalink
define subtype for compliance & mitigation
Browse files Browse the repository at this point in the history
verinice-veo#3073
  • Loading branch information
UrsZeidler committed Sep 20, 2024
1 parent fdad36b commit a89a719
Show file tree
Hide file tree
Showing 13 changed files with 285 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import jakarta.validation.constraints.NotNull;

import org.veo.adapter.presenter.api.common.IdRef;
import org.veo.core.entity.ControlImplementationConfiguration;
import org.veo.core.entity.Domain;
import org.veo.core.entity.DomainTemplate;
import org.veo.core.entity.decision.Decision;
Expand Down Expand Up @@ -80,6 +81,9 @@ public abstract class AbstractDomainDto extends AbstractVersionedSelfReferencing

private Map<String, Decision> decisions;

@Schema(description = "Defines the relevant subtype for mitigation and/or compliance controls.")
private ControlImplementationConfiguration controlImplementationConfiguration;

@Valid
@Schema(
description = "The definitions of domain-specific element properties",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,8 @@ public FullDomainDto transformDomain2Dto(@Valid Domain source) {
source.getElementTypeDefinitions().stream()
.collect(toMap(ElementTypeDefinition::getElementType, this::mapElementTypeDefinition)));

target.setControlImplementationConfiguration(source.getControlImplementationConfiguration());

mapVersionedSelfReferencingProperties(source, target);
mapNameableProperties(source, target);
target.setRiskDefinitions(Map.copyOf(source.getRiskDefinitions()));
Expand Down Expand Up @@ -420,6 +422,7 @@ private void mapDomain(DomainBase source, ExportDomainTemplateDto target) {
target.setDecisions(Map.copyOf(source.getDecisions()));
target.setInspections(Map.copyOf(source.getInspections()));
target.setIncarnationConfiguration(source.getIncarnationConfiguration());
target.setControlImplementationConfiguration(source.getControlImplementationConfiguration());
target.setRiskDefinitions(Map.copyOf(source.getRiskDefinitions()));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.veo.adapter.presenter.api.dto.ElementTypeDefinitionDto;
import org.veo.adapter.presenter.api.response.IdentifiableDto;
import org.veo.core.entity.CatalogItem;
import org.veo.core.entity.ControlImplementationConfiguration;
import org.veo.core.entity.DomainBase;
import org.veo.core.entity.IncarnationConfiguration;
import org.veo.core.entity.ProfileState;
Expand Down Expand Up @@ -65,6 +66,9 @@ public class ExportDomainTemplateDto extends AbstractDomainTemplateDto

private IncarnationConfiguration incarnationConfiguration = new IncarnationConfiguration();

private ControlImplementationConfiguration controlImplementationConfiguration =
new ControlImplementationConfiguration();

@Override
@JsonIgnore
public UUID getSelfId() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*******************************************************************************
* verinice.veo
* Copyright (C) 2024 Urs Zeidler
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package org.veo.core.entity;

import jakarta.validation.constraints.Size;

import javax.annotation.Nullable;

import org.veo.core.entity.aspects.ElementDomainAssociation;

public record ControlImplementationConfiguration(
@Nullable @Size(max = ElementDomainAssociation.SUB_TYPE_MAX_LENGTH)
String complianceControlSubType,
@Nullable @Size(max = ElementDomainAssociation.SUB_TYPE_MAX_LENGTH)
String mitigationControlSubType) {
public ControlImplementationConfiguration() {
this(null, null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ default Map<String, ElementTypeDefinitionState> getElementTypeDefinitionStates()

void setIncarnationConfiguration(IncarnationConfiguration incarnationConfiguration);

void setControlImplementationConfiguration(
ControlImplementationConfiguration controlImplementationConfiguration);

default Optional<ElementTypeDefinition> findElementTypeDefinition(String type) {
return getElementTypeDefinitions().stream()
.filter(d -> d.getElementType().equals(type))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import jakarta.validation.constraints.NotNull;

import org.veo.core.entity.CatalogItem;
import org.veo.core.entity.ControlImplementationConfiguration;
import org.veo.core.entity.DomainBase;
import org.veo.core.entity.IncarnationConfiguration;
import org.veo.core.entity.ProfileState;
Expand Down Expand Up @@ -52,6 +53,8 @@ public interface DomainBaseState extends EntityState {
*/
IncarnationConfiguration getIncarnationConfiguration();

ControlImplementationConfiguration getControlImplementationConfiguration();

String getTemplateVersion();

Set<TemplateItemState<CatalogItem, DomainBase>> getCatalogItemStates();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*******************************************************************************
* verinice.veo
* Copyright (C) 2024 Jonas Jordan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package org.veo.core.usecase.domain;

import java.time.Instant;
import java.util.Optional;
import java.util.UUID;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;

import org.veo.core.entity.Client;
import org.veo.core.entity.Control;
import org.veo.core.entity.ControlImplementationConfiguration;
import org.veo.core.entity.Key;
import org.veo.core.entity.definitions.ElementTypeDefinition;
import org.veo.core.repository.DomainRepository;
import org.veo.core.usecase.TransactionalUseCase;
import org.veo.core.usecase.UseCase;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class SaveControlImplementationConfigurationUseCase
implements TransactionalUseCase<
SaveControlImplementationConfigurationUseCase.InputData, UseCase.EmptyOutput> {

private final DomainRepository domainRepository;

@Override
public EmptyOutput execute(InputData input) {
var domain = domainRepository.getActiveById(input.domainId, input.authenticatedClient.getId());
validate(
domain.getElementTypeDefinition(Control.SINGULAR_TERM),
input.controlImplementationConfiguration);
domain.setControlImplementationConfiguration(input.controlImplementationConfiguration);
domain.setUpdatedAt(Instant.now());
return EmptyOutput.INSTANCE;
}

private void validate(
ElementTypeDefinition elementTypeDefinition,
@NotNull ControlImplementationConfiguration controlImplementationConfiguration) {
Optional.ofNullable(controlImplementationConfiguration.complianceControlSubType())
.ifPresent(elementTypeDefinition::getSubTypeDefinition);
Optional.ofNullable(controlImplementationConfiguration.mitigationControlSubType())
.ifPresent(elementTypeDefinition::getSubTypeDefinition);
}

@Override
public boolean isReadOnly() {
return false;
}

@Valid
public record InputData(
@NotNull Client authenticatedClient,
@NotNull Key<UUID> domainId,
@NotNull ControlImplementationConfiguration controlImplementationConfiguration)
implements UseCase.InputData {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ private void map(DomainBaseState source, DomainBase target, boolean copyProfiles
target.setDecisions(source.getDecisions());
target.setInspections(source.getInspections());
target.setIncarnationConfiguration(source.getIncarnationConfiguration());
target.setControlImplementationConfiguration(source.getControlImplementationConfiguration());

// Create all catalog items and register them in the resolver before mapping them, because they
// may reference each other.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*******************************************************************************
* verinice.veo
* Copyright (C) 2023 Jonas Jordan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package org.veo.persistence.migrations

import org.flywaydb.core.api.migration.BaseJavaMigration
import org.flywaydb.core.api.migration.Context

import groovy.sql.Sql

class V96__add_controlImplementation_configuration extends BaseJavaMigration {
@Override
void migrate(Context context) throws Exception {
new Sql(context.connection).with {
migrateTable("domain", it)
migrateTable("domaintemplate", it)
}
}

private static boolean migrateTable(String table, Sql context) {
context.execute("""
alter table $table
add column control_implementation_configuration jsonb;
update $table
set control_implementation_configuration =
CASE WHEN name = 'IT-Grundschutz'
THEN '{"complianceControlSubType": "CTL_Module" ,"mitigationControlSubType": "CTL_Safeguard" }'::jsonb
WHEN name = 'DS-GVO'
THEN '{"mitigationControlSubType": "CTL_TOM" }'::jsonb
WHEN name = 'ISO/IEC 27000'
THEN '{"mitigationControlSubType": "CTL_ISOControl" }'::jsonb
ELSE '{}'::jsonb
END;
alter table $table
alter column control_implementation_configuration set not null;
""".toString())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

import org.hibernate.annotations.Type;

import org.veo.core.entity.ControlImplementationConfiguration;
import org.veo.core.entity.DomainBase;
import org.veo.core.entity.IncarnationConfiguration;
import org.veo.core.entity.Nameable;
Expand Down Expand Up @@ -104,6 +105,11 @@ public abstract class DomainBaseData extends IdentifiableVersionedData
@Type(JsonType.class)
private IncarnationConfiguration incarnationConfiguration = new IncarnationConfiguration();

@NotNull
@Type(JsonType.class)
private ControlImplementationConfiguration controlImplementationConfiguration =
new ControlImplementationConfiguration();

@Override
public Map<String, RiskDefinition> getRiskDefinitions() {
return riskDefinitionSet.getRiskDefinitions();
Expand Down
20 changes: 20 additions & 0 deletions veo-rest/src/main/java/org/veo/rest/ContentCreationController.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
import org.veo.adapter.service.domaintemplate.dto.ExportDomainTemplateDto;
import org.veo.adapter.service.domaintemplate.dto.ExportProfileDto;
import org.veo.core.entity.Client;
import org.veo.core.entity.ControlImplementationConfiguration;
import org.veo.core.entity.DomainTemplate;
import org.veo.core.entity.EntityType;
import org.veo.core.entity.IncarnationConfiguration;
Expand All @@ -89,6 +90,7 @@
import org.veo.core.usecase.domain.DeleteInspectionUseCase;
import org.veo.core.usecase.domain.DeleteProfileUseCase;
import org.veo.core.usecase.domain.DeleteRiskDefinitionUseCase;
import org.veo.core.usecase.domain.SaveControlImplementationConfigurationUseCase;
import org.veo.core.usecase.domain.SaveDecisionUseCase;
import org.veo.core.usecase.domain.SaveInspectionUseCase;
import org.veo.core.usecase.domain.SaveRiskDefinitionUseCase;
Expand Down Expand Up @@ -123,6 +125,8 @@ public class ContentCreationController extends AbstractVeoController {
private final EntityToDtoTransformer entityToDtoTransformer;
private final UpdateElementTypeDefinitionUseCase updateElementTypeDefinitionUseCase;
private final SaveIncarnationConfigurationUseCase saveIncarnationConfigurationUseCase;
private final SaveControlImplementationConfigurationUseCase
saveControlImplementationConfigurationUseCase;
private final SaveDecisionUseCase saveDecisionUseCase;
private final SaveInspectionUseCase saveInspectionUseCase;
private final SaveRiskDefinitionUseCase saveRiskDefinitionUseCase;
Expand Down Expand Up @@ -241,6 +245,22 @@ public CompletableFuture<ResponseEntity<ApiResponseBody>> saveIncarnationConfigu
empty -> ResponseEntity.noContent().build());
}

@PutMapping("/domains/{domainId}/control-implementation-configuration")
@Operation(summary = "Update domain-specific configuration related to control implementations.")
@ApiResponse(responseCode = "204", description = "Control implementations config updated")
public CompletableFuture<ResponseEntity<ApiResponseBody>> saveControlImplementationConfiguration(
@Parameter(hidden = true) ApplicationUser user,
@Parameter(required = true, example = UUID_EXAMPLE, description = UUID_DESCRIPTION)
@PathVariable
UUID domainId,
@RequestBody ControlImplementationConfiguration controlImplementationConfiguration) {
return useCaseInteractor.execute(
saveControlImplementationConfigurationUseCase,
new SaveControlImplementationConfigurationUseCase.InputData(
getClient(user), Key.from(domainId), controlImplementationConfiguration),
empty -> ResponseEntity.noContent().build());
}

@PutMapping("/domains/{domainId}/decisions/{decisionKey}")
@Operation(summary = "Create or update decision with given key")
@ApiResponse(responseCode = "201", description = "Decision created")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@
import org.veo.core.usecase.domain.GetElementStatusCountUseCase;
import org.veo.core.usecase.domain.GetInspectionUseCase;
import org.veo.core.usecase.domain.GetInspectionsUseCase;
import org.veo.core.usecase.domain.SaveControlImplementationConfigurationUseCase;
import org.veo.core.usecase.domain.SaveDecisionUseCase;
import org.veo.core.usecase.domain.SaveInspectionUseCase;
import org.veo.core.usecase.domain.SaveRiskDefinitionUseCase;
Expand Down Expand Up @@ -275,6 +276,12 @@ public SaveIncarnationConfigurationUseCase saveIncarnationConfigurationUseCase(
return new SaveIncarnationConfigurationUseCase(domainRepository);
}

@Bean
public SaveControlImplementationConfigurationUseCase
saveControlImplementationConfigurationUseCase(DomainRepository domainRepository) {
return new SaveControlImplementationConfigurationUseCase(domainRepository);
}

@Bean
public GetProfilesUseCase getProfilesUseCase(ProfileRepository profileRepository) {
return new GetProfilesUseCase(profileRepository);
Expand Down
Loading

0 comments on commit a89a719

Please sign in to comment.