From 0fb4867ee050806de04a54302c80f6c4575737df Mon Sep 17 00:00:00 2001 From: deiflaender Date: Thu, 2 Dec 2021 10:33:33 +0100 Subject: [PATCH] RED-2900: Added missing migration of digital signature --- .../MigrationEncryptionDecryptionService.java | 49 +++++ .../v1/server/migration/MigrationService.java | 197 +++++++++++++++--- .../model/DigitalSignatureModel.java | 22 ++ .../integration/tests/MigrationTest.java | 12 ++ .../migration/configuration_table_v1.json | 7 +- 5 files changed, 253 insertions(+), 34 deletions(-) create mode 100644 persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/MigrationEncryptionDecryptionService.java create mode 100644 persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/model/DigitalSignatureModel.java diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/MigrationEncryptionDecryptionService.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/MigrationEncryptionDecryptionService.java new file mode 100644 index 000000000..6b8f7f525 --- /dev/null +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/MigrationEncryptionDecryptionService.java @@ -0,0 +1,49 @@ +package com.iqser.red.service.peristence.v1.server.migration; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.util.Arrays; +import java.util.Base64; + +import javax.annotation.PostConstruct; +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import lombok.SneakyThrows; + +@Service +public class MigrationEncryptionDecryptionService { + + @Value("${configuration-service.crypto.key:redaction}") + private String key; + private SecretKeySpec secretKey; + + @SneakyThrows + @PostConstruct + protected void postConstruct() { + byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); + var sha = MessageDigest.getInstance("SHA-1"); + keyBytes = sha.digest(keyBytes); + keyBytes = Arrays.copyOf(keyBytes, 16); + secretKey = new SecretKeySpec(keyBytes, "AES"); + } + + @SneakyThrows + public String encrypt(String strToEncrypt) { + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes(StandardCharsets.UTF_8))); + } + + @SneakyThrows + public String decrypt(String strToDecrypt) { + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING"); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)), StandardCharsets.UTF_8); + } + + +} diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/MigrationService.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/MigrationService.java index 05f5c1388..88d11f8dc 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/MigrationService.java +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/MigrationService.java @@ -1,42 +1,127 @@ package com.iqser.red.service.peristence.v1.server.migration; +import static com.iqser.red.service.persistence.management.v1.processor.utils.TypeIdUtils.toTypeId; +import static java.util.stream.Collectors.toList; + +import java.time.OffsetDateTime; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.iqser.red.service.peristence.v1.server.exception.MigrationException; -import com.iqser.red.service.peristence.v1.server.migration.model.*; +import com.iqser.red.service.peristence.v1.server.migration.model.CommentRow; +import com.iqser.red.service.peristence.v1.server.migration.model.ConfigurationRow; +import com.iqser.red.service.peristence.v1.server.migration.model.DigitalSignatureModel; +import com.iqser.red.service.peristence.v1.server.migration.model.DossierAttributeRow; +import com.iqser.red.service.peristence.v1.server.migration.model.DossierAttributesConfig; +import com.iqser.red.service.peristence.v1.server.migration.model.EntryRow; +import com.iqser.red.service.peristence.v1.server.migration.model.FileAttributesConfig; +import com.iqser.red.service.peristence.v1.server.migration.model.IdRemovalRow; +import com.iqser.red.service.peristence.v1.server.migration.model.ManualForceRedactionRow; +import com.iqser.red.service.peristence.v1.server.migration.model.ManualImageRecategorizationRow; +import com.iqser.red.service.peristence.v1.server.migration.model.ManualLegalBasisChangeRow; +import com.iqser.red.service.peristence.v1.server.migration.model.ManualRedactionEntryRow; +import com.iqser.red.service.peristence.v1.server.migration.model.MigrationAuditModel; +import com.iqser.red.service.peristence.v1.server.migration.model.MigrationComment; +import com.iqser.red.service.peristence.v1.server.migration.model.MigrationDossier; +import com.iqser.red.service.peristence.v1.server.migration.model.MigrationDossierTemplate; +import com.iqser.red.service.peristence.v1.server.migration.model.MigrationDownloadStatus; +import com.iqser.red.service.peristence.v1.server.migration.model.MigrationFileStatus; +import com.iqser.red.service.peristence.v1.server.migration.model.MigrationNotification; +import com.iqser.red.service.peristence.v1.server.migration.model.MigrationRedactionLog; +import com.iqser.red.service.peristence.v1.server.migration.model.MigrationRedactionLogEntry; +import com.iqser.red.service.peristence.v1.server.migration.model.MigrationViewedPage; +import com.iqser.red.service.peristence.v1.server.migration.model.NewIdForDossierTemplate; +import com.iqser.red.service.peristence.v1.server.migration.model.Status; +import com.iqser.red.service.peristence.v1.server.migration.model.VersionRow; +import com.iqser.red.service.peristence.v1.server.migration.model.VersionType; import com.iqser.red.service.peristence.v1.server.service.FileManagementStorageService; import com.iqser.red.service.peristence.v1.server.utils.StorageIdUtils; -import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.*; +import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.AnnotationEntityId; +import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.CommentEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.IdRemovalEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.ManualForceRedactionEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.ManualImageRecategorizationEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.ManualLegalBasisChangeEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.ManualRedactionEntryEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.RectangleEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.ViewedPageEntity; import com.iqser.red.service.persistence.management.v1.processor.entity.audit.AuditEntity; -import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.*; -import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.*; +import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.ColorsEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.DictionaryEntryEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.DigitalSignatureEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.FileAttributesGeneralConfigurationEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.LegalBasisEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.LegalBasisMappingEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.RuleSetEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.TypeEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.WatermarkEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierAttributeEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierTemplateEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileAttributeConfigEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileAttributeEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.ReportTemplateEntity; import com.iqser.red.service.persistence.management.v1.processor.entity.download.DownloadStatusEntity; import com.iqser.red.service.persistence.management.v1.processor.entity.notification.NotificationEntity; import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException; +import com.iqser.red.service.persistence.management.v1.processor.service.EncryptionDecryptionService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileAttributeConfigPersistenceService; -import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.*; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.AuditRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.ColorsRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.CommentRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DigitalSignatureRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierAttributeConfigRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierAttributeRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierTemplateRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DownloadStatusRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.EntryRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileAttributeConfigRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileAttributesGeneralConfigurationRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.ForceRedactionRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.ImageRecategorizationRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.LegalBasisChangeRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.LegalBasisMappingRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.ManualRedactionRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.NotificationRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.RemoveRedactionRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.ReportTemplateRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.RuleSetRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.TypeRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.ViewedPagesRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.WatermarkRepository; import com.iqser.red.service.persistence.service.v1.api.model.annotations.Comment; import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.FileType; import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.ProcessingStatus; import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.WorkflowStatus; -import com.iqser.red.service.redaction.v1.model.*; +import com.iqser.red.service.redaction.v1.model.Change; +import com.iqser.red.service.redaction.v1.model.ChangeType; +import com.iqser.red.service.redaction.v1.model.Engine; +import com.iqser.red.service.redaction.v1.model.Rectangle; +import com.iqser.red.service.redaction.v1.model.RedactionLog; +import com.iqser.red.service.redaction.v1.model.RedactionLogEntry; import com.iqser.red.storage.commons.exception.StorageObjectDoesNotExist; import com.iqser.red.storage.commons.service.StorageService; + import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.time.OffsetDateTime; -import java.time.temporal.ChronoUnit; -import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; - -import static com.iqser.red.service.persistence.management.v1.processor.utils.TypeIdUtils.toTypeId; -import static java.util.stream.Collectors.toList; - @SuppressWarnings("PMD") @Slf4j @@ -79,6 +164,9 @@ public class MigrationService { private final CommentRepository commentRepository; private final StorageService storageService; + private final MigrationEncryptionDecryptionService migrationEncryptionDecryptionService; + private final EncryptionDecryptionService encryptionDecryptionService; + private final DigitalSignatureRepository digitalSignatureRepository; @SneakyThrows @@ -102,7 +190,7 @@ public class MigrationService { dossierTemplates.forEach(dossierTemplate -> { migrateDossierTemplate(dossierTemplate); log.info("Migrated dossierTemplates"); - addValues(dossierAttributesOldIdToNewId,migrateDossierAttributeConfiguration(dossierTemplate.getDossierTemplateId(), configurationRows)); + addValues(dossierAttributesOldIdToNewId, migrateDossierAttributeConfiguration(dossierTemplate.getDossierTemplateId(), configurationRows)); log.info("Migrated dossier attributes config"); addValues(fileAttributesOldIdToNewId, migrateFileAttributeConfiguration(dossierTemplate.getDossierTemplateId(), configurationRows)); log.info("Migrated file attributes config"); @@ -114,9 +202,11 @@ public class MigrationService { log.info("Migrated default colors"); migrateRules(dossierTemplate.getDossierTemplateId(), versions); log.info("Migrated rules"); + }); - log.info("Migrated reportTemplates"); + migrateDigitalSignature(); + log.info("Migrated digital signature"); migrateReportTemplates(); log.info("Migrated reportTemplates"); migrateDossiers(); @@ -161,7 +251,9 @@ public class MigrationService { } - private void addValues(Map> existing, Map> newValues){ + private void addValues(Map> existing, + Map> newValues) { + newValues.entrySet().forEach(entry -> { existing.computeIfAbsent(entry.getKey(), x -> new ArrayList<>()).addAll(entry.getValue()); }); @@ -359,13 +451,11 @@ public class MigrationService { .equals(dossierTemplateId) && versionRow.getVersionType() == VersionType.RULE.ordinal()) .findFirst(); - if(!versionOptional.isPresent()){ + if (!versionOptional.isPresent()) { log.warn("no rules version found for dossier template {}", dossierTemplateId); return; } - var version = versionOptional - .get() - .getVersion(); + var version = versionOptional.get().getVersion(); String rules = new String(fileManagementStorageService.getStoredObjectBytes(String.format("rules/%s-rules.drl", dossierTemplateId))); RuleSetEntity ruleSet = new RuleSetEntity(); @@ -501,7 +591,6 @@ public class MigrationService { .processedDate(redaction.getProcessedDate()) .softDeletedTime(redaction.getSoftDeletedTime()) .fileStatus(file) - .build(); legalBasisChangeRepository.save(manualLegalBasisChange); @@ -530,7 +619,6 @@ public class MigrationService { .processedDate(redaction.getProcessedDate()) .softDeletedTime(redaction.getSoftDeletedTime()) .fileStatus(file) - .build(); removeRedactionRepository.save(idRemoval); @@ -671,7 +759,9 @@ public class MigrationService { break; case REPROCESS: if (oldFile.getLastSuccessfulStatus() != null) { - workflowStatus = oldFile.getLastSuccessfulStatus().equals(Status.UNASSIGNED) ? WorkflowStatus.NEW : WorkflowStatus.valueOf(oldFile.getLastSuccessfulStatus().name()); + workflowStatus = oldFile.getLastSuccessfulStatus() + .equals(Status.UNASSIGNED) ? WorkflowStatus.NEW : WorkflowStatus.valueOf(oldFile.getLastSuccessfulStatus() + .name()); processingStatus = ProcessingStatus.REPROCESS; } else { workflowStatus = WorkflowStatus.NEW; @@ -680,7 +770,9 @@ public class MigrationService { break; case PROCESSING: if (oldFile.getLastSuccessfulStatus() != null) { - workflowStatus = oldFile.getLastSuccessfulStatus().equals(Status.UNASSIGNED) ? WorkflowStatus.NEW : WorkflowStatus.valueOf(oldFile.getLastSuccessfulStatus().name()); + workflowStatus = oldFile.getLastSuccessfulStatus() + .equals(Status.UNASSIGNED) ? WorkflowStatus.NEW : WorkflowStatus.valueOf(oldFile.getLastSuccessfulStatus() + .name()); processingStatus = ProcessingStatus.PROCESSING; } else { workflowStatus = WorkflowStatus.NEW; @@ -689,7 +781,9 @@ public class MigrationService { break; case ERROR: if (oldFile.getLastSuccessfulStatus() != null) { - workflowStatus = oldFile.getLastSuccessfulStatus().equals(Status.UNASSIGNED) ? WorkflowStatus.NEW : WorkflowStatus.valueOf(oldFile.getLastSuccessfulStatus().name()); + workflowStatus = oldFile.getLastSuccessfulStatus() + .equals(Status.UNASSIGNED) ? WorkflowStatus.NEW : WorkflowStatus.valueOf(oldFile.getLastSuccessfulStatus() + .name()); processingStatus = ProcessingStatus.ERROR; } else { workflowStatus = WorkflowStatus.NEW; @@ -698,7 +792,9 @@ public class MigrationService { break; case DELETED: if (oldFile.getLastSuccessfulStatus() != null) { - workflowStatus = oldFile.getLastSuccessfulStatus().equals(Status.UNASSIGNED) ? WorkflowStatus.NEW : WorkflowStatus.valueOf(oldFile.getLastSuccessfulStatus().name()); + workflowStatus = oldFile.getLastSuccessfulStatus() + .equals(Status.UNASSIGNED) ? WorkflowStatus.NEW : WorkflowStatus.valueOf(oldFile.getLastSuccessfulStatus() + .name()); processingStatus = ProcessingStatus.DELETED; } else { workflowStatus = WorkflowStatus.NEW; @@ -723,7 +819,9 @@ public class MigrationService { break; case FULLREPROCESS: if (oldFile.getLastSuccessfulStatus() != null) { - workflowStatus = oldFile.getLastSuccessfulStatus().equals(Status.UNASSIGNED) ? WorkflowStatus.NEW : WorkflowStatus.valueOf(oldFile.getLastSuccessfulStatus().name()); + workflowStatus = oldFile.getLastSuccessfulStatus() + .equals(Status.UNASSIGNED) ? WorkflowStatus.NEW : WorkflowStatus.valueOf(oldFile.getLastSuccessfulStatus() + .name()); processingStatus = ProcessingStatus.FULLREPROCESS; } else { workflowStatus = WorkflowStatus.NEW; @@ -732,7 +830,9 @@ public class MigrationService { break; case OCR_PROCESSING: if (oldFile.getLastSuccessfulStatus() != null) { - workflowStatus = oldFile.getLastSuccessfulStatus().equals(Status.UNASSIGNED) ? WorkflowStatus.NEW : WorkflowStatus.valueOf(oldFile.getLastSuccessfulStatus().name()); + workflowStatus = oldFile.getLastSuccessfulStatus() + .equals(Status.UNASSIGNED) ? WorkflowStatus.NEW : WorkflowStatus.valueOf(oldFile.getLastSuccessfulStatus() + .name()); processingStatus = ProcessingStatus.OCR_PROCESSING; } else { workflowStatus = WorkflowStatus.NEW; @@ -741,7 +841,9 @@ public class MigrationService { break; case INDEXING: if (oldFile.getLastSuccessfulStatus() != null) { - workflowStatus = oldFile.getLastSuccessfulStatus().equals(Status.UNASSIGNED) ? WorkflowStatus.NEW : WorkflowStatus.valueOf(oldFile.getLastSuccessfulStatus().name()); + workflowStatus = oldFile.getLastSuccessfulStatus() + .equals(Status.UNASSIGNED) ? WorkflowStatus.NEW : WorkflowStatus.valueOf(oldFile.getLastSuccessfulStatus() + .name()); processingStatus = ProcessingStatus.INDEXING; } else { workflowStatus = WorkflowStatus.NEW; @@ -970,6 +1072,35 @@ public class MigrationService { } + @SneakyThrows + private void migrateDigitalSignature() { + + byte[] confTableBytes = fileManagementStorageService.getStoredObjectBytes(getMigrationId("configuration_table_v1")); + Map configMap = objectMapper.readValue(confTableBytes, new TypeReference>() { + }); + + if (!configMap.containsKey("DIGITAL_SIGNATURE")) { + return; + } + + var oldEncrypted = configMap.get("DIGITAL_SIGNATURE"); + + var oldSignature = objectMapper.readValue(migrationEncryptionDecryptionService.decrypt(oldEncrypted), DigitalSignatureModel.class); + + DigitalSignatureEntity digitalSignatureEntity = DigitalSignatureEntity.builder() + .location(oldSignature.getLocation()) + .reason(oldSignature.getReason()) + .contactInfo(oldSignature.getContactInfo()) + .certificateName(oldSignature.getCertificateName()) + .build(); + + digitalSignatureEntity.setPrivateKey(encryptionDecryptionService.encrypt(oldSignature.getPrivateKey())); + digitalSignatureEntity.setPassword(encryptionDecryptionService.encrypt(oldSignature.getPassword())); + digitalSignatureRepository.save(digitalSignatureEntity); + + } + + private void migrateWatermark(String dossierTemplateId, List configurationRows) { configurationRows.stream() diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/model/DigitalSignatureModel.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/model/DigitalSignatureModel.java new file mode 100644 index 000000000..e79357ae7 --- /dev/null +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/migration/model/DigitalSignatureModel.java @@ -0,0 +1,22 @@ +package com.iqser.red.service.peristence.v1.server.migration.model; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor +@Builder +public class DigitalSignatureModel { + + private String location; + private String reason; + private String contactInfo; + private String certificateName; + private byte[] privateKey; + private String password; + +} diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/MigrationTest.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/MigrationTest.java index d5548dfa7..ec42750b5 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/MigrationTest.java +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/MigrationTest.java @@ -15,6 +15,7 @@ import org.springframework.core.io.ClassPathResource; import com.iqser.red.service.peristence.v1.server.Application; import com.iqser.red.service.peristence.v1.server.integration.client.AuditClient; import com.iqser.red.service.peristence.v1.server.integration.client.DictionaryClient; +import com.iqser.red.service.peristence.v1.server.integration.client.DigitalSignatureClient; import com.iqser.red.service.peristence.v1.server.integration.client.DossierAttributeClient; import com.iqser.red.service.peristence.v1.server.integration.client.DossierAttributeConfigClient; import com.iqser.red.service.peristence.v1.server.integration.client.DossierClient; @@ -99,6 +100,9 @@ public class MigrationTest extends AbstractPersistenceServerServiceTest { @Autowired private RedactionLogClient redactionLogClient; + @Autowired + private DigitalSignatureClient digitalSignatureClient; + @Test @SneakyThrows @@ -197,6 +201,11 @@ public class MigrationTest extends AbstractPersistenceServerServiceTest { .getInputStream() .readAllBytes()); + ClassPathResource confTable1Json = new ClassPathResource("files/migration/configuration_table_v1.json"); + when(fileManagementStorageService.getStoredObjectBytes(getMigrationId("configuration_table_v1"))).thenReturn(confTable1Json + .getInputStream() + .readAllBytes()); + when(fileManagementStorageService.getStoredObjectBytes(String.format("rules/%s-rules.drl", "6d945ebb-604b-4781-a72b-0f07e8ceb3e6"))) .thenReturn("Rules".getBytes(StandardCharsets.UTF_8)); when(fileManagementStorageService.getStoredObjectBytes(String.format("rules/%s-rules.drl", "49156a1f-30cc-412f-8778-eb5bd842d709"))) @@ -290,6 +299,9 @@ public class MigrationTest extends AbstractPersistenceServerServiceTest { }); assertThat(files.size()).isEqualTo(33); + var digitalSignature = digitalSignatureClient.getDigitalSignature(); + assertThat(digitalSignature.getCertificateName()).isNotEmpty(); + var auditCategories = auditClient.getCategories(); assertThat(auditCategories.size()).isEqualTo(7); diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/resources/files/migration/configuration_table_v1.json b/persistence-service-v1/persistence-service-server-v1/src/test/resources/files/migration/configuration_table_v1.json index c11c24732..b12c6daa4 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/test/resources/files/migration/configuration_table_v1.json +++ b/persistence-service-v1/persistence-service-server-v1/src/test/resources/files/migration/configuration_table_v1.json @@ -1 +1,6 @@ -{"fileAttributes":"{\"filenameMappingColumnHeaderName\":null,\"delimiter\":\",\",\"fileAttributeConfigs\":[]}","legalBasis":"[{\"name\":\"1.\\tnames and addresses of persons\",\"description\":\"names and addresses of persons involved in testing on vertebrate animals\",\"reason\":\"[Reg (EC) No 1107/2009 Art. 63 (2g)]\"},{\"name\":\"2.\\tmethods of analysis for impurities\",\"description\":\"methods of analysis for impurities in the active substance as manufactured except for methods for impurities that are considered to be toxicologically, ecotoxicologically or environmentally relevant\",\"reason\":\"[Reg (EC) No 1107/2009 Art. 63 (2d)]\"},{\"name\":\"3.\\tmethod of manufacture\",\"description\":\"the method of manufacture\",\"reason\":\"[Reg (EC) No 1107/2009 Art. 63 (2a)]\"},{\"name\":\"4.\\tcomposition of a plant protection product\",\"description\":\"information on the complete composition of a plant protection product\",\"reason\":\"[Reg (EC) No 1107/2009 Art. 63 (2f)]\"},{\"name\":\"5.\\tresults of production batches\",\"description\":\"results of production batches of the active substance including impurities\",\"reason\":\"[Reg (EC) No 1107/2009 Art. 63 (2c)]\"},{\"name\":\"6.\\tspecification of impurity of the active substance\",\"description\":\"the specification of impurity of the active substance except for the impurities that are considered to be toxicologically, ecotoxicologically or environmentally relevant\",\"reason\":\"[Reg (EC) No 1107/2009 Art. 63 (2a)]\"},{\"name\":\"7. links between a producer and applicant\",\"description\":\"links between a producer or importer and the applicant or the authorisation holder\",\"reason\":\"[Reg (EC) No 1107/2009 Art. 63 (2e)]\"}]","colors":"{\"defaultColor\":\"#acfc00\",\"requestAdd\":\"#04b093\",\"requestRemove\":\"#04b093\",\"notRedacted\":\"#ff00bb\",\"analysisColor\":\"#dd4d50\",\"updatedColor\":\"#fdbd00\",\"dictionaryRequestColor\":\"#5b97db\",\"manualRedactionColor\":\"#acfc00\",\"previewColor\":\"#cccccc\"}"} \ No newline at end of file +{ + "fileAttributes": "{\"filenameMappingColumnHeaderName\":null,\"delimiter\":\",\",\"fileAttributeConfigs\":[]}", + "DIGITAL_SIGNATURE": "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", + "legalBasis": "[{\"name\":\"1.\\tnames and addresses of persons\",\"description\":\"names and addresses of persons involved in testing on vertebrate animals\",\"reason\":\"[Reg (EC) No 1107/2009 Art. 63 (2g)]\"},{\"name\":\"2.\\tmethods of analysis for impurities\",\"description\":\"methods of analysis for impurities in the active substance as manufactured except for methods for impurities that are considered to be toxicologically, ecotoxicologically or environmentally relevant\",\"reason\":\"[Reg (EC) No 1107/2009 Art. 63 (2d)]\"},{\"name\":\"3.\\tmethod of manufacture\",\"description\":\"the method of manufacture\",\"reason\":\"[Reg (EC) No 1107/2009 Art. 63 (2a)]\"},{\"name\":\"4.\\tcomposition of a plant protection product\",\"description\":\"information on the complete composition of a plant protection product\",\"reason\":\"[Reg (EC) No 1107/2009 Art. 63 (2f)]\"},{\"name\":\"5.\\tresults of production batches\",\"description\":\"results of production batches of the active substance including impurities\",\"reason\":\"[Reg (EC) No 1107/2009 Art. 63 (2c)]\"},{\"name\":\"6.\\tspecification of impurity of the active substance\",\"description\":\"the specification of impurity of the active substance except for the impurities that are considered to be toxicologically, ecotoxicologically or environmentally relevant\",\"reason\":\"[Reg (EC) No 1107/2009 Art. 63 (2a)]\"},{\"name\":\"7. links between a producer and applicant\",\"description\":\"links between a producer or importer and the applicant or the authorisation holder\",\"reason\":\"[Reg (EC) No 1107/2009 Art. 63 (2e)]\"}]", + "colors": "{\"defaultColor\":\"#acfc00\",\"requestAdd\":\"#04b093\",\"requestRemove\":\"#04b093\",\"notRedacted\":\"#ff00bb\",\"analysisColor\":\"#dd4d50\",\"updatedColor\":\"#fdbd00\",\"dictionaryRequestColor\":\"#5b97db\",\"manualRedactionColor\":\"#acfc00\",\"previewColor\":\"#cccccc\"}" +} \ No newline at end of file