diff --git a/config/module.config.php b/config/module.config.php index 6e328a8b..9adcb0b5 100755 --- a/config/module.config.php +++ b/config/module.config.php @@ -1571,15 +1571,23 @@ Service\SoaScaleCommentService::class => AutowireFactory::class, CronTask\Service\CronTaskService::class => AutowireFactory::class, /* Export services. */ - Export\Service\AnrExportService::class => ReflectionBasedAbstractFactory::class, - Export\Service\AssetExportService::class => AutowireFactory::class, + Export\Service\AnrExportService::class => AutowireFactory::class, Export\Service\ObjectExportService::class => AutowireFactory::class, - Export\Service\SoaScaleCommentExportService::class => AutowireFactory::class, - Export\Service\OperationalRiskScalesExportService::class => AutowireFactory::class, + Export\Service\InstanceExportService::class => AutowireFactory::class, /* Import services. */ Import\Service\ObjectImportService::class => AutowireFactory::class, Import\Service\AssetImportService::class => AutowireFactory::class, Import\Service\InstanceImportService::class => AutowireFactory::class, + Import\Processor\AssetImportProcessor::class => AutowireFactory::class, + Import\Processor\InformationRiskImportProcessor::class => AutowireFactory::class, + Import\Processor\ObjectCategoryImportProcessor::class => AutowireFactory::class, + Import\Processor\ObjectImportProcessor::class => AutowireFactory::class, + Import\Processor\OperationalRisksImportProcessor::class => AutowireFactory::class, + Import\Processor\RecommendationImportProcessor::class => AutowireFactory::class, + Import\Processor\ReferentialImportProcessor::class => AutowireFactory::class, + Import\Processor\RolfTagImportProcessor::class => AutowireFactory::class, + Import\Processor\ThreatImportProcessor::class => AutowireFactory::class, + Import\Processor\VulnerabilityImportProcessor::class => AutowireFactory::class, // Helpers Import\Helper\ImportCacheHelper::class => AutowireFactory::class, @@ -1684,10 +1692,8 @@ }, // Commands - Import\Command\ImportAnalysesCommand::class => static function ( - ContainerInterface $container, - $serviceName - ) { + Import\Command\ImportAnalysesCommand::class => static function (ContainerInterface $container) + { /** @var ConnectedUserService $connectedUserService */ $connectedUserService = $container->get(ConnectedUserService::class); $connectedUserService->setConnectedUser(new Entity\User([ @@ -1711,7 +1717,9 @@ 'lazy_services' => [ 'class_map' => [ Table\UserTokenTable::class => Table\UserTokenTable::class, - Service\AnrInstanceService::class => Service\AnrInstanceService::class + Service\AnrInstanceService::class => Service\AnrInstanceService::class, + Import\Processor\ObjectCategoryImportProcessor::class => + Import\Processor\ObjectCategoryImportProcessor::class, ], 'proxies_target_dir' => $dataPath . '/LazyServices/Proxy', 'write_proxy_files' => $env === 'production', diff --git a/migrations/db/20230901112005_fix_positions_cleanup_db.php b/migrations/db/20230901112005_fix_positions_cleanup_db.php index fafaed30..c518a1c8 100644 --- a/migrations/db/20230901112005_fix_positions_cleanup_db.php +++ b/migrations/db/20230901112005_fix_positions_cleanup_db.php @@ -49,41 +49,6 @@ public function change() $previousAnrId = $amvData['anr_id']; } - /* Fix the objects positions. */ - $objectsQuery = $this->query( - 'SELECT uuid, anr_id, object_category_id, position - FROM objects - ORDER BY anr_id, object_category_id, position' - ); - $previousObjectCategoryId = null; - $previousAnrId = null; - $expectedObjectPosition = 1; - foreach ($objectsQuery->fetchAll() as $objectData) { - if ($previousObjectCategoryId === null) { - $previousObjectCategoryId = $objectData['object_category_id']; - $previousAnrId = $objectData['anr_id']; - } - if ($objectData['object_category_id'] !== $previousObjectCategoryId - || $previousAnrId !== $objectData['anr_id'] - ) { - $expectedObjectPosition = 1; - } - - if ($expectedObjectPosition !== $objectData['position']) { - $this->execute( - sprintf( - 'UPDATE objects SET position = %d WHERE uuid = "%s"', - $expectedObjectPosition, - $objectData['uuid'] - ) - ); - } - - $expectedObjectPosition++; - $previousObjectCategoryId = $objectData['object_category_id']; - $previousAnrId = $objectData['anr_id']; - } - /* Fix the objects compositions positions. */ $objectsQuery = $this->query( 'SELECT id, anr_id, father_id, position FROM objects_objects ORDER BY anr_id, father_id, position' @@ -183,6 +148,7 @@ public function change() ->removeColumn('disponibility') ->removeColumn('token_import') ->removeColumn('original_name') + ->removeColumn('position') ->update(); $this->table('instances_consequences')->removeColumn('object_id')->removeColumn('locally_touched')->update(); @@ -282,6 +248,12 @@ public function change() $this->table('measures_amvs') ->changePrimaryKey(['measure_id', 'amv_id', 'anr_id']) ->addForeignKey('measure_id', 'measures', 'id', ['delete' => 'CASCADE', 'update' => 'RESTRICT']) + ->addForeignKey( + ['amv_id', 'anr_id'], + 'amvs', + ['uuid', 'anr_id'], + ['delete' => 'CASCADE', 'update' => 'RESTRICT'] + ) ->update(); $this->table('measures_amvs')->removeColumn('measure_uuid')->update(); /* Apply measures relation to measures_rolf_risks. */ diff --git a/src/Entity/Recommendation.php b/src/Entity/Recommendation.php index 4716221c..0cfcaa5d 100755 --- a/src/Entity/Recommendation.php +++ b/src/Entity/Recommendation.php @@ -203,6 +203,13 @@ public function setDueDate(?DateTime $date): self return $this; } + public function setDueDateFromString(string $dateString): self + { + $this->dueDate = new DateTime($dateString); + + return $this; + } + public function getRecommendationSet(): RecommendationSet { return $this->recommendationSet; diff --git a/src/Export/Service/AnrExportService.php b/src/Export/Service/AnrExportService.php index 2be3aedf..aeee2c57 100644 --- a/src/Export/Service/AnrExportService.php +++ b/src/Export/Service/AnrExportService.php @@ -98,13 +98,13 @@ private function prepareExportData(Entity\Anr $anr, array $exportParams): array 'withSoas' => $withSoas, 'withRecords' => $withRecords, 'withLibrary' => $withLibrary, - 'withKnowledge' => $withKnowledgeBase, + 'withKnowledgeBase' => $withKnowledgeBase, 'languageCode' => $anr->getLanguageCode(), 'languageIndex' => $anr->getLanguage(), 'knowledgeBase' => $withKnowledgeBase ? $this->prepareKnowledgeBaseData($anr, $withEval, $withControls, $withRecommendations) : [], - 'library' => $withLibrary ? $this->prepareLibraryData($anr) : [], + 'library' => $withLibrary ? $this->prepareLibraryData($anr, !$withKnowledgeBase) : [], 'instances' => $this ->prepareInstancesData($anr, !$withLibrary, $withEval, $withControls, $withRecommendations), 'anrInstanceMetadataFields' => $this->prepareAnrInstanceMetadataFieldsData($anr), @@ -141,9 +141,9 @@ private function prepareKnowledgeBaseData( 'threats' => $this->prepareThreatsData($anr, $withEval), 'vulnerabilities' => $this->prepareVulnerabilitiesData($anr), 'referentials' => $withControls ? $this->prepareReferentialsData($anr) : [], - 'informationRisks' => $this->prepareInformationRisksData($anr, $withEval), - 'tags' => $this->prepareTagsData($anr), - 'operationalRisks' => $this->prepareOperationalRisksData($anr), + 'informationRisks' => $this->prepareInformationRisksData($anr, $withEval, $withControls), + 'rolfTags' => $this->prepareRolfTagsData($anr), + 'operationalRisks' => $this->prepareOperationalRisksData($anr, $withControls), 'recommendationSets' => $withRecommendations ? $this->prepareRecommendationSetsData($anr) : [], ]; } @@ -184,18 +184,18 @@ private function prepareVulnerabilitiesData(Entity\Anr $anr): array return $result; } - private function prepareInformationRisksData(Entity\Anr $anr, bool $withEval): array + private function prepareInformationRisksData(Entity\Anr $anr, bool $withEval, bool $withControls): array { $result = []; /** @var Entity\Amv $amv */ foreach ($this->amvTable->findByAnr($anr) as $amv) { - $result[] = $this->prepareInformationRiskData($amv); + $result[] = $this->prepareInformationRiskData($amv, $withEval, $withControls, false); } return $result; } - private function prepareTagsData(Entity\Anr $anr): array + private function prepareRolfTagsData(Entity\Anr $anr): array { $result = []; $languageIndex = $anr->getLanguage(); @@ -211,13 +211,13 @@ private function prepareTagsData(Entity\Anr $anr): array return $result; } - private function prepareOperationalRisksData(Entity\Anr $anr): array + private function prepareOperationalRisksData(Entity\Anr $anr, bool $withControls): array { $result = []; $languageIndex = $anr->getLanguage(); /** @var Entity\RolfRisk $rolfRisk */ foreach ($this->rolfRiskTable->findByAnr($anr) as $rolfRisk) { - $result[] = $this->prepareOperationalRiskData($rolfRisk, $languageIndex); + $result[] = $this->prepareOperationalRiskData($rolfRisk, $languageIndex, $withControls, false); } return $result; @@ -266,51 +266,61 @@ private function prepareRecommendationSetsData(Entity\Anr $anr): array return $result; } - private function prepareLibraryData(Entity\Anr $anr): array + private function prepareLibraryData(Entity\Anr $anr, bool $addRolfRisksInObjects): array { return [ - 'categories' => $this->prepareCategoriesAndObjects($anr), + 'categories' => $this->prepareCategoriesAndObjects($anr, $addRolfRisksInObjects), ]; } - private function prepareCategoriesAndObjects(Entity\Anr $anr): array + private function prepareCategoriesAndObjects(Entity\Anr $anr, bool $addRolfRisksInObjects): array { $result = []; $languageIndex = $anr->getLanguage(); foreach ($this->objectCategoryTable->findRootCategoriesByAnrOrderedByPosition($anr) as $objectCategory) { - $result[] = $this->prepareCategoryData($objectCategory, $languageIndex, true); + $result[] = $this->prepareCategoryData($objectCategory, $languageIndex, true, $addRolfRisksInObjects); } return $result; } - private function prepareChildrenCategoriesData(Entity\ObjectCategory $objectCategory, int $languageIndex): array - { + private function prepareChildrenCategoriesData( + Entity\ObjectCategory $objectCategory, + int $languageIndex, + bool $addRolfRisksInObjects + ): array { $result = []; foreach ($objectCategory->getChildren() as $childObjectCategory) { - $result[] = $this - ->prepareCategoryData($childObjectCategory, $languageIndex, false); + $result[] = $this->prepareCategoryData($childObjectCategory, $languageIndex, false, $addRolfRisksInObjects); } return $result; } - private function prepareObjectsDataOfCategory(Entity\ObjectCategory $objectCategory, int $languageIndex): array - { + private function prepareObjectsDataOfCategory( + Entity\ObjectCategory $objectCategory, + int $languageIndex, + bool $addRolfRisksInObjects + ): array { $result = []; foreach ($objectCategory->getObjects() as $object) { - $result[] = $this->prepareObjectData($object, $languageIndex, false); + $result[] = $this->prepareObjectData($object, $languageIndex, false, false, $addRolfRisksInObjects); } return $result; } - private function prepareCategoryData(Entity\ObjectCategory $objectCategory, int $languageIndex, bool $isRoot): array - { + private function prepareCategoryData( + Entity\ObjectCategory $objectCategory, + int $languageIndex, + bool $isRoot, + bool $addRolfRisksInObjects + ): array { return [ 'label' => $objectCategory->getLabel($languageIndex), - 'children' => $this->prepareChildrenCategoriesData($objectCategory, $languageIndex), - 'objects' => $this->prepareObjectsDataOfCategory($objectCategory, $languageIndex), + 'children' => $this->prepareChildrenCategoriesData($objectCategory, $languageIndex, $addRolfRisksInObjects), + 'objects' => $this->prepareObjectsDataOfCategory($objectCategory, $languageIndex, $addRolfRisksInObjects), + 'position' => $objectCategory->getPosition(), 'isRoot' => $isRoot, ]; } diff --git a/src/Export/Service/InstanceExportService.php b/src/Export/Service/InstanceExportService.php index 09eb3137..d05d27ae 100644 --- a/src/Export/Service/InstanceExportService.php +++ b/src/Export/Service/InstanceExportService.php @@ -71,9 +71,9 @@ private function prepareExportData(Entity\Instance $instance, array $exportParam 'export_datetime' => (new \DateTime())->format('Y-m-d H:i:s'), 'languageCode' => $anr->getLanguageCode(), 'languageIndex' => $anr->getLanguage(), - 'with_eval' => $withEval, - 'with_controls' => $withControls, - 'with_recommendations' => $withRecommendations, + 'withEval' => $withEval, + 'withControls' => $withControls, + 'withRecommendations' => $withRecommendations, 'instance' => $this->prepareInstanceData( $instance, $languageIndex, diff --git a/src/Export/Service/Traits/AssetExportTrait.php b/src/Export/Service/Traits/AssetExportTrait.php index 1eb51a8b..334a1fa2 100644 --- a/src/Export/Service/Traits/AssetExportTrait.php +++ b/src/Export/Service/Traits/AssetExportTrait.php @@ -19,8 +19,15 @@ trait AssetExportTrait 'description' => "string", 'type' => "int", 'status' => "int" - ])] private function prepareAssetData(Entity\Asset $asset, int $languageIndex): array - { + ])] private function prepareAssetData( + Entity\Asset $asset, + int $languageIndex, + bool $includeCompleteData = true + ): array { + if (!$includeCompleteData) { + return ['uuid' => $asset->getUuid()]; + } + return [ 'uuid' => $asset->getUuid(), 'code' => $asset->getCode(), diff --git a/src/Export/Service/Traits/InformationRiskExportTrait.php b/src/Export/Service/Traits/InformationRiskExportTrait.php index fa39c9bb..82d5061a 100644 --- a/src/Export/Service/Traits/InformationRiskExportTrait.php +++ b/src/Export/Service/Traits/InformationRiskExportTrait.php @@ -27,7 +27,8 @@ trait InformationRiskExportTrait ])] private function prepareInformationRiskData( Entity\Amv $amv, bool $withEval = false, - bool $withControls = true + bool $withControls = true, + bool $includeCompleteRelationData = true ): array { /** @var Entity\Asset $asset */ $asset = $amv->getAsset(); @@ -40,15 +41,21 @@ trait InformationRiskExportTrait $measuresData = []; if ($withControls) { foreach ($amv->getMeasures() as $measure) { - $measuresData[] = $this->prepareMeasureData($measure, $languageIndex); + $measuresData[] = $this->prepareMeasureData( + $measure, + $languageIndex, + false, + $includeCompleteRelationData + ); } } return [ 'uuid' => $amv->getUuid(), - 'asset' => $this->prepareAssetData($asset, $languageIndex), - 'threat' => $this->prepareThreatData($threat, $languageIndex, $withEval), - 'vulnerability' => $this->prepareVulnerabilityData($vulnerability, $languageIndex), + 'asset' => $this->prepareAssetData($asset, $languageIndex, $includeCompleteRelationData), + 'threat' => $this->prepareThreatData($threat, $languageIndex, $withEval, $includeCompleteRelationData), + 'vulnerability' => $this + ->prepareVulnerabilityData($vulnerability, $languageIndex, $includeCompleteRelationData), 'measures' => $measuresData, 'status' => $amv->getStatus(), ]; diff --git a/src/Export/Service/Traits/InstanceExportTrait.php b/src/Export/Service/Traits/InstanceExportTrait.php index 34e9b7c4..6502f368 100644 --- a/src/Export/Service/Traits/InstanceExportTrait.php +++ b/src/Export/Service/Traits/InstanceExportTrait.php @@ -45,7 +45,7 @@ private function prepareInstanceData( 'integrityInherited' => $withEval ? (int)$instance->isIntegrityInherited() : 1, 'availabilityInherited' => $withEval ? (int)$instance->isAvailabilityInherited() : 1, 'asset' => $this->prepareAssetData($asset, $languageIndex), - /* For Anr and Instance export instanceRisks are added to the instance, so not needed in asset. */ + /* For Anr and Instance export instanceRisks are added to the instance, so not needed in AMVs in asset. */ 'object' => $includeCompleteObjectData ? $this->prepareObjectData($object, $languageIndex, false) : ['uuid' => $instance->getObject()->getUuid()], diff --git a/src/Export/Service/Traits/MeasureExportTrait.php b/src/Export/Service/Traits/MeasureExportTrait.php index 9920e31d..f69baa4e 100644 --- a/src/Export/Service/Traits/MeasureExportTrait.php +++ b/src/Export/Service/Traits/MeasureExportTrait.php @@ -21,8 +21,13 @@ trait MeasureExportTrait ])] private function prepareMeasureData( Entity\Measure $measure, int $languageIndex, - bool $includeLinks = false + bool $includeLinks = false, + bool $includeCompleteData = true ): array { + if (!$includeCompleteData) { + return ['uuid' => $measure->getUuid()]; + } + $result = [ 'uuid' => $measure->getUuid(), 'code' => $measure->getCode(), diff --git a/src/Export/Service/Traits/ObjectExportTrait.php b/src/Export/Service/Traits/ObjectExportTrait.php index cc3764d4..10053f54 100644 --- a/src/Export/Service/Traits/ObjectExportTrait.php +++ b/src/Export/Service/Traits/ObjectExportTrait.php @@ -17,12 +17,12 @@ trait ObjectExportTrait private function prepareObjectData( Entity\MonarcObject $object, int $languageIndex, - bool $addAmvsToAssetData + bool $addAmvsToAssetData, + bool $includeCategory = true, + bool $addRolfRisksInObjects = true ): array { /** @var Entity\ObjectCategory $objectCategory */ $objectCategory = $object->getCategory(); - /** @var Entity\RolfTag $rolfTag */ - $rolfTag = $object->getRolfTag(); /** @var Entity\Asset $asset */ $asset = $object->getAsset(); $assetData = $this->prepareAssetData($asset, $languageIndex); @@ -32,26 +32,42 @@ private function prepareObjectData( $assetData['informationRisks'][] = $this->prepareInformationRiskData($amv); } } + $rolfTagData = null; + if ($object->getRolfTag() !== null) { + /** @var Entity\RolfTag $rolfTag */ + $rolfTag = $object->getRolfTag(); + $rolfTagData = [ + 'id' => $rolfTag->getId(), + 'code' => $rolfTag->getCode(), + 'label' => $rolfTag->getLabel($languageIndex), + ]; + if ($addRolfRisksInObjects) { + $rolfTagData['rolfRisks'] = $this->prepareRolfRisksData($rolfTag); + } + } - return [ + $result = [ 'uuid' => $object->getUuid(), 'name' => $object->getName($languageIndex), 'label' => $object->getLabel($languageIndex), 'mode' => $object->getMode(), 'scope' => $object->getScope(), - 'position' => $object->getPosition(), 'asset' => $assetData, - 'rolfTag' => $rolfTag !== null ? [ - 'id' => $rolfTag->getId(), - 'code' => $rolfTag->getCode(), - 'label' => $rolfTag->getLabel($languageIndex), - 'rolfRisks' => $this->prepareRolfRisksData($rolfTag), - ] : [], - 'category' => $objectCategory !== null ? $this->prepareCategoryAndParentsData($objectCategory) : null, - 'children' => $object->hasChildren() - ? $this->prepareChildrenObjectsData($object, $languageIndex, $addAmvsToAssetData) - : [], + 'rolfTag' => $rolfTagData, + 'children' => $object->hasChildren() ? $this->prepareChildrenObjectsData( + $object, + $languageIndex, + $addAmvsToAssetData, + $addRolfRisksInObjects + ) : [], ]; + if ($includeCategory) { + $result['category'] = $objectCategory !== null + ? $this->prepareCategoryAndParentsData($objectCategory) + : null; + } + + return $result; } private function prepareCategoryAndParentsData(Entity\ObjectCategory $objectCategory): array @@ -70,14 +86,15 @@ private function prepareCategoryAndParentsData(Entity\ObjectCategory $objectCate private function prepareChildrenObjectsData( Entity\MonarcObject $object, int $languageIndex, - bool $addAmvsToAssetData + bool $addAmvsToAssetData, + bool $addRolfRisksInObjects ): array { $result = []; foreach ($object->getChildrenLinks() as $childLink) { /** @var Entity\MonarcObject $childObject */ $childObject = $childLink->getChild(); $result[] = $this - ->prepareObjectData($childObject, $languageIndex, $addAmvsToAssetData); + ->prepareObjectData($childObject, $languageIndex, $addAmvsToAssetData, true, $addRolfRisksInObjects); } return $result; diff --git a/src/Export/Service/Traits/OperationalRiskExportTrait.php b/src/Export/Service/Traits/OperationalRiskExportTrait.php index 174941e7..2d275f6a 100644 --- a/src/Export/Service/Traits/OperationalRiskExportTrait.php +++ b/src/Export/Service/Traits/OperationalRiskExportTrait.php @@ -24,12 +24,18 @@ trait OperationalRiskExportTrait ])] private function prepareOperationalRiskData( Entity\RolfRisk $rolfRisk, int $languageIndex, - bool $withControls = true + bool $withControls = true, + bool $includeCompleteRelationData = true ): array { $measuresData = []; if ($withControls) { foreach ($rolfRisk->getMeasures() as $measure) { - $measuresData[] = $this->prepareMeasureData($measure, $languageIndex); + $measuresData[] = $this->prepareMeasureData( + $measure, + $languageIndex, + false, + $includeCompleteRelationData + ); } } $rolfTagsData = []; diff --git a/src/Export/Service/Traits/RecommendationExportTrait.php b/src/Export/Service/Traits/RecommendationExportTrait.php index 4a62fc41..0f2e3122 100644 --- a/src/Export/Service/Traits/RecommendationExportTrait.php +++ b/src/Export/Service/Traits/RecommendationExportTrait.php @@ -24,7 +24,7 @@ private function prepareRecommendationData( 'status' => $recommendation->getStatus(), 'responsible' => $recommendation->getResponsible(), 'duedate' => $recommendation->getDueDate()?->format('Y-m-d'), - 'counterTreated' => $recommendation->getCode(), + 'counterTreated' => $recommendation->getCounterTreated(), ]; if ($includeRecommendationSetData) { $result['recommendationSet'] = [ diff --git a/src/Export/Service/Traits/ThreatExportTrait.php b/src/Export/Service/Traits/ThreatExportTrait.php index b74a41c9..4ddd7254 100644 --- a/src/Export/Service/Traits/ThreatExportTrait.php +++ b/src/Export/Service/Traits/ThreatExportTrait.php @@ -12,8 +12,16 @@ trait ThreatExportTrait { - #[Pure] private function prepareThreatData(Entity\Threat $threat, int $languageIndex, bool $withEval): array - { + #[Pure] private function prepareThreatData( + Entity\Threat $threat, + int $languageIndex, + bool $withEval, + bool $includeCompleteData = true + ): array { + if (!$includeCompleteData) { + return ['uuid' => $threat->getUuid()]; + } + return [ 'uuid' => $threat->getUuid(), 'label' => $threat->getLabel($languageIndex), diff --git a/src/Export/Service/Traits/VulnerabilityExportTrait.php b/src/Export/Service/Traits/VulnerabilityExportTrait.php index 8b3f608b..6fd06af6 100644 --- a/src/Export/Service/Traits/VulnerabilityExportTrait.php +++ b/src/Export/Service/Traits/VulnerabilityExportTrait.php @@ -18,8 +18,15 @@ trait VulnerabilityExportTrait 'description' => "string", 'code' => "null|string", 'status' => "int" - ])] private function prepareVulnerabilityData(Entity\Vulnerability $vulnerability, int $languageIndex): array - { + ])] private function prepareVulnerabilityData( + Entity\Vulnerability $vulnerability, + int $languageIndex, + bool $includeCompleteData = true + ): array { + if (!$includeCompleteData) { + return ['uuid' => $vulnerability->getUuid()]; + } + return [ 'uuid' => $vulnerability->getUuid(), 'label' => $vulnerability->getLabel($languageIndex), diff --git a/src/Import/Controller/ApiAnrInstancesImportController.php b/src/Import/Controller/ApiAnrInstancesImportController.php index 98fa2df9..6fabe10a 100755 --- a/src/Import/Controller/ApiAnrInstancesImportController.php +++ b/src/Import/Controller/ApiAnrInstancesImportController.php @@ -1,45 +1,39 @@ instanceImportService = $instanceImportService; $this->importConfig = $configService->getConfigOption('import') ? : []; - $this->cronTaskService = $cronTaskService; - $this->anrTable = $anrTable; } /** @@ -47,25 +41,22 @@ public function __construct( */ public function getList() { - $anrId = (int)$this->params()->fromRoute('anrid'); - $anr = $this->anrTable->findById($anrId); + /** @var Anr $anr */ + $anr = $this->getRequest()->getAttribute('anr'); - return new JsonModel([ + return $this->getPreparedJsonResponse([ 'status' => $anr->getStatusName(), 'messages' => $this->cronTaskService->getResultMessagesByNameWithParam( CronTask::NAME_INSTANCE_IMPORT, - ['anrId' => $anrId] + ['anrId' => $anr->getId()] ), ]); } public function create($data) { - $anrId = (int)$this->params()->fromRoute('anrid'); - if (empty($anrId)) { - throw new Exception('Anr id missing', 412); - } - + /** @var Anr $anr */ + $anr = $this->getRequest()->getAttribute('anr'); $files = $this->params()->fromFiles('file'); if (empty($files)) { throw new Exception('File missing', 412); @@ -73,7 +64,9 @@ public function create($data) $data['file'] = $files; if (!empty($this->importConfig['isBackgroundProcessActive'])) { + // TODO: move it to a service /* Upload file to process it later. */ + $anrId = $anr->getId(); $tmpFile = current($files); $fileName = $anrId . '-' . $tmpFile['name']; $fileNameWithPath = $this->moveTmpFile($tmpFile, $this->importConfig['uploadFolder'], $fileName); @@ -94,22 +87,18 @@ public function create($data) ); /* Set Anr status to pending. */ - $this->anrTable->save( - $this->anrTable->findById($anrId)->setStatus(AnrSuperClass::STATUS_AWAITING_OF_IMPORT) - ); + $this->anrTable->save($anr->setStatus(AnrSuperClass::STATUS_AWAITING_OF_IMPORT)); - return new JsonModel([ - 'status' => 'ok', + return $this->getSuccessfulJsonResponse([ 'isBackgroundProcess' => true, 'id' => [], 'errors' => [], ]); } - [$ids, $errors] = $this->instanceImportService->importFromFile($anrId, $data); + [$ids, $errors] = $this->instanceImportService->importFromFile($anr, $data); - return new JsonModel([ - 'status' => 'ok', + return $this->getSuccessfulJsonResponse([ 'isBackgroundProcess' => false, 'id' => $ids, 'errors' => $errors, @@ -123,15 +112,12 @@ public function create($data) */ public function deleteList($data) { - $anrId = (int)$this->params()->fromRoute('anrid'); - if (empty($anrId)) { - throw new Exception('Anr id missing', 412); - } + /** @var Anr $anr */ + $anr = $this->getRequest()->getAttribute('anr'); if (!empty($this->importConfig['isBackgroundProcessActive'])) { $importCronTask = $this->cronTaskService - ->getLatestTaskByNameWithParam(CronTask::NAME_INSTANCE_IMPORT, ['anrId' => $anrId]); - $anr = $this->anrTable->findById($anrId); + ->getLatestTaskByNameWithParam(CronTask::NAME_INSTANCE_IMPORT, ['anrId' => $anr->getId()]); if ($importCronTask !== null && !$anr->isActive()) { $anr->setStatus(AnrSuperClass::STATUS_ACTIVE); $this->anrTable->save($anr, false); @@ -139,6 +125,6 @@ public function deleteList($data) } } - return new JsonModel(['status' => 'ok']); + return $this->getSuccessfulJsonResponse(); } } diff --git a/src/Import/Controller/ApiAnrObjectsImportController.php b/src/Import/Controller/ApiAnrObjectsImportController.php index fe35e7d6..b34b2377 100755 --- a/src/Import/Controller/ApiAnrObjectsImportController.php +++ b/src/Import/Controller/ApiAnrObjectsImportController.php @@ -7,19 +7,17 @@ namespace Monarc\FrontOffice\Import\Controller; -use Laminas\Mvc\Controller\AbstractRestfulController; -use Laminas\View\Model\JsonModel; +use Monarc\Core\Controller\Handler\AbstractRestfulControllerRequestHandler; +use Monarc\Core\Controller\Handler\ControllerRequestResponseHandlerTrait; use Monarc\Core\Exception\Exception; use Monarc\FrontOffice\Import\Service\ObjectImportService; -class ApiAnrObjectsImportController extends AbstractRestfulController +class ApiAnrObjectsImportController extends AbstractRestfulControllerRequestHandler { - private ObjectImportService $objectImportService; + use ControllerRequestResponseHandlerTrait; - public function __construct(ObjectImportService $objectImportService) + public function __construct(private ObjectImportService $objectImportService) { - $this->anrObjectService = $anrObjectService; - $this->objectImportService = $objectImportService; } public function getList() @@ -32,7 +30,7 @@ public function getList() $objects = $this->anrObjectService->getCommonObjects($anrId, $filter); - return new JsonModel([ + return $this->getPreparedJsonResponse([ 'count' => \count($objects), 'objects' => $objects, ]); @@ -50,7 +48,7 @@ public function get($id) $this->formatDependencies($object, ['asset', 'category', 'rolfTag']); unset($object['anrs']); - return new JsonModel($object); + return $this->getPreparedJsonResponse($object); } public function create($data) @@ -68,8 +66,7 @@ public function create($data) [$ids, $errors] = $this->objectImportService->importFromFile($anrId, $data); - return new JsonModel([ - 'status' => 'ok', + return $this->getSuccessfulJsonResponse([ 'id' => $ids, 'errors' => $errors, ]); @@ -88,8 +85,7 @@ public function patch($id, $data) throw new Exception('An error occurred during the import of the object.', 412); } - return new JsonModel([ - 'status' => 'ok', + return $this->getSuccessfulJsonResponse([ 'id' => $monarcObject->getUuid(), ]); } diff --git a/src/Import/Helper/ImportCacheHelper.php b/src/Import/Helper/ImportCacheHelper.php index e519ff29..533f94a6 100644 --- a/src/Import/Helper/ImportCacheHelper.php +++ b/src/Import/Helper/ImportCacheHelper.php @@ -21,11 +21,13 @@ public function isItemInArrayCache(string $cacheKey, $itemKey): bool return isset($this->arrayCache[$cacheKey][$itemKey]); } - public function addItemToArrayCache( - string $cacheKey, - $value, - $itemKey = null - ): void { + public function addItemsToArrayCache(string $cacheKey, array $values): void + { + $this->arrayCache[$cacheKey] = $values; + } + + public function addItemToArrayCache(string $cacheKey, $value, $itemKey = null): void + { if ($itemKey === null) { $this->arrayCache[$cacheKey][] = $value; } else { diff --git a/src/Import/Processor/AssetImportProcessor.php b/src/Import/Processor/AssetImportProcessor.php index 1325282e..2d22a09e 100644 --- a/src/Import/Processor/AssetImportProcessor.php +++ b/src/Import/Processor/AssetImportProcessor.php @@ -23,7 +23,7 @@ public function __construct( public function processAssetsData(Entity\Anr $anr, array $assetsData): void { - $this->prepareAssetUuidsAndCodesCache($anr); + $this->prepareAssetsAndCodesCache($anr); foreach ($assetsData as $assetData) { $this->processAssetData($anr, $assetData); } @@ -31,7 +31,7 @@ public function processAssetsData(Entity\Anr $anr, array $assetsData): void public function processAssetData(Entity\Anr $anr, array $assetData): Entity\Asset { - $asset = $this->getAssetFromCacheOrDb($anr, $assetData['uuid']); + $asset = $this->getAssetFromCache($assetData['uuid']); if ($asset !== null) { return $asset; } @@ -55,25 +55,18 @@ public function processAssetData(Entity\Anr $anr, array $assetData): Entity\Asse return $asset; } - public function getAssetFromCacheOrDb(Entity\Anr $anr, string $uuid): ?Entity\Asset + public function getAssetFromCache(string $uuid): ?Entity\Asset { - $asset = $this->importCacheHelper->getItemFromArrayCache('assets', $uuid); - /* The current anr asserts' UUIDs are preloaded, so can be validated first. */ - if ($asset === null && $this->importCacheHelper->isItemInArrayCache('assets_uuids', $uuid)) { - /** @var ?Entity\Asset $asset */ - $asset = $this->assetTable->findByUuidAndAnr($uuid, $anr, false); - } - - return $asset; + return $this->importCacheHelper->getItemFromArrayCache('assets', $uuid); } - public function prepareAssetUuidsAndCodesCache(Entity\Anr $anr): void + public function prepareAssetsAndCodesCache(Entity\Anr $anr): void { - if (!$this->importCacheHelper->isCacheKeySet('assets_uuids')) { - foreach ($this->assetTable->findUuidsAndCodesByAnr($anr) as $data) { - $this->importCacheHelper - ->addItemToArrayCache('assets_uuids', (string)$data['uuid'], (string)$data['uuid']); - $this->importCacheHelper->addItemToArrayCache('assets_codes', $data['code'], $data['code']); + if (!$this->importCacheHelper->isCacheKeySet('assets')) { + /** @var Entity\Asset $asset */ + foreach ($this->assetTable->findByAnr($anr) as $asset) { + $this->importCacheHelper->addItemToArrayCache('assets', $asset, $asset->getUuid()); + $this->importCacheHelper->addItemToArrayCache('assets_codes', $asset->getCode(), $asset->getCode()); } } } diff --git a/src/Import/Processor/InformationRiskImportProcessor.php b/src/Import/Processor/InformationRiskImportProcessor.php new file mode 100644 index 00000000..350393fd --- /dev/null +++ b/src/Import/Processor/InformationRiskImportProcessor.php @@ -0,0 +1,103 @@ +prepareInformationRisksUuids($anr); + foreach ($informationRisksData as $informationRiskData) { + $this->processInformationRiskData($anr, $informationRiskData); + } + } + + public function processInformationRiskData(Entity\Anr $anr, array $informationRiskData): Entity\Amv + { + $informationRisk = $this->getInformationRiskFromCache($informationRiskData['uuid']); + if ($informationRisk !== null) { + return $informationRisk; + } + + $asset = $this->assetImportProcessor->getAssetFromCache( + $informationRiskData['asset']['uuid'] ?? $informationRiskData['asset'] + ); + $threat = $this->threatImportProcessor + ->getThreatFromCache($informationRiskData['threat']['uuid'] ?? $informationRiskData['threat']); + $vulnerability = $this->vulnerabilityImportProcessor->getVulnerabilityFromCache( + $informationRiskData['vulnerability']['uuid'] ?? $informationRiskData['vulnerability'] + ); + if ($asset === null || $threat === null || $vulnerability === null) { + throw new \LogicException( + 'Assets, threats and vulnerabilities have to be imported before the information risks.' + ); + } + + /* Prepare the max positions per asset as the objects are not saved in the DB to be able to determine on fly. */ + if (!isset($this->maxPositionsPerAsset[$asset->getUuid()])) { + $this->maxPositionsPerAsset[$asset->getUuid()] = $this->amvTable->findMaxPosition([ + 'anr' => $anr, + 'asset' => [ + 'uuid' => $asset->getUuid(), + 'anr' => $anr, + ], + ]); + } + + $amv = $this->anrAmvService->createAmvFromPreparedData($anr, $asset, $threat, $vulnerability, [ + 'uuid' => $informationRiskData['uuid'], + 'status' => $informationRiskData['status'], + 'setOnlyExactPosition' => true, + 'position' => ++$this->maxPositionsPerAsset[$asset->getUuid()], + ], false, false); + foreach ($informationRiskData['measures'] as $measureData) { + $measureUuid = $measureData['uuid'] ?? $measureData; + $measure = $this->referentialImportProcessor->getMeasureFromCache($measureUuid); + if ($measure !== null) { + $amv->addMeasure($measure); + } + } + $this->amvTable->save($amv, false); + $this->importCacheHelper->addItemToArrayCache('amvs', $amv, $amv->getUuid()); + + return $amv; + } + + public function getInformationRiskFromCache(string $uuid): ?Entity\Amv + { + return $this->importCacheHelper->getItemFromArrayCache('amvs', $uuid); + } + + public function prepareInformationRisksUuids(Entity\Anr $anr): void + { + if (!$this->importCacheHelper->isCacheKeySet('amvs')) { + /** @var Entity\Amv $amv */ + foreach ($this->amvTable->findByAnr($anr) as $amv) { + $this->importCacheHelper->addItemToArrayCache('amvs', $amv, $amv->getUuid()); + } + } + } +} diff --git a/src/Import/Processor/ObjectCategoryImportProcessor.php b/src/Import/Processor/ObjectCategoryImportProcessor.php new file mode 100644 index 00000000..e26a3a59 --- /dev/null +++ b/src/Import/Processor/ObjectCategoryImportProcessor.php @@ -0,0 +1,123 @@ +prepareObjectCategoriesCache($anr); + foreach ($objectCategoriesData as $categoryData) { + $this->processObjectCategoryData($anr, $categoryData, $importMode); + } + } + + public function processObjectCategoryData( + Entity\Anr $anr, + array $objectCategoryData, + string $importMode + ): Entity\ObjectCategory { + $parentCategory = isset( + $objectCategoryData['parent'] + ) && $objectCategoryData['parent'] instanceof Entity\ObjectCategory ? $objectCategoryData['parent'] : null; + + // TODO: process the categories data when the request comes NOT from the library: + // - parent and it's parents' data are inside so this method have to be recursively called. + // - support he old structure format: + // -> the categories data are under the objects structure and they are inside of the "instances" !!! + + $parentCategoryLabel = ''; + if ($parentCategory !== null) { + $parentCategoryLabel = $parentCategory->getLabel($anr->getLanguage()); + } + + /* If parents are different, a new category is created anyway. */ + /** @var ?Entity\ObjectCategory $objectCategory */ + $objectCategory = $this->importCacheHelper->getItemFromArrayCache( + 'object_categories_by_label', + $objectCategoryData['label'] . $parentCategoryLabel + ); + + if ($objectCategory === null) { + /* In the new data structure there is only "label" field set. */ + if (isset($objectCategoryData['label'])) { + $objectCategoryData['label' . $anr->getLanguage()] = $objectCategoryData['label']; + } + /* Prepare the position and cache it. */ + if (!isset($this->maxPositionPerCategory[$parentCategoryLabel])) { + /* If the parent category in new, there is no need to fetch it from the DB. */ + $this->maxPositionPerCategory[$parentCategoryLabel] = 0; + if ($parentCategory === null || $parentCategory->getId() !== null) { + $this->maxPositionPerCategory[$parentCategoryLabel] = $this->objectCategoryTable + ->findMaxPositionByAnrAndParent($anr, $parentCategory); + } + } + $objectCategoryData['position'] = ++$this->maxPositionPerCategory[$parentCategoryLabel]; + $objectCategoryData['setOnlyExactPosition'] = true; + + $objectCategory = $this->anrObjectCategoryService->create($anr, $objectCategoryData, false); + /* Adds the parent category label to the item key to avoid its comparison in the children's process. */ + $this->importCacheHelper->addItemToArrayCache( + 'object_categories_by_label', + $objectCategory, + $objectCategory->getLabel($anr->getLanguage()) . $parentCategoryLabel + ); + } + + if (!empty($objectCategoryData['objects'])) { + $this->objectImportProcessor + ->processObjectsData($anr, $objectCategory, $objectCategoryData['objects'], $importMode); + } + if (!empty($objectCategoryData['children'])) { + foreach ($objectCategoryData['children'] as $childObjectCategoryData) { + $childObjectCategoryData['parent'] = $objectCategory; + $this->processObjectCategoryData($anr, $childObjectCategoryData, $importMode); + } + } + + return $objectCategory; + } + + /* The categories cache's items keys are based on their labels + parent's labels if not root. */ + private function prepareObjectCategoriesCache(Entity\Anr $anr): void + { + if (!$this->importCacheHelper->isCacheKeySet('object_categories_by_label')) { + $languageIndex = $anr->getLanguage(); + /** @var Entity\ObjectCategory $objectCategory */ + foreach ($this->objectCategoryTable->findByAnr($anr) as $objectCategory) { + $parentCategoryLabel = ''; + if ($objectCategory->getParent() !== null) { + $parentCategoryLabel = $objectCategory->getParent()->getLabel($languageIndex); + } + $this->importCacheHelper->addItemToArrayCache( + 'object_categories_by_label', + $objectCategory, + $objectCategory->getLabel($languageIndex) . $parentCategoryLabel + ); + } + } + } +} diff --git a/src/Import/Processor/ObjectImportProcessor.php b/src/Import/Processor/ObjectImportProcessor.php new file mode 100644 index 00000000..822129b2 --- /dev/null +++ b/src/Import/Processor/ObjectImportProcessor.php @@ -0,0 +1,181 @@ +prepareObjectsCache($anr); + foreach ($objectsData as $objectData) { + $this->processObjectData($anr, $objectCategory, $objectData, $importMode); + } + } + + public function processObjectData( + Entity\Anr $anr, + Entity\ObjectCategory $objectCategory, + array $objectData, + string $importMode + ): Entity\MonarcObject { + $assetData = $objectData['asset']['asset'] ?? $objectData['asset']; + $objectScope = (int)$objectData['scope']; + $nameFiledKey = 'name' . $anr->getLanguage(); + $object = null; + /* In the new data structure there is only "name" field set. */ + if (isset($objectData['name'])) { + $objectData[$nameFiledKey] = $objectData['name']; + } + if ($objectScope === ObjectSuperClass::SCOPE_LOCAL || ( + $objectScope === ObjectSuperClass::SCOPE_GLOBAL && $importMode === ObjectImportService::IMPORT_MODE_MERGE + )) { + $object = $this->getObjectFromCache( + $objectData[$nameFiledKey], + $assetData['uuid'], + $objectScope, + $objectCategory->getId() + ); + if ($object !== null) { + foreach ($object->getChildren() as $linkedObject) { + $object->removeChild($linkedObject); + } + $this->monarcObjectTable->save($object, false); + } + } + + if ($object === null) { + $asset = $this->assetImportProcessor->processAssetData($anr, $assetData); + $rolfTag = null; + if (!empty($objectData['rofTag'])) { + if (isset($objectData['rofTag']['code'])) { + $rolfTag = $this->rolfTagImportProcessor->processRolfTagData($anr, $objectData['rofTag']); + } elseif (isset($objectData['rofTags'][$objectData['rofTag']])) { + /* Handles the structure prior the version 2.13.1 */ + $rolfTag = $this->rolfTagImportProcessor + ->processRolfTagData($anr, $objectData['rofTags'][$objectData['rofTag']]); + } + } + + /* Avoid the UUID duplication. */ + if ($this->importCacheHelper->isItemInArrayCache('objects_uuid', $objectData['uuid'])) { + unset($objectData['uuid']); + } + /* In the new data structure there is only "label" field set. */ + if (isset($objectData['label'])) { + $objectData['label' . $anr->getLanguage()] = $objectData['label']; + } + $objectData[$nameFiledKey] = $this->prepareUniqueObjectName($objectData[$nameFiledKey]); + + $object = $this->anrObjectService + ->createMonarcObject($anr, $asset, $objectCategory, $rolfTag, $objectData, false); + + $this->importCacheHelper->addItemToArrayCache( + 'objects_by_name_asset_scope_category', + $object, + $objectData[$nameFiledKey] . $asset->getUuid() . $object->getScope() . $objectCategory->getId() + ); + $this->importCacheHelper->addItemToArrayCache('objects_names', $objectData[$nameFiledKey]); + } + + /* Process objects links. */ + foreach ($objectData['children'] as $childObjectData) { + $objectCategory = $this->objectCategoryImportProcessor->processObjectCategoryData( + $anr, + $childObjectData['category'], + $importMode + ); + $childObject = $this->processObjectData($anr, $objectCategory, $childObjectData, $importMode); + /* Determine the max position of the link and store in the cache property. */ + if (!isset($this->linksMaxPositions[$object->getUuid()])) { + $this->linksMaxPositions[$object->getUuid()] = $this->objectObjectTable->findMaxPosition([ + 'anr' => $anr, + 'parent' => $object, + ]); + } + $positionData = [ + 'position' => ++$this->linksMaxPositions[$object->getUuid()], + 'forcePositionUpdate' => true, + ]; + $this->anrObjectObjectService->createObjectLink($object, $childObject, $positionData, false); + } + + return $object; + } + + public function getObjectFromCache( + string $name, + string $assetUuid, + int $scope, + ?int $categoryId + ): ?Entity\MonarcObject { + return $this->importCacheHelper->getItemFromArrayCache( + 'objects_by_name_asset_scope_category', + $name . $assetUuid . $scope . $categoryId + ); + } + + private function prepareObjectsCache(Entity\Anr $anr): void + { + if (!$this->importCacheHelper->isCacheKeySet('objects')) { + $languageIndex = $anr->getLanguage(); + /** @var Entity\MonarcObject $object */ + foreach ($this->monarcObjectTable->findByAnr($anr) as $object) { + $this->importCacheHelper->addItemToArrayCache('objects_uuids', $object->getUuid(), $object->getUuid()); + $this->importCacheHelper->addItemToArrayCache( + 'objects_by_name_asset_scope_category', + $object, + $object->getName($languageIndex) . $object->getAsset()->getUuid() . $object->getScope() + . $object->getCategory()?->getId() + ); + $this->importCacheHelper->addItemToArrayCache('objects_names', $object->getName($languageIndex)); + } + } + } + + private function prepareUniqueObjectName(string $objectName, int $index = 1): string + { + if ($this->importCacheHelper->isItemInArrayCache('objects_names', $objectName)) { + if (str_contains($objectName, ' - Imp. #')) { + $objectName = preg_replace('/#\d+/', '#' . $index, $objectName); + } else { + $objectName .= ' - Imp. #' . $index; + } + + return $this->prepareUniqueObjectName($objectName, $index + 1); + } + + return $objectName; + } +} diff --git a/src/Import/Processor/OperationalRisksImportProcessor.php b/src/Import/Processor/OperationalRisksImportProcessor.php new file mode 100644 index 00000000..449088a9 --- /dev/null +++ b/src/Import/Processor/OperationalRisksImportProcessor.php @@ -0,0 +1,85 @@ +prepareRolfRisksCache($anr); + foreach ($operationalRisksData as $operationalRiskData) { + $this->processOperationalRiskData($anr, $operationalRiskData); + } + } + + public function processOperationalRiskData(Entity\Anr $anr, array $operationalRiskData): Entity\RolfRisk + { + $operationalRisk = $this->getRolfRiskFromCache($operationalRiskData['code']); + if ($operationalRisk !== null) { + return $operationalRisk; + } + + $operationalRisk = $this->anrRolfRiskService->create($anr, [ + 'code' => $operationalRiskData['code'], + 'label' . $anr->getLanguage() => $operationalRiskData['label'] + ?? $operationalRiskData['label' . $anr->getLanguage()], + 'description' . $anr->getLanguage() => $operationalRiskData['label'] + ?? $operationalRiskData['description' . $anr->getLanguage()], + ], false); + $this->importCacheHelper->addItemToArrayCache( + 'rolf_risks_by_code', + $operationalRisk, + $operationalRisk->getCode() + ); + foreach ($operationalRiskData['measures'] as $measureData) { + $measureUuid = $measureData['uuid'] ?? $measureData; + $measure = $this->referentialImportProcessor->getMeasureFromCache($measureUuid); + if ($measure !== null) { + $operationalRisk->addMeasure($measure); + } + } + foreach ($operationalRiskData['rolfTags'] as $rolfTagData) { + $rolfTag = $this->rolfTagImportProcessor->getRolfTagFromCache($rolfTagData['code']); + if ($rolfTag !== null) { + $operationalRisk->addTag($rolfTag); + } + } + $this->rolfRiskTable->save($operationalRisk, false); + + return $operationalRisk; + } + + public function getRolfRiskFromCache(string $code): ?Entity\RolfRisk + { + return $this->importCacheHelper->getItemFromArrayCache('rolf_risks_by_code', $code); + } + + public function prepareRolfRisksCache(Entity\Anr $anr): void + { + if (!$this->importCacheHelper->isCacheKeySet('rolf_risks_by_code')) { + /** @var Entity\RolfRisk $rolfRisk */ + foreach ($this->rolfRiskTable->findByAnr($anr) as $rolfRisk) { + $this->importCacheHelper->addItemToArrayCache('rolf_risks_by_code', $rolfRisk, $rolfRisk->getCode()); + } + } + } +} diff --git a/src/Import/Processor/RecommendationImportProcessor.php b/src/Import/Processor/RecommendationImportProcessor.php new file mode 100644 index 00000000..52fadd3d --- /dev/null +++ b/src/Import/Processor/RecommendationImportProcessor.php @@ -0,0 +1,146 @@ +prepareRecommendationsCache($anr); + foreach ($recommendationSetsData as $recommendationSetData) { + $this->processRecommendationSetData($anr, $recommendationSetData); + } + } + + public function processRecommendationSetData(Entity\Anr $anr, array $recommendationSetData): void + { + $labelKey = 'label' . $anr->getLanguage(); + /* Supports the structure format prior v2.13.1 */ + if (isset($recommendationSetData[$labelKey]) && !isset($recommendationSetData['label'])) { + $recommendationSetData['label'] = $recommendationSetData[$labelKey]; + } + $recommendationSet = $this->getRecommendationSetFromCache( + $recommendationSetData['uuid'], + $recommendationSetData['label'] + ); + if ($recommendationSet === null) { + $recommendationSet = $this->anrRecommendationSetService->create($anr, $recommendationSetData, false); + $this->importCacheHelper->addItemToArrayCache( + 'recommendations_sets', + $recommendationSet, + $recommendationSet->getUuid() + ); + } + + if (!empty($recommendationSetData['recommendations'])) { + $this->processRecommendationsData($recommendationSet, $recommendationSetData['recommendations']); + } + } + + public function processRecommendationsData( + Entity\RecommendationSet $recommendationSet, + array $recommendationsData, + bool $prepareCache = false + ): void { + if ($prepareCache) { + $this->prepareRecommendationsCache($recommendationSet->getAnr()); + } + foreach ($recommendationsData as $recommendationData) { + $this->processRecommendationData($recommendationSet, $recommendationData); + } + } + + public function processRecommendationData( + Entity\RecommendationSet $recommendationSet, + array $recommendationData + ): Entity\Recommendation { + $anr = $recommendationSet->getAnr(); + $recommendation = $this->getRecommendationFromCache($recommendationData['uuid']); + if ($recommendation !== null) { + return $recommendation; + } + + /* The code should be unique within recommendations sets. */ + if (\in_array($recommendationData['code'], $this->importCacheHelper->getItemFromArrayCache( + 'recommendations_codes_by_set_uuid', + $recommendationSet->getUuid() + ) ?? [], true)) { + $recommendationData['code'] .= '-' . time(); + } + $recommendationData['recommendationSet'] = $recommendationSet; + + $recommendation = $this->anrRecommendationService->create($anr, $recommendationData, false); + $this->importCacheHelper->addItemToArrayCache('recommendations', $recommendation, $recommendation->getUuid()); + + return $recommendation; + } + + public function getRecommendationSetFromCache(string $uuid, string $label): ?Entity\RecommendationSet + { + $recommendationSet = $this->importCacheHelper->getItemFromArrayCache('recommendations_sets', $uuid); + if ($recommendationSet === null) { + /** @var Entity\RecommendationSet $set */ + foreach ($this->importCacheHelper->getItemFromArrayCache('recommendations_sets') ?? [] as $set) { + if ($set->getLabel() === $label) { + $recommendationSet = $set; + break; + } + } + } + + return $recommendationSet; + } + + public function getRecommendationFromCache(string $uuid): ?Entity\Recommendation + { + return $this->importCacheHelper->getItemFromArrayCache('recommendations', $uuid); + } + + public function prepareRecommendationsCache(Entity\Anr $anr): void + { + if (!$this->importCacheHelper->isCacheKeySet('recommendations_sets')) { + /** @var Entity\RecommendationSet $recommendationSet */ + foreach ($this->recommendationSetTable->findByAnr($anr) as $recommendationSet) { + $this->importCacheHelper->addItemToArrayCache( + 'recommendations_sets', + $recommendationSet, + $recommendationSet->getUuid() + ); + $recommendationsCodes = []; + foreach ($recommendationSet->getRecommendations() as $recommendation) { + $this->importCacheHelper->addItemToArrayCache( + 'recommendations', + $recommendation, + $recommendation->getUuid() + ); + $recommendationsCodes[] = $recommendation->getCode(); + } + $this->importCacheHelper->addItemToArrayCache( + 'recommendations_codes_by_set_uuid', + $recommendationsCodes, + $recommendationSet->getUuid() + ); + + } + } + } +} diff --git a/src/Import/Processor/ReferentialImportProcessor.php b/src/Import/Processor/ReferentialImportProcessor.php index 52a4d98b..99fde50a 100644 --- a/src/Import/Processor/ReferentialImportProcessor.php +++ b/src/Import/Processor/ReferentialImportProcessor.php @@ -31,7 +31,7 @@ public function __construct( public function processReferentialsData(Entity\Anr $anr, array $referentialsData): void { - $this->prepareReferentialUuidsAndMeasuresUuidsAndCodesCache($anr); + $this->prepareReferentialsAndMeasuresCache($anr); $this->prepareSoaCategoriesCache($anr); foreach ($referentialsData as $referentialData) { $this->processReferentialData($anr, $referentialData); @@ -41,16 +41,6 @@ public function processReferentialsData(Entity\Anr $anr, array $referentialsData public function processReferentialData(Entity\Anr $anr, array $referentialData): Entity\Referential { $referential = $this->importCacheHelper->getItemFromArrayCache('referentials', $referentialData['uuid']); - if ($referential !== null) { - return $referential; - } - - /* The current anr referential' UUIDs are preloaded, so can be validated first. */ - if ($this->importCacheHelper->isCacheKeySet('referential_' . $referential['uuid'] . '_measures_uuids')) { - /** @var Entity\Referential $referential */ - $referential = $this->referentialTable->findByUuidAndAnr($referentialData['uuid'], $anr, false); - } - if ($referential === null) { /* In the new data structure there is only "label" field set. */ if (isset($referentialData['label'])) { @@ -75,7 +65,7 @@ public function processMeasuresData( bool $prepareCache = false ): void { if ($prepareCache) { - $this->prepareReferentialUuidsAndMeasuresUuidsAndCodesCache($anr); + $this->prepareReferentialsAndMeasuresCache($anr); $this->prepareSoaCategoriesCache($anr); } @@ -113,70 +103,53 @@ public function processSoaCategoryData( return $soaCategory; } - private function processMeasureData( + public function getMeasureFromCache(string $measureUuid): ?Entity\Measure + { + return $this->importCacheHelper->getItemFromArrayCache('measures', $measureUuid); + } + + public function processMeasureData( Entity\Anr $anr, Entity\Referential $referential, array $measureData ): Entity\Measure { - $measure = $this->importCacheHelper->getItemFromArrayCache('measures', $measureData['uuid']); - if ($measure !== null) { - return $measure; - } + $measure = $this->getMeasureFromCache($measureData['uuid']); + if ($measure === null) { + /* The code should be unique. */ + if (\in_array($measureData['code'], $this->importCacheHelper->getItemFromArrayCache( + 'measures_codes_by_ref_uuid', + $referential->getUuid() + ) ?? [], true)) { + $measureData['code'] .= '-' . time(); + } - /* The current anr measures' UUIDs are preloaded, so can be validated first. */ - if ($this->importCacheHelper->isItemInArrayCache( - 'referential_' . $referential->getUuid() . '_measures_uuids', - $measureData['uuid'] - )) { - /** @var Entity\Measure $measure */ - $measure = $this->measureTable->findByUuidAndAnr($measureData['uuid'], $anr, false); - if ($measure !== null) { - return $measure; + /* In the new data structure there is only "label" field set. */ + if (isset($measureData['label'])) { + $measureData['label' . $anr->getLanguage()] = $measureData['label']; } - } - /* The code should be unique. */ - if ($this->importCacheHelper->isItemInArrayCache( - 'referential_' . $referential->getUuid() . '_measures_codes', - $measureData['code'] - )) { - $measureData['code'] .= '-' . time(); - } + $soaCategory = $this->processSoaCategoryData($anr, $referential, $measureData); - /* In the new data structure there is only "label" field set. */ - if (isset($measureData['label'])) { - $measureData['label' . $anr->getLanguage()] = $measureData['label']; + $measure = $this->anrMeasureService + ->createMeasureObject($anr, $referential, $soaCategory, $measureData, false); + $this->importCacheHelper->addItemToArrayCache('measures', $measure, $measure->getUuid()); } - $soaCategory = $this->processSoaCategoryData($anr, $referential, $measureData); - - $measure = $this->anrMeasureService->createMeasureObject($anr, $referential, $soaCategory, $measureData, false); - $this->importCacheHelper->addItemToArrayCache('measures', $measure, $measure->getUuid()); - $this->processLinkedMeasures($measure, $measureData); return $measure; } + // TODO: handle the old structure 'measuresmeasures', it's in a parallel key with measures and consists elements: + // {"father": "3e3e542a-67b2-4a77-b09b-9dc9b977cd8e", "child": "01096bf7-a45e-40d9-851e-72a6b8d7344a"} private function processLinkedMeasures(Entity\Measure $measure, array $measureData): void { if (!empty($measureData['linkedMeasures'])) { foreach ($measureData['linkedMeasures'] as $linkedMeasureData) { - $linkedMeasure = $this->importCacheHelper - ->getItemFromArrayCache('measures', $linkedMeasureData['uuid']); - if ($linkedMeasure === null) { - if ($this->importCacheHelper->isItemInArrayCache( - 'referential_' . $linkedMeasureData['referential']['uuid'] . '_measures_uuids', - $linkedMeasureData['uuid'] - )) { - /** @var Entity\Measure $linkedMeasure */ - $linkedMeasure = $this->measureTable->findByUuidAndAnr( - $linkedMeasureData['uuid'], - $measure->getAnr(), - false - ); - } - } + $linkedMeasure = $this->importCacheHelper->getItemFromArrayCache( + 'measures', + $linkedMeasureData['uuid'] + ); if ($linkedMeasure !== null) { $measure->addLinkedMeasure($linkedMeasure); $this->measureTable->save($linkedMeasure, false); @@ -185,20 +158,21 @@ private function processLinkedMeasures(Entity\Measure $measure, array $measureDa } } - private function prepareReferentialUuidsAndMeasuresUuidsAndCodesCache(Entity\Anr $anr): void + private function prepareReferentialsAndMeasuresCache(Entity\Anr $anr): void { - if (!$this->importCacheHelper->isCacheKeySet('referential_cache')) { - $this->importCacheHelper->addItemToArrayCache('referential_cache', true); - foreach ($this->referentialTable->findReferentialsUuidsWithMeasuresUuidsAndCodesByAnr($anr) as $data) { - $this->importCacheHelper->addItemToArrayCache( - 'referential_' . $data['uuid'] . '_measures_uuids', - (string)$data['measure_uuid'], - (string)$data['measure_uuid'] - ); + if (!$this->importCacheHelper->isCacheKeySet('referentials')) { + /** @var Entity\Referential $referential */ + foreach ($this->referentialTable->findByAnr($anr) as $referential) { + $this->importCacheHelper->addItemToArrayCache('referentials', $referential, $referential->getUuid()); + $measuresCodes = []; + foreach ($referential->getMeasures() as $measure) { + $this->importCacheHelper->addItemToArrayCache('measures', $measure, $measure->getUuid()); + $measuresCodes[] = $measure->getCode(); + } $this->importCacheHelper->addItemToArrayCache( - 'referential_' . $data['uuid'] . '_measures_codes', - $data['measure_code'], - $data['measure_code'] + 'measures_codes_by_ref_uuid', + $measuresCodes, + $referential->getUuid() ); } } diff --git a/src/Import/Processor/RolfTagImportProcessor.php b/src/Import/Processor/RolfTagImportProcessor.php new file mode 100644 index 00000000..4e225616 --- /dev/null +++ b/src/Import/Processor/RolfTagImportProcessor.php @@ -0,0 +1,64 @@ +prepareRolfTagsCache($anr); + foreach ($rolfTagsData as $rolfTagData) { + $this->processRolfTagData($anr, $rolfTagData); + } + } + + public function processRolfTagData(Entity\Anr $anr, array $rolfTagData): Entity\RolfTag + { + $rolfTag = $this->getRolfTagFromCache($rolfTagData['code']); + if ($rolfTag !== null) { + return $rolfTag; + } + + /* In the new data structure there is only "label" field set. */ + if (isset($rolfTagData['label'])) { + $rolfTagData['label' . $anr->getLanguage()] = $rolfTagData['label']; + } + + $rolfTag = $this->anrRolfTagService->create($anr, $rolfTagData, false); + $this->importCacheHelper->addItemToArrayCache('rolf_tags_by_code', $rolfTag, $rolfTag->getCode()); + + return $rolfTag; + } + + public function getRolfTagFromCache(string $code): ?Entity\RolfTag + { + return $this->importCacheHelper->getItemFromArrayCache('rolf_tags_by_code', $code); + } + + public function prepareRolfTagsCache(Entity\Anr $anr): void + { + if (!$this->importCacheHelper->isCacheKeySet('rolf_tags_by_code')) { + /** @var Entity\RolfTag $rolfTag */ + foreach ($this->rolfTagTable->findByAnr($anr) as $rolfTag) { + $this->importCacheHelper->addItemToArrayCache('rolf_tags_by_code', $rolfTag, $rolfTag->getCode()); + } + } + } +} diff --git a/src/Import/Processor/ThreatImportProcessor.php b/src/Import/Processor/ThreatImportProcessor.php index d59c5987..6131bd60 100644 --- a/src/Import/Processor/ThreatImportProcessor.php +++ b/src/Import/Processor/ThreatImportProcessor.php @@ -27,7 +27,7 @@ public function __construct( public function processThreatsData(Entity\Anr $anr, array $threatsData, array $themesData): void { - $this->prepareThreatUuidsAndCodesCache($anr); + $this->prepareThreatsAndCodesCache($anr); $this->prepareThemesCache($anr); foreach ($threatsData as $threatData) { @@ -37,7 +37,7 @@ public function processThreatsData(Entity\Anr $anr, array $threatsData, array $t public function processThreatData(Entity\Anr $anr, array $threatData, array $themesData): Entity\Threat { - $threat = $this->getThreatFromCacheOrDb($anr, $threatData['uuid']); + $threat = $this->getThreatFromCache($threatData['uuid']); if ($threat !== null) { return $threat; } @@ -72,25 +72,18 @@ public function processThreatData(Entity\Anr $anr, array $threatData, array $the return $threat; } - public function getThreatFromCacheOrDb(Entity\Anr $anr, string $uuid): ?Entity\Threat + public function getThreatFromCache(string $uuid): ?Entity\Threat { - $threat = $this->importCacheHelper->getItemFromArrayCache('threats', $uuid); - /* The current anr threats' UUIDs are preloaded, so can be validated first. */ - if ($threat === null && $this->importCacheHelper->isItemInArrayCache('threats_uuids', $uuid)) { - /** @var ?Entity\Threat $threat */ - $threat = $this->threatTable->findByUuidAndAnr($uuid, $anr, false); - } - - return $threat; + return $this->importCacheHelper->getItemFromArrayCache('threats', $uuid); } - public function prepareThreatUuidsAndCodesCache(Entity\Anr $anr): void + public function prepareThreatsAndCodesCache(Entity\Anr $anr): void { - if (!$this->importCacheHelper->isCacheKeySet('threats_uuids')) { - foreach ($this->threatTable->findUuidsAndCodesByAnr($anr) as $data) { - $this->importCacheHelper - ->addItemToArrayCache('threats_uuids', (string)$data['uuid'], (string)$data['uuid']); - $this->importCacheHelper->addItemToArrayCache('threats_codes', $data['code'], $data['code']); + if (!$this->importCacheHelper->isCacheKeySet('threats')) { + /** @var Entity\Threat $threat */ + foreach ($this->threatTable->findByAnr($anr) as $threat) { + $this->importCacheHelper->addItemToArrayCache('threats', $threat, $threat->getUuid()); + $this->importCacheHelper->addItemToArrayCache('threats_codes', $threat->getCode(), $threat->getCode()); } } } diff --git a/src/Import/Processor/VulnerabilityImportProcessor.php b/src/Import/Processor/VulnerabilityImportProcessor.php index e72434e6..e3810049 100644 --- a/src/Import/Processor/VulnerabilityImportProcessor.php +++ b/src/Import/Processor/VulnerabilityImportProcessor.php @@ -24,7 +24,7 @@ public function __construct( public function processVulnerabilitiesData(Entity\Anr $anr, array $vulnerabilitiesData): void { - $this->prepareVulnerabilityUuidsAndCodesCache($anr); + $this->prepareVulnerabilitiesAndCodesCache($anr); foreach ($vulnerabilitiesData as $vulnerabilityData) { $this->processVulnerabilityData($anr, $vulnerabilityData); } @@ -32,7 +32,7 @@ public function processVulnerabilitiesData(Entity\Anr $anr, array $vulnerabiliti public function processVulnerabilityData(Entity\Anr $anr, array $vulnerabilityData): Entity\Vulnerability { - $vulnerability = $this->getVulnerabilityFromCacheOrDb($anr, $vulnerabilityData['uuid']); + $vulnerability = $this->getVulnerabilityFromCache($vulnerabilityData['uuid']); if ($vulnerability !== null) { return $vulnerability; } @@ -56,25 +56,26 @@ public function processVulnerabilityData(Entity\Anr $anr, array $vulnerabilityDa return $vulnerability; } - public function getVulnerabilityFromCacheOrDb(Entity\Anr $anr, string $uuid): ?Entity\Vulnerability + public function getVulnerabilityFromCache(string $uuid): ?Entity\Vulnerability { - $vulnerability = $this->importCacheHelper->getItemFromArrayCache('vulnerabilities', $uuid); - /* The current anr vulnerabilities' UUIDs are preloaded, so can be validated first. */ - if ($vulnerability === null && $this->importCacheHelper->isItemInArrayCache('vulnerabilities_uuids', $uuid)) { - /** @var ?Entity\Vulnerability $vulnerability */ - $vulnerability = $this->vulnerabilityTable->findByUuidAndAnr($uuid, $anr, false); - } - - return $vulnerability; + return $this->importCacheHelper->getItemFromArrayCache('vulnerabilities', $uuid); } - public function prepareVulnerabilityUuidsAndCodesCache(Entity\Anr $anr): void + public function prepareVulnerabilitiesAndCodesCache(Entity\Anr $anr): void { if (!$this->importCacheHelper->isCacheKeySet('vulnerabilities_uuids')) { - foreach ($this->vulnerabilityTable->findUuidsAndCodesByAnr($anr) as $data) { - $this->importCacheHelper - ->addItemToArrayCache('vulnerabilities_uuids', (string)$data['uuid'], (string)$data['uuid']); - $this->importCacheHelper->addItemToArrayCache('vulnerabilities_codes', $data['code'], $data['code']); + /** @var Entity\Vulnerability $vulnerability */ + foreach ($this->vulnerabilityTable->findByAnr($anr) as $vulnerability) { + $this->importCacheHelper->addItemToArrayCache( + 'vulnerabilities', + $vulnerability, + $vulnerability->getUuid() + ); + $this->importCacheHelper->addItemToArrayCache( + 'vulnerabilities_codes', + $vulnerability->getCode(), + $vulnerability->getCode() + ); } } } diff --git a/src/Import/Service/AssetImportService.php b/src/Import/Service/AssetImportService.php index 002eab07..ded4f816 100644 --- a/src/Import/Service/AssetImportService.php +++ b/src/Import/Service/AssetImportService.php @@ -1,7 +1,7 @@ connectedUser = $connectedUserService->getConnectedUser(); @@ -62,124 +51,20 @@ public function importFromArray($monarcVersion, array $data, Anr $anr): ?Asset . ' Please contact us for more details.'); } - $asset = $this->processAssetDataAndGetAsset($data['asset'], $anr); - if (!empty($data['amvs'])) { - $this->processThreatsData($data['threats'], $data['themes'] ?? [], $anr); - $this->processVulnerabilitiesData($data['vuls'], $anr); - $this->processAmvsData($data['amvs'], $anr, $asset); + $asset = $this->assetImportProcessor->processAssetData($anr, $data['asset']); + /* In the new structure 'amvs' => 'informationRisks', 'vuls' => 'vulnerabilities'. */ + if (!empty($data['amvs']) || !empty($data['informationRisks'])) { + $this->threatImportProcessor->processThreatsData($anr, $data['threats'], $data['themes'] ?? []); + $this->vulnerabilityImportProcessor + ->processVulnerabilitiesData($anr, $data['vuls'] ?? $data['vulnerabilities']); + $this->processInformationRisksData($data['amvs'] ?? $data['informationRisks'], $anr, $asset); } return $asset; } - private function processAssetDataAndGetAsset(array $assetData, Anr $anr): Asset - { - /** @var Asset|null $asset */ - $asset = $this->assetTable->findByUuidAndAnr($assetData['uuid'], $anr, false); - if ($asset !== null) { - return $asset; - } - - /* The code should be unique. */ - if ($this->assetTable->doesCodeAlreadyExist($assetData['code'], $anr)) { - $assetData['code'] .= '-' . time(); - } - -// $asset = (new Asset()) -// ->setUuid($assetData['uuid']) -// ->setAnr($anr) -// ->setLabels($assetData) -// ->setDescriptions($assetData) -// ->setStatus($assetData['status'] ?? 1) -// ->setMode($assetData['mode'] ?? 0) -// ->setType($assetData['type']) -// ->setCode($assetData['code']); -// -// $this->assetTable->save($asset, false); - - // TODO: adjust the asset service code... - return $this->anrAssetService->create($anr, $assetData, false); - } - - private function processThreatsData(array $threatsData, array $themesData, Anr $anr): void - { - $languageIndex = $anr->getLanguage(); - $labelKey = 'label' . $languageIndex; - - foreach ($threatsData as $threatUuid => $threatData) { - $themeData = $themesData[$threatData['theme']] ?? []; - /** @var Threat|null $threat */ - $threat = $this->threatTable->findByUuidAndAnr($threatUuid, $anr, false); - if ($threat !== null) { - /* Validate Theme. */ - $currentTheme = $threat->getTheme(); - if (!empty($themeData) - && ($currentTheme === null - || $currentTheme->getLabel($languageIndex) !== $themeData[$labelKey] - ) - ) { - $theme = $this->processThemeDataAndGetTheme($themeData, $anr); - $threat->setTheme($theme); - - $this->threatTable->save($threat, false); - } - } else { - /* The code should be unique. */ - if ($this->importCacheHelper->getItemFromArrayCache('threats_codes', $threatData['code']) !== null - || $this->threatTable->doesCodeAlreadyExist($threatData['code'], $anr) - ) { - $threatData['code'] .= '-' . time(); - } - $threatData['theme'] = !empty($themeData) ? $this->processThemeDataAndGetTheme($themeData, $anr) : null; - - $threat = $this->anrThreatService->create($anr, $threatData, false); - } - - $this->importCacheHelper->addItemToArrayCache('threats', $threat, $threat->getUuid()); - $this->importCacheHelper->addItemToArrayCache('threats_codes', $threat->getCode(), $threat->getCode()); - } - } - - private function processThemeDataAndGetTheme(array $themeData, Anr $anr): Theme - { - $this->importCacheHelper->prepareThemesCacheData($anr); - - $languageIndex = $anr->getLanguage(); - $labelKey = 'label' . $languageIndex; - $theme = $this->importCacheHelper->getItemFromArrayCache('themes_by_labels', $themeData[$labelKey]); - if ($theme === null) { - $theme = $this->anrThemeService->create($anr, $themeData, false); - $this->importCacheHelper->addItemToArrayCache('themes_by_labels', $theme, $themeData[$labelKey]); - } - - return $theme; - } - - private function processVulnerabilitiesData(array $vulnerabilitiesData, Anr $anr): void - { - foreach ($vulnerabilitiesData as $vulnerabilityData) { - /** @var Vulnerability|null $vulnerability */ - $vulnerability = $this->vulnerabilityTable->findByUuidAndAnr($vulnerabilityData['uuid'], $anr, false); - if ($vulnerability === null) { - /* The code should be unique. */ - if ($this->importCacheHelper - ->getItemFromArrayCache('vulnerabilities_codes', $vulnerabilityData['code']) !== null - || $this->vulnerabilityTable->doesCodeAlreadyExist($vulnerabilityData['code'], $anr) - ) { - $vulnerabilityData['code'] .= '-' . time(); - } - - $vulnerability = $this->anrVulnerabilityService->create($anr, $vulnerabilityData, false); - } - - $this->importCacheHelper->addItemToArrayCache('vulnerabilities', $vulnerability, $vulnerability->getUuid()); - $this->importCacheHelper - ->addItemToArrayCache('vulnerabilities_codes', $vulnerability->getCode(), $vulnerability->getCode()); - } - } - // TODO: use services to create the objects. - private function processAmvsData(array $amvsData, Anr $anr, Asset $asset): void + private function processInformationRisksData(array $amvsData, Anr $anr, Asset $asset): void { foreach ($amvsData as $amvUuid => $amvData) { /** @var Amv|null $amv */ @@ -189,12 +74,12 @@ private function processAmvsData(array $amvsData, Anr $anr, Asset $asset): void ->setUuid($amvUuid) ->setAnr($anr) ->setAsset($asset) - ->setMeasures(null) ->setCreator($this->connectedUser->getEmail()); - $threat = $this->importCacheHelper->getItemFromArrayCache('threats', $amvData['threat']); - $vulnerability = $this->importCacheHelper - ->getItemFromArrayCache('vulnerabilities', $amvData['vulnerability']); + $threat = $this->threatImportProcessor->getThreatFromCache($amvData['threat']); + $vulnerability = $this->vulnerabilityImportProcessor->getVulnerabilityFromCache( + $amvData['vulnerability'] + ); if ($threat === null || $vulnerability === null) { throw new Exception(sprintf( 'The import file is malformed. AMV\'s "%s" threats or vulnerability was not processed before.', @@ -239,7 +124,7 @@ private function processAmvsData(array $amvsData, Anr $anr, Asset $asset): void $instanceRisk->setSpecific(InstanceRisk::TYPE_SPECIFIC); $this->instanceRiskTable->save($instanceRisk, false); } - $this->instanceRiskTable->getDb()->flush(); + $this->instanceRiskTable->flush(); foreach ($instanceRisks as $instanceRisk) { $instanceRisk @@ -247,7 +132,7 @@ private function processAmvsData(array $amvsData, Anr $anr, Asset $asset): void ->setUpdater($this->connectedUser->getEmail()); $this->instanceRiskTable->save($instanceRisk, false); } - $this->instanceRiskTable->getDb()->flush(); + $this->instanceRiskTable->flush(); $amvsToDelete[] = $oldAmv; } @@ -264,14 +149,14 @@ private function processMeasuresAndReferentialData(array $measuresData, Anr $anr $labelKey = 'label' . $languageIndex; foreach ($measuresData as $measureUuid) { $measure = $this->importCacheHelper->getItemFromArrayCache('measures', $measureUuid) - ?: $this->measureTable->findByAnrAndUuid($anr, $measureUuid); + ?: $this->measureTable->findByUuidAndAnr($measureUuid, $anr); if ($measure === null) { /* Backward compatibility. Prior v2.10.3 we did not set referential data when exported. */ $referentialUuid = $data['measures'][$measureUuid]['referential']['uuid'] ?? $data['measures'][$measureUuid]['referential']; $referential = $this->importCacheHelper->getItemFromArrayCache('referentials', $referentialUuid) - ?: $this->referentialTable->findByAnrAndUuid($anr, $referentialUuid); + ?: $this->referentialTable->findByUuidAndAnr($referentialUuid, $anr); /* For backward compatibility. */ if ($referential === null @@ -283,7 +168,7 @@ private function processMeasuresAndReferentialData(array $measuresData, Anr $anr ->setLabels([$labelKey => $data['measures'][$measureUuid]['referential'][$labelKey]]) ->setCreator($this->connectedUser->getEmail()); - $this->referentialTable->saveEntity($referential, false); + $this->referentialTable->save($referential, false); $this->importCacheHelper->addItemToArrayCache('referentials', $referential, $referentialUuid); } @@ -314,7 +199,7 @@ private function processMeasuresAndReferentialData(array $measuresData, Anr $anr $measure->addAmv($amv); - $this->measureTable->saveEntity($measure, false); + $this->measureTable->save($measure, false); } } } diff --git a/src/Import/Service/InstanceImportService.php b/src/Import/Service/InstanceImportService.php index b707a1c8..c1722e07 100755 --- a/src/Import/Service/InstanceImportService.php +++ b/src/Import/Service/InstanceImportService.php @@ -16,6 +16,7 @@ use Monarc\Core\Helper\EncryptDecryptHelperTrait; use Monarc\FrontOffice\Import\Helper\ImportCacheHelper; use Monarc\FrontOffice\Entity; +use Monarc\FrontOffice\Import\Processor; use Monarc\FrontOffice\Table; use Monarc\FrontOffice\Model\Table as DeprecatedTable; use Monarc\FrontOffice\Service; @@ -38,6 +39,14 @@ class InstanceImportService private array $cachedData = []; public function __construct( + private Processor\AssetImportProcessor $assetImportProcessor, + private Processor\ThreatImportProcessor $threatImportProcessor, + private Processor\VulnerabilityImportProcessor $vulnerabilityImportProcessor, + private Processor\ReferentialImportProcessor $referentialImportProcessor, + private Processor\InformationRiskImportProcessor $informationRiskImportProcessor, + private Processor\RolfTagImportProcessor $rolfTagImportProcessor, + private Processor\OperationalRisksImportProcessor $operationalRisksImportProcessor, + private Processor\RecommendationImportProcessor $recommendationImportProcessor, private Service\AnrInstanceRiskService $anrInstanceRiskService, private Service\InstanceRiskOwnerService $instanceRiskOwnerService, private Service\AnrInstanceService $anrInstanceService, @@ -60,10 +69,9 @@ public function __construct( private DeprecatedTable\QuestionTable $questionTable, private DeprecatedTable\QuestionChoiceTable $questionChoiceTable, private DeprecatedTable\SoaTable $soaTable, - private DeprecatedTable\MeasureTable $measureTable, - private DeprecatedTable\MeasureMeasureTable $measureMeasureTable, + private Table\MeasureTable $measureTable, private Table\ThemeTable $themeTable, - private DeprecatedTable\ReferentialTable $referentialTable, + private Table\ReferentialTable $referentialTable, private DeprecatedTable\InterviewTable $interviewTable, private Table\DeliveryTable $deliveryTable, private Table\ScaleImpactTypeTable $scaleImpactTypeTable, @@ -90,109 +98,173 @@ public function __construct( * * @return array An array where the first key is the generated IDs, and the second are import errors */ - public function importFromFile(int $anrId, array $data): array + public function importFromFile(Entity\Anr $anr, array $importParams): array { // Mode may either be 'merge' or 'duplicate' - $mode = empty($data['mode']) ? 'merge' : $data['mode']; + $mode = empty($importParams['mode']) ? 'merge' : $importParams['mode']; /* * The object may be imported at the root, or under an existing instance in the ANR instances tree */ $parentInstance = null; - if (!empty($data['idparent'])) { - $parentInstance = $this->instanceTable->findById((int)$data['idparent']); + if (!empty($importParams['idparent'])) { + /** @var Entity\Instance $parentInstance */ + $parentInstance = $this->instanceTable->findById((int)$importParams['idparent']); } // We can have multiple files imported with the same password (we'll emit warnings if the password mismatches) - if (empty($data['file'])) { + if (empty($importParams['file'])) { throw new Exception('File missing', 412); } - $ids = []; - $errors = []; - $anr = $this->anrTable->findById($anrId); - // TODO: remove this!!! - ini_set('max_execution_time', '0'); - ini_set('memory_limit', '-1'); +// ini_set('max_execution_time', '0'); +// ini_set('memory_limit', '-1'); - foreach ($data['file'] as $keyfile => $f) { + $createdInstancesIds = []; + $importErrors = []; + foreach ($importParams['file'] as $key => $file) { // Ensure the file has been uploaded properly, silently skip the files that are erroneous - if (isset($f['error']) && $f['error'] === UPLOAD_ERR_OK && file_exists($f['tmp_name'])) { - if (empty($data['password'])) { - $file = json_decode(trim(file_get_contents($f['tmp_name'])), true, 512, JSON_THROW_ON_ERROR); + if (isset($file['error']) && $file['error'] === UPLOAD_ERR_OK && file_exists($file['tmp_name'])) { + if (empty($importParams['password'])) { + $data = json_decode(trim(file_get_contents($file['tmp_name'])), true, 512, JSON_THROW_ON_ERROR); } else { - $decryptedResult = $this->decrypt(file_get_contents($f['tmp_name']), $data['password']); + $decryptedResult = $this->decrypt(file_get_contents($file['tmp_name']), $importParams['password']); if ($decryptedResult === false) { throw new Exception('Password is not correct.', 412); } - $file = json_decode(trim($decryptedResult), true, 512, JSON_THROW_ON_ERROR); + $data = json_decode(trim($decryptedResult), true, 512, JSON_THROW_ON_ERROR); unset($decryptedResult); } - if ($file !== false - && ($id = $this->importFromArray($file, $anr, $parentInstance, $mode)) !== false) { - // Import was successful, store the ID - if (is_array($id)) { - $ids += array_merge($ids, $id); - } else { - $ids[] = $id; - } + if ($data !== false) { + $createdInstancesIds = array_merge( + $createdInstancesIds, + $this->importFromArray($data, $anr, $parentInstance, $mode) + ); } else { - $errors[] = 'The file "' . $f['name'] . '" can\'t be imported'; + $importErrors[] = 'The file "' . $file['name'] . '" can\'t be imported'; } } - // Free up the memory in case we're handling big files - unset($data['file'][$keyfile]); + // Free up the memory in case of big files. + unset($importParams['file'][$key]); } - return [$ids, $errors]; + return [$createdInstancesIds, $importErrors]; } /** - * Imports an instance from an exported data (json) array. + * Imports instances ot the whole analysis from an exported data (json) array. * * @param array $data The instance data * @param Entity\Anr $anr The target ANR - * @param null|CoreEntity\InstanceSuperClass $parentInstance The parent instance, that should be imported, - * null if it's root. - * @param string $modeImport Import mode, either 'merge' or 'duplicate' + * @param null|Entity\Instance $parentInstance The parent instance, that should be imported, null if it's root. + * @param string $importMode Import mode, either 'merge' or 'duplicate' * - * @return array|bool An array of created instances IDs, or false in case of error + * @return array An array of created instances IDs, or false in case of error */ - public function importFromArray( + private function importFromArray( array $data, Entity\Anr $anr, - ?CoreEntity\InstanceSuperClass $parentInstance = null, - string $modeImport = 'merge' - ) { + ?Entity\Instance $parentInstance, + string $importMode + ): array { + $this->setAndValidateMonarcVersion($data); $this->validateIfImportIsPossible($anr, $parentInstance, $data); - $this->setAndValidateMonarcVersion($data); + $result = []; + if (!$this->isMonarcVersionLowerThen('2.13.1')) { + /* New structure of the import data. */ + if ($data['type'] === 'anr') { + $result = $this->processAnrImport($anr, $data, $parentInstance, $importMode); + } elseif ($data['type'] === 'instance') { + $result = $this->processInstanceImport($anr, $data, $parentInstance, $importMode); + } + + return $result; + } $this->currentAnalyseMaxRecommendationPosition = $this->recommendationTable->findMaxPosition(['anr' => $anr]); $this->currentMaxInstancePosition = $this->instanceTable->findMaxPosition( ['anr' => $anr, 'parent' => $parentInstance] ); - $result = false; - - if (isset($data['type']) && $data['type'] === 'instance') { + if ($data['type'] === 'instance') { $this->importType = 'instance'; - - $result = $this->importInstanceFromArray($data, $anr, $parentInstance, $modeImport); + $result = $this->importInstanceFromArray($data, $anr, $parentInstance, $importMode); + } elseif ($data['type'] === 'anr') { + $this->importType = 'anr'; + $result = $this->importAnrFromArray($data, $anr, $parentInstance, $importMode); } - if (isset($data['type']) && $data['type'] === 'anr') { - $this->importType = 'anr'; + return $result; + } + + /* START new import structure process. */ - $result = $this->importAnrFromArray($data, $anr, $parentInstance, $modeImport); + private function processAnrImport( + Entity\Anr $anr, + array $data, + ?Entity\Instance $parentInstance, + string $importMode + ): array { + $result = []; + if ($data['withKnowledgeBase']) { + $this->processKnowledgeBaseData($anr, $data['knowledgeBase']); } + if ($data['withLibrary']) { + $this->processLibraryData($anr, $data['library'], $importMode); + } + // $this->processAnrInstanceMetadataFields($anr, $data['anrInstanceMetadataFields']); + // TODO: not sure if 'scales' and 'operationalRiskScales' have to be processed before or after the instances. + // perhaps they have to be cached first to be able to compare or the old ones preloaded. return $result; } + private function processKnowledgeBaseData(Entity\Anr $anr, array $knowledgeBaseData): void + { + $this->assetImportProcessor->processAssetsData($anr, $knowledgeBaseData['assets']); + $this->threatImportProcessor->processThreatsData($anr, $knowledgeBaseData['threats'], []); + $this->vulnerabilityImportProcessor->processVulnerabilitiesData($anr, $knowledgeBaseData['vulnerabilities']); + $this->referentialImportProcessor->processReferentialsData($anr, $knowledgeBaseData['referentails']); + $this->informationRiskImportProcessor->processInformationRisksData( + $anr, + $knowledgeBaseData['informationRisks'] + ); + $this->rolfTagImportProcessor->processRolfTagsData($anr, $knowledgeBaseData['rolfTags']); + $this->operationalRisksImportProcessor->processOperationalRisksData( + $anr, + $knowledgeBaseData['operationalRisks'] + ); + $this->recommendationImportProcessor->processRecommendationSetsData( + $anr, + $knowledgeBaseData['recommendationSets'] + ); + } + + private function processLibraryData(Entity\Anr $anr, array $libraryData, string $importMode): void + { + // TODO: 1. process categories, 2. objects & children, 3. objects compositions. 4. asset, + // 5. rolfTag, 6. RolfRisks + // TODO: for the export: if the KB is generated then we can reduce data inside the objects: asset (uuid), rolfTag (code) and nothing more. + // TODO: for the export: it seems the category is not needed inside of the object for library export as objects are inside of categories. + } + + private function processInstanceImport( + Entity\Anr $anr, + array $data, + ?Entity\Instance $parentInstance = null, + string $importMode = 'merge' + ): array { + $result = []; + + return $result; + } + + /* END new import structure process. */ + private function isImportTypeAnr(): bool { return $this->importType === 'anr'; @@ -202,7 +274,7 @@ private function isImportTypeAnr(): bool * @param array $data * @param Entity\Anr $anr * @param CoreEntity\InstanceSuperClass|null $parentInstance - * @param string $modeImport + * @param string $importMode * * @return bool|int */ @@ -210,9 +282,9 @@ private function importInstanceFromArray( array $data, Entity\Anr $anr, ?CoreEntity\InstanceSuperClass $parentInstance, - string $modeImport + string $importMode ) { - $monarcObject = $this->objectImportService->importFromArray($data['object'], $anr, $modeImport); + $monarcObject = $this->objectImportService->importFromArray($data['object'], $anr, $importMode); if ($monarcObject === null) { return false; } @@ -229,13 +301,13 @@ private function importInstanceFromArray( $this->prepareInstanceConsequences($data, $anr, $instance, $monarcObject, $includeEval); - $this->updateInstanceImpactsFromBrothers($instance, $modeImport); + $this->updateInstanceImpactsFromBrothers($instance, $importMode); $this->instanceTable->save($instance->refreshInheritedImpact(), false); $this->createSetOfRecommendations($data, $anr); - $this->processInstanceRisks($data, $anr, $instance, $monarcObject, $includeEval, $modeImport); + $this->processInstanceRisks($data, $anr, $instance, $monarcObject, $includeEval, $importMode); $this->processOperationalInstanceRisks($data, $anr, $instance, $monarcObject, $includeEval); @@ -251,12 +323,12 @@ private function importInstanceFromArray( $child['operationalRiskScales'] = $data['operationalRiskScales']; } } - $this->importInstanceFromArray($child, $anr, $instance, $modeImport); + $this->importInstanceFromArray($child, $anr, $instance, $importMode); } $this->anrInstanceService->updateChildrenImpactsAndRisks($instance); } - $this->instanceTable->getDb()->flush(); + $this->instanceTable->flush(); return $instance->getId(); } @@ -281,7 +353,7 @@ private function importAnrFromArray( $this->anrTable->save($anr, false); } } - $this->anrTable->getDb()->flush(); + $this->anrTable->flush(); } if (!empty($data['method']['data'])) { //Data of textboxes @@ -291,7 +363,7 @@ private function importAnrFromArray( $this->anrTable->save($anr, false); } } - $this->anrTable->getDb()->flush(); + $this->anrTable->flush(); } if (!empty($data['method']['interviews'])) { //Data of interviews @@ -312,7 +384,7 @@ private function importAnrFromArray( $anr->set($key, $v); $this->anrTable->save($anr, false); } - $this->anrTable->getDb()->flush(); + $this->anrTable->flush(); } if (!empty($data['method']['deliveries'])) { // Data of deliveries generation @@ -359,11 +431,11 @@ private function importAnrFromArray( $this->questionTable->getDb()->flush(); - /** @var Question[] $questions */ + /** @var Entity\Question[] $questions */ // TODO: findByAnr or better use the saved questions before, we don't need to query the db. $questions = $this->questionTable->getEntityByFields(['anr' => $anr->getId()]); - /** @var QuestionChoice[] $questionChoices */ + /** @var Entity\QuestionChoice[] $questionChoices */ // TODO: findByAnr or better use the saved questions before, we don't need to query the db. $questionChoices = $this->questionChoiceTable->getEntityByFields(['anr' => $anr->getId()]); @@ -400,9 +472,9 @@ private function importAnrFromArray( /* Process the evaluation of threats. */ if (!empty($data['method']['threats'])) { - $this->importCacheHelper->prepareThemesCacheData($anr); + $this->threatImportProcessor->prepareThemesCache($anr); foreach ($data['method']['threats'] as $threatUuid => $threatData) { - /** @var ?Threat $threat */ + /** @var ?Entity\Threat $threat */ $threat = $this->importCacheHelper->getItemFromArrayCache('threats', $threatUuid) ?: $this->threatTable->findByUuidAndAnr($threatUuid, $anr, false); if ($threat === null) { @@ -416,7 +488,7 @@ private function importAnrFromArray( ? $threatData['code'] . '-' . time() : $threatData['code']; - $threat = (new Threat()) + $threat = (new Entity\Threat()) ->setUuid($threatData['uuid']) ->setAnr($anr) ->setCode($threatData['code']) @@ -437,7 +509,7 @@ private function importAnrFromArray( $labelValue = $themeData[$labelKey]; $theme = $this->importCacheHelper->getItemFromArrayCache('themes_by_labels', $labelValue); if ($theme === null) { - $theme = (new Theme()) + $theme = (new Entity\Theme()) ->setAnr($anr) ->setLabels($themeData) ->setCreator($this->connectedUser->getEmail()); @@ -467,14 +539,15 @@ private function importAnrFromArray( if (!empty($data['referentials'])) { foreach ($data['referentials'] as $referentialUuid => $referentialData) { $referential = $this->importCacheHelper->getItemFromArrayCache('referentials', $referentialUuid) - ?: $this->referentialTable->findByAnrAndUuid($anr, $referentialUuid); + ?: $this->referentialTable->findByUuidAndAnr($referentialUuid, $anr); if ($referential === null) { - $referential = (new Referential($referentialData)) + // TODO: remove the instantiation with obj passing ... use the service + $referential = (new Entity\Referential($referentialData)) ->setUuid($referentialUuid) ->setAnr($anr) ->setCreator($this->connectedUser->getEmail()); - $this->referentialTable->saveEntity($referential, false); + $this->referentialTable->save($referential, false); } $this->importCacheHelper->addItemToArrayCache('referentials', $referential, $referentialUuid); @@ -502,10 +575,11 @@ private function importAnrFromArray( /* * Import the measures. */ + // TODO: use the ReferentialImportProcessor. if (isset($data['measures'])) { - foreach ($data['measures'] as $measureUuid => $measureData) { + foreach ($data['measures'] ?? [] as $measureUuid => $measureData) { $measure = $this->importCacheHelper->getItemFromArrayCache('measures', $measureUuid) - ?: $this->measureTable->findByAnrAndUuid($anr, $measureUuid); + ?: $this->measureTable->findByUuidAndAnr($measureUuid, $anr); $referential = $this->importCacheHelper ->getItemFromArrayCache('referentials', $measureData['referential']); if ($measure === null && $referential !== null) { @@ -525,7 +599,7 @@ private function importAnrFromArray( ->setReferential($referential) ->setCategory($soaCategory) ->setCreator($this->connectedUser->getEmail()); - $this->measureTable->saveEntity($measure, false); + $this->measureTable->save($measure, false); $this->importCacheHelper->addItemToArrayCache('measures', $measure, $measureUuid); @@ -538,33 +612,10 @@ private function importAnrFromArray( } } - $this->measureTable->getDb()->flush(); + $this->measureTable->flush(); } - // import the measuresmeasures - if (isset($data['measuresMeasures'])) { - foreach ($data['measuresMeasures'] as $measureMeasureData) { - if (!$this->measureMeasureTable->existsWithAnrFatherUuidAndChildUuid( - $anr, - $measureMeasureData['father'], - $measureMeasureData['child'] - )) { - $parentMeasure = $this->importCacheHelper - ->getItemFromArrayCache('measures', $measureMeasureData['father']); - $childMeasure = $this->importCacheHelper - ->getItemFromArrayCache('measures', $measureMeasureData['child']); - $measureMeasure = (new Entity\MeasureMeasure()) - ->setAnr($anr) - ->setFather($parentMeasure) - ->setChild($childMeasure) - ->setCreator($this->connectedUser->getEmail()); - $this->measureMeasureTable->saveEntity($measureMeasure, false); - } - } - - // TODO: avoid saving here. - $this->measureMeasureTable->getDb()->flush(); - } + $this->importMeasuresLinks($anr, $data); // import soaScaleComment $maxOrig = null; //used below for soas @@ -650,11 +701,11 @@ private function importAnrFromArray( if (isset($data['soas'])) { foreach ($data['soas'] as $soaData) { $measure = $this->importCacheHelper->getItemFromArrayCache('measures', $soaData['measure_id']) - ?: $this->measureTable->findByAnrAndUuid($anr, $soaData['measure_id']); + ?: $this->measureTable->findByUuidAndAnr($soaData['measure_id'], $anr); if ($measure !== null) { $soa = $this->soaTable->findByAnrAndMeasureUuid($anr, $soaData['measure_id']); if ($soa === null) { - $soa = (new Soa($soaData)) + $soa = (new Entity\Soa($soaData)) ->setAnr($anr) ->setMeasure($measure); } else { @@ -722,10 +773,10 @@ private function importAnrFromArray( $instance->set($t . 'h', 0); $instance->set($t, $this->approximate( $instance->get($t), - $scalesData['current'][Scale::TYPE_IMPACT]['min'], - $scalesData['current'][Scale::TYPE_IMPACT]['max'], - $scalesData['external'][Scale::TYPE_IMPACT]['min'], - $scalesData['external'][Scale::TYPE_IMPACT]['max'] + $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['min'], + $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['max'], + $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['min'], + $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['max'] )); } @@ -735,10 +786,10 @@ private function importAnrFromArray( foreach ($consequences as $conseq) { $conseq->set($t, $conseq->isHidden() ? -1 : $this->approximate( $conseq->get($t), - $scalesData['current'][Scale::TYPE_IMPACT]['min'], - $scalesData['current'][Scale::TYPE_IMPACT]['max'], - $scalesData['external'][Scale::TYPE_IMPACT]['min'], - $scalesData['external'][Scale::TYPE_IMPACT]['max'] + $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['min'], + $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['max'], + $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['min'], + $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['max'] )); $this->instanceConsequenceTable->save($conseq, false); } @@ -748,31 +799,31 @@ private function importAnrFromArray( foreach ($this->threatTable->findByAnr($anr) as $threat) { $threat->setQualification($this->approximate( $threat->getQualification(), - $scalesData['current'][Scale::TYPE_THREAT]['min'], - $scalesData['current'][Scale::TYPE_THREAT]['max'], - $scalesData['external'][Scale::TYPE_THREAT]['min'], - $scalesData['external'][Scale::TYPE_THREAT]['max'], + $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['min'], + $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['max'], + $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['min'], + $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['max'], )); $this->threatTable->save($threat, false); } // Informational Risks - /** @var InstanceRisk $instancesRisk */ + /** @var Entity\InstanceRisk $instancesRisk */ foreach ($this->instanceRiskTable->findByAnr($anr) as $instanceRisk) { $instanceRisk->setThreatRate($this->approximate( $instanceRisk->getThreatRate(), - $scalesData['current'][Scale::TYPE_THREAT]['min'], - $scalesData['current'][Scale::TYPE_THREAT]['max'], - $scalesData['external'][Scale::TYPE_THREAT]['min'], - $scalesData['external'][Scale::TYPE_THREAT]['max'], + $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['min'], + $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['max'], + $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['min'], + $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['max'], )); $oldVulRate = $instanceRisk->getVulnerabilityRate(); $instanceRisk->setVulnerabilityRate($this->approximate( $instanceRisk->getVulnerabilityRate(), - $scalesData['current'][Scale::TYPE_VULNERABILITY]['min'], - $scalesData['current'][Scale::TYPE_VULNERABILITY]['max'], - $scalesData['external'][Scale::TYPE_VULNERABILITY]['min'], - $scalesData['external'][Scale::TYPE_VULNERABILITY]['max'], + $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_VULNERABILITY]['min'], + $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_VULNERABILITY]['max'], + $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_VULNERABILITY]['min'], + $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_VULNERABILITY]['max'], )); $newVulRate = $instanceRisk->getVulnerabilityRate(); $instanceRisk->setReductionAmount( @@ -797,6 +848,10 @@ private function importAnrFromArray( usort($data['instances'], function ($a, $b) { return $a['instance']['position'] <=> $b['instance']['position']; }); + $this->assetImportProcessor->prepareAssetUuidsAndCodesCache($anr); + $this->threatImportProcessor->prepareThreatUuidsAndCodesCache($anr); + $this->threatImportProcessor->prepareThemesCache($anr); + $this->vulnerabilityImportProcessor->prepareVulnerabilityUuidsAndCodesCache($anr); foreach ($data['instances'] as $inst) { if ($first) { if ($data['with_eval'] && isset($data['scales'])) { @@ -825,7 +880,7 @@ private function importAnrFromArray( 'scaleImpactType' => $siType->getId() ]); if (empty($instanceConsequence)) { - $consequence = (new InstanceConsequence()) + $consequence = (new Entity\InstanceConsequence()) ->setAnr($anr) ->setInstance($instance) ->setObject($instance->getObject()) @@ -1018,9 +1073,17 @@ private function validateIfImportIsPossible(Entity\Anr $anr, ?Entity\Instance $p throw new Exception('Parent instance should be in the node tree and the analysis IDs are matched', 412); } - if (!empty($data['with_eval']) && empty($data['scales'])) { + if ((!empty($data['with_eval']) || !empty($data['withEval'])) && empty($data['scales'])) { throw new Exception('The importing file should include evaluation scales.', 412); } + + if (!$this->isMonarcVersionLowerThen('2.13.1') && $anr->getLanguageCode() !== $data['languageCode']) { + throw new Exception(sprintf( + 'The current analysis language "%s" should be the same as importing one "%s"', + $anr->getLanguageCode(), + $data['languageCode'] + ), 412); + } } private function prepareInstanceConsequences( @@ -1181,7 +1244,7 @@ private function processInstanceRisks( ? $threatData['code'] . '-' . time() : $threatData['code']; // TODO: inject and use the $this->threatService->create(); - $threat = (new Threat()) + $threat = (new Entity\Threat()) ->setUuid($threatData['uuid']) ->setAnr($anr) ->setCode($threatData['code']) @@ -1229,7 +1292,7 @@ private function processInstanceRisks( ? $vulnerabilityData['code'] . '-' . time() : $vulnerabilityData['code']; // TODO: inject and use $this->vulnerabilityService->create($anr, $vulnerabilityData); - $vulnerability = (new Vulnerability()) + $vulnerability = (new Entity\Vulnerability()) ->setUuid($vulnerabilityData['uuid']) ->setAnr($anr) ->setLabels($vulnerabilityData) @@ -1285,10 +1348,10 @@ private function processInstanceRisks( ? $instanceRiskData['threatRate'] : $this->approximate( $instanceRiskData['threatRate'], - $scalesData['external'][Scale::TYPE_THREAT]['min'], - $scalesData['external'][Scale::TYPE_THREAT]['max'], - $scalesData['current'][Scale::TYPE_THREAT]['min'], - $scalesData['current'][Scale::TYPE_THREAT]['max'] + $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['min'], + $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['max'], + $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['min'], + $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['max'] ) ); $instanceRisk->setVulnerabilityRate( @@ -1296,10 +1359,10 @@ private function processInstanceRisks( ? $instanceRiskData['vulnerabilityRate'] : $this->approximate( $instanceRiskData['vulnerabilityRate'], - $scalesData['external'][Scale::TYPE_VULNERABILITY]['min'], - $scalesData['external'][Scale::TYPE_VULNERABILITY]['max'], - $scalesData['current'][Scale::TYPE_VULNERABILITY]['min'], - $scalesData['current'][Scale::TYPE_VULNERABILITY]['max'] + $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_VULNERABILITY]['min'], + $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_VULNERABILITY]['max'], + $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_VULNERABILITY]['min'], + $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_VULNERABILITY]['max'] ) ); $instanceRisk->setIsThreatRateNotSetOrModifiedExternally((bool)$instanceRiskData['mh']); @@ -1333,7 +1396,7 @@ private function processInstanceRisks( $objectIdsBrothers = $this->instanceTable->findByAnrAndObject($anr, $instance->getObject()); // TODO: findBy... - /** @var InstanceRisk $instanceRiskBrothers */ + /** @var Entity\InstanceRisk $instanceRiskBrothers */ $instanceRiskBrothers = current($this->instanceRiskTable->getEntityByFields([ 'anr' => $anr->getId(), 'instance' => ['op' => 'IN', 'value' => $objectIdsBrothers], @@ -1360,7 +1423,8 @@ private function processInstanceRisks( $dataUpdate['comment'] = $instanceRiskBrothers->getComment(); } - $this->anrInstanceRiskService->update($instanceRisk->getId(), $dataUpdate); + // TODO: pass the object to update. + $this->anrInstanceRiskService->update($anr, $instanceRisk->getId(), $dataUpdate); } } @@ -1370,7 +1434,7 @@ private function processInstanceRisks( $recommendation = $this->processRecommendationDataLinkedToRisk( $anr, $reco, - $instanceRiskData['kindOfMeasure'] !== InstanceRiskSuperClass::KIND_NOT_TREATED + $instanceRiskData['kindOfMeasure'] !== CoreEntity\InstanceRiskSuperClass::KIND_NOT_TREATED ); // TODO: check why do we have it here: foreach ($instanceRisk->getRecommendationRisks() as $recommendationRisk) { @@ -1381,7 +1445,7 @@ private function processInstanceRisks( } } - $recommendationRisk = (new RecommendationRisk()) + $recommendationRisk = (new Entity\RecommendationRisk()) ->setAnr($anr) ->setInstance($instance) ->setInstanceRisk($instanceRisk) @@ -1401,7 +1465,7 @@ private function processInstanceRisks( if (!empty($brotherInstances)) { foreach ($brotherInstances as $brotherInstance) { // Get the risks of brothers - /** @var InstanceRisk[] $brothers */ + /** @var Entity\InstanceRisk[] $brothers */ if ($instanceRisk->isSpecific()) { $brothers = $this->instanceRiskTable->findByInstanceAndInstanceRiskRelations( $brotherInstance, @@ -1419,7 +1483,7 @@ private function processInstanceRisks( $recommendationRiskBrother = $this->recommendationRiskTable ->findByInstanceRiskAndRecommendation($brother, $recommendation); if ($recommendationRiskBrother === null) { - $recommendationRiskBrother = (new RecommendationRisk()) + $recommendationRiskBrother = (new Entity\RecommendationRisk()) ->setAnr($anr) ->setInstance($brotherInstance) ->setInstanceRisk($brother) @@ -1455,7 +1519,7 @@ private function processInstanceRisks( ); foreach ($instanceRiskBrothers as $instanceRiskBrother) { - /** @var RecommendationRisk[] $brotherRecoRisks */ + /** @var Entity\RecommendationRisk[] $brotherRecoRisks */ // Get recommendation of brother $brotherRecoRisks = $this->recommendationRiskTable->getEntityByFields([ 'anr' => $anr->getId(), @@ -1475,7 +1539,7 @@ private function processInstanceRisks( ); if ($recommendationRisk === null) { - $recommendationRisk = (new RecommendationRisk()) + $recommendationRisk = (new Entity\RecommendationRisk()) ->setAnr($anr) ->setInstance($instance) ->setInstanceRisk($brotherRecoRisk->getInstanceRisk()) @@ -1507,7 +1571,7 @@ private function processInstanceRisks( // TODO: replace all the queries with QueryBuilder. Review the logic. // Get recommendations of brothers - /** @var RecommendationRisk[] $exitingRecoRisks */ + /** @var Entity\RecommendationRisk[] $exitingRecoRisks */ $exitingRecoRisks = $this->recommendationRiskTable->getEntityByFields([ 'anr' => $anr->getId(), 'asset' => ['anr' => $anr->getId(), 'uuid' => $instanceRisk->getAsset()->getUuid()], @@ -1521,7 +1585,7 @@ private function processInstanceRisks( } } - /** @var RecommendationRisk $recommendationRiskToCreate */ + /** @var Entity\RecommendationRisk $recommendationRiskToCreate */ foreach ($recoToCreate as $recommendationRiskToCreate) { // Check if reco-risk link exist $recoCreated = $this->recommendationRiskTable->getEntityByFields([ @@ -1564,7 +1628,7 @@ private function processInstanceRisks( ], ])); - $recommendationRisk = (new RecommendationRisk()) + $recommendationRisk = (new Entity\RecommendationRisk()) ->setAnr($anr) ->setInstance($instance) ->setInstanceRisk($instanceRiskSpecific) @@ -1587,9 +1651,9 @@ private function processInstanceRisks( private function processOperationalInstanceRisks( array $data, - Anr $anr, - Instance $instance, - MonarcObject $monarcObject, + Entity\Anr $anr, + Entity\Instance $instance, + Entity\MonarcObject $monarcObject, bool $includeEval ): void { if (empty($data['risksop'])) { @@ -1604,12 +1668,12 @@ private function processOperationalInstanceRisks( if ($includeEval && !$this->isImportTypeAnr()) { $externalOperationalRiskScalesData = $this->getExternalOperationalRiskScalesData($anr, $data); $areScalesLevelsOfLikelihoodDifferent = $this->areScalesLevelsOfTypeDifferent( - OperationalRiskScale::TYPE_LIKELIHOOD, + CoreEntity\OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD, $operationalRiskScalesData, $externalOperationalRiskScalesData ); $areImpactScaleTypesValuesDifferent = $this->areScaleTypeValuesDifferent( - OperationalRiskScale::TYPE_IMPACT, + CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT, $operationalRiskScalesData, $externalOperationalRiskScalesData ); @@ -1628,7 +1692,7 @@ private function processOperationalInstanceRisks( ]; foreach ($data['risksop'] as $operationalRiskData) { - $operationalInstanceRisk = (new InstanceRiskOp()) + $operationalInstanceRisk = (new Entity\InstanceRiskOp()) ->setAnr($anr) ->setInstance($instance) ->setObject($monarcObject) @@ -1668,8 +1732,8 @@ private function processOperationalInstanceRisks( if ($areScalesLevelsOfLikelihoodDifferent) { $this->adjustOperationalRisksProbabilityScales( $operationalInstanceRisk, - $externalOperationalRiskScalesData[OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD], - $operationalRiskScalesData[OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD] + $externalOperationalRiskScalesData[CoreEntity\OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD], + $operationalRiskScalesData[CoreEntity\OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD] ); } @@ -1684,11 +1748,11 @@ private function processOperationalInstanceRisks( } } - $impactScale = $operationalRiskScalesData[OperationalRiskScale::TYPE_IMPACT]; + $impactScale = $operationalRiskScalesData[CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT]; foreach ($impactScale['operationalRiskScaleTypes'] as $index => $scaleType) { - /** @var OperationalRiskScaleType $operationalRiskScaleType */ + /** @var Entity\OperationalRiskScaleType $operationalRiskScaleType */ $operationalRiskScaleType = $scaleType['object']; - $operationalInstanceRiskScale = (new OperationalInstanceRiskScale()) + $operationalInstanceRiskScale = (new Entity\OperationalInstanceRiskScale()) ->setAnr($anr) ->setOperationalRiskScaleType($operationalRiskScaleType) ->setOperationalInstanceRisk($operationalInstanceRisk) @@ -1721,7 +1785,9 @@ private function processOperationalInstanceRisks( /* We convert from the importing new scales to the current anr scales. */ $this->adjustOperationalInstanceRisksScales( $operationalInstanceRiskScale, - $externalOperationalRiskScalesData[OperationalRiskScale::TYPE_IMPACT], + $externalOperationalRiskScalesData[ + CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT + ], $impactScale ); } @@ -1735,7 +1801,9 @@ private function processOperationalInstanceRisks( /* We convert from the importing new scales to the current anr scales. */ $this->adjustOperationalInstanceRisksScales( $operationalInstanceRiskScale, - $externalOperationalRiskScalesData[OperationalRiskScale::TYPE_IMPACT], + $externalOperationalRiskScalesData[ + CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT + ], $impactScale ); } @@ -1758,7 +1826,7 @@ private function processOperationalInstanceRisks( || $scalesValueData['targetedValue'] !== -1 ) { $labelTranslationKey = (string)Uuid::uuid4(); - $operationalRiskScaleType = (new OperationalRiskScaleType()) + $operationalRiskScaleType = (new Entity\OperationalRiskScaleType()) ->setAnr($anr) ->setOperationalRiskScale($impactScale['object']) ->setLabelTranslationKey($labelTranslationKey) @@ -1786,7 +1854,7 @@ private function processOperationalInstanceRisks( ); } - $operationalInstanceRiskScale = (new OperationalInstanceRiskScale()) + $operationalInstanceRiskScale = (new Entity\OperationalInstanceRiskScale()) ->setAnr($anr) ->setOperationalInstanceRisk($operationalInstanceRisk) ->setOperationalRiskScaleType($operationalRiskScaleType) @@ -1798,7 +1866,9 @@ private function processOperationalInstanceRisks( $this->adjustOperationalInstanceRisksScales( $operationalInstanceRiskScale, - $externalOperationalRiskScalesData[OperationalRiskScale::TYPE_IMPACT], + $externalOperationalRiskScalesData[ + CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT + ], $impactScale ); @@ -1819,7 +1889,7 @@ private function processOperationalInstanceRisks( ); foreach ($operationalInstanceRisks as $operationalInstanceRiskToUpdate) { if ($operationalInstanceRiskToUpdate->getId() !== $operationalInstanceRisk->getId()) { - $operationalInstanceRiskScale = (new OperationalInstanceRiskScale()) + $operationalInstanceRiskScale = (new Entity\OperationalInstanceRiskScale()) ->setAnr($anr) ->setOperationalInstanceRisk($operationalInstanceRiskToUpdate) ->setOperationalRiskScaleType($operationalRiskScaleType) @@ -1869,7 +1939,7 @@ private function processOperationalInstanceRisks( } private function matchAndGetOperationalRiskScaleTypesMap( - Anr $anr, + Entity\Anr $anr, array $operationalRiskScalesData, array $externalOperationalRiskScalesData ): array { @@ -1883,14 +1953,14 @@ private function matchAndGetOperationalRiskScaleTypesMap( [Translation::OPERATIONAL_RISK_SCALE_TYPE], $anrLanguageCode ); - $scaleTypesData = $operationalRiskScalesData[OperationalRiskScale::TYPE_IMPACT] + $scaleTypesData = $operationalRiskScalesData[CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT] ['operationalRiskScaleTypes']; - $externalScaleTypesData = - $externalOperationalRiskScalesData[OperationalRiskScale::TYPE_IMPACT]['operationalRiskScaleTypes']; + $externalScaleTypesData = $externalOperationalRiskScalesData + [CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT]['operationalRiskScaleTypes']; foreach ($externalScaleTypesData as $externalScaleTypeData) { $isMatched = false; foreach ($scaleTypesData as $scaleTypeData) { - /** @var OperationalRiskScaleType $scaleType */ + /** @var Entity\OperationalRiskScaleType $scaleType */ $scaleType = $scaleTypeData['object']; $scaleTypeTranslation = $scaleTypesTranslations[$scaleType->getLabelTranslationKey()]; if ($externalScaleTypeData['translation']['value'] === $scaleTypeTranslation->getValue()) { @@ -1956,18 +2026,15 @@ public function areScaleTypeValuesDifferent( /** * Update the instance impacts from brothers for global assets. - * - * @param InstanceSuperClass $instance - * @param string $modeImport */ - private function updateInstanceImpactsFromBrothers(InstanceSuperClass $instance, string $modeImport): void + private function updateInstanceImpactsFromBrothers(Entity\Instance $instance, string $modeImport): void { if ($modeImport === 'merge' && $instance->getObject()->isScopeGlobal()) { $instanceBrothers = $this->getInstanceBrothers($instance); if (!empty($instanceBrothers)) { // Update impacts of the instance. We use only one brother global instance as the impacts are the same. $instanceBrother = current($instanceBrothers); - foreach (InstanceConsequence::getAvailableScalesCriteria() as $scaleCriteria) { + foreach (Entity\InstanceConsequence::getAvailableScalesCriteria() as $scaleCriteria) { if ($instanceBrother->{'getInherited' . $scaleCriteria}() === 0) { $instance->{'setInherited' . $scaleCriteria}(0); $instance->{'set' . $scaleCriteria}($instanceBrother->{'get' . $scaleCriteria}()); @@ -2025,13 +2092,13 @@ private function getInstanceBrothers(InstanceSuperClass $instance): array private function createInstanceRiskFromData( array $instanceRiskData, - Anr $anr, - Instance $instance, - Asset $asset, - Threat $threat, - Vulnerability $vulnerability - ): InstanceRisk { - $instanceRisk = (new InstanceRisk()) + Entity\Anr $anr, + Entity\Instance $instance, + Entity\Asset $asset, + Entity\Threat $threat, + Entity\Vulnerability $vulnerability + ): Entity\InstanceRisk { + $instanceRisk = (new Entity\InstanceRisk()) ->setAnr($anr) ->setInstance($instance) ->setAsset($asset) @@ -2123,7 +2190,7 @@ private function setAndValidateMonarcVersion($data): void } } - private function adjustOperationalRisksScaleValuesBasedOnNewScales(Anr $anr, array $data): void + private function adjustOperationalRisksScaleValuesBasedOnNewScales(Entity\Anr $anr, array $data): void { $operationalInstanceRisks = $this->instanceRiskOpTable->findByAnr($anr); if (!empty($operationalInstanceRisks)) { @@ -2133,15 +2200,15 @@ private function adjustOperationalRisksScaleValuesBasedOnNewScales(Anr $anr, arr foreach ($operationalInstanceRisks as $operationalInstanceRisk) { $this->adjustOperationalRisksProbabilityScales( $operationalInstanceRisk, - $currentOperationalRiskScalesData[OperationalRiskScale::TYPE_LIKELIHOOD], - $externalOperationalRiskScalesData[OperationalRiskScale::TYPE_LIKELIHOOD] + $currentOperationalRiskScalesData[CoreEntity\OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD], + $externalOperationalRiskScalesData[CoreEntity\OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD] ); foreach ($operationalInstanceRisk->getOperationalInstanceRiskScales() as $instanceRiskScale) { $this->adjustOperationalInstanceRisksScales( $instanceRiskScale, - $currentOperationalRiskScalesData[OperationalRiskScale::TYPE_IMPACT], - $externalOperationalRiskScalesData[OperationalRiskScale::TYPE_IMPACT] + $currentOperationalRiskScalesData[CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT], + $externalOperationalRiskScalesData[CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT] ); } @@ -2150,12 +2217,12 @@ private function adjustOperationalRisksScaleValuesBasedOnNewScales(Anr $anr, arr $this->anrInstanceRiskOpService->updateRiskCacheValues($operationalInstanceRisk); } - $this->instanceRiskOpTable->getDb()->flush(); + $this->instanceRiskOpTable->flush(); } } private function adjustOperationalRisksProbabilityScales( - InstanceRiskOp $operationalInstanceRisk, + Entity\InstanceRiskOp $operationalInstanceRisk, array $fromOperationalRiskScalesData, array $toOperationalRiskScalesData ): void { @@ -2171,7 +2238,7 @@ private function adjustOperationalRisksProbabilityScales( } private function adjustOperationalInstanceRisksScales( - OperationalInstanceRiskScale $instanceRiskScale, + Entity\OperationalInstanceRiskScale $instanceRiskScale, array $fromOperationalRiskScalesData, array $toOperationalRiskScalesData ): void { @@ -2245,21 +2312,22 @@ private function getCurrentOperationalRiskScalesData(Anr $anr): array * Prepare and cache the new scales for the future use. * The format can be different, depends on the version (before v2.11.0 and after). */ - private function getExternalOperationalRiskScalesData(Anr $anr, array $data): array + private function getExternalOperationalRiskScalesData(Entity\Anr $anr, array $data): array { if (empty($this->cachedData['externalOperationalRiskScalesData'])) { /* Populate with informational risks scales in case if there is an import of file before v2.11.0. */ $scalesDataResult = [ - OperationalRiskScale::TYPE_IMPACT => [ + CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT => [ 'min' => 0, - 'max' => $data['scales'][Scale::TYPE_IMPACT]['max'] - $data['scales'][Scale::TYPE_IMPACT]['min'], + 'max' => $data['scales'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['max'] + - $data['scales'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['min'], 'commentsIndexToValueMap' => [], 'operationalRiskScaleTypes' => [], 'operationalRiskScaleComments' => [], ], - OperationalRiskScale::TYPE_LIKELIHOOD => [ - 'min' => $data['scales'][Scale::TYPE_THREAT]['min'], - 'max' => $data['scales'][Scale::TYPE_THREAT]['max'], + CoreEntity\OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD => [ + 'min' => $data['scales'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['min'], + 'max' => $data['scales'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['max'], 'commentsIndexToValueMap' => [], 'operationalRiskScaleTypes' => [], 'operationalRiskScaleComments' => [], @@ -2291,13 +2359,15 @@ private function getExternalOperationalRiskScalesData(Anr $anr, array $data): ar } else { /* Convert comments and types from informational risks to operational (new format). */ $anrLanguageCode = $this->getAnrLanguageCode($anr); - $scaleMin = $data['scales'][Scale::TYPE_IMPACT]['min']; + $scaleMin = $data['scales'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['min']; foreach ($this->scaleImpactTypeTable->findByAnrOrderedByPosition($anr) as $index => $scaleImpactType) { - if ($scaleImpactType->isSys() - && \in_array($scaleImpactType->getType(), ScaleImpactType::getScaleImpactTypesRolfp(), true) - ) { + if ($scaleImpactType->isSys() && \in_array( + $scaleImpactType->getType(), + CoreEntity\ScaleImpactTypeSuperClass::getScaleImpactTypesRolfp(), + true + )) { $labelTranslationKey = (string)Uuid::uuid4(); - $scalesDataResult[Scale::TYPE_IMPACT]['operationalRiskScaleTypes'][$index] = [ + $scalesDataResult[CoreEntity\ScaleSuperClass::TYPE_IMPACT]['operationalRiskScaleTypes'][$index] = [ 'id' => $scaleImpactType->getId(), 'isHidden' => $scaleImpactType->isHidden(), 'labelTranslationKey' => $labelTranslationKey, @@ -2311,11 +2381,15 @@ private function getExternalOperationalRiskScalesData(Anr $anr, array $data): ar } foreach ($data['scalesComments'] as $scaleComment) { $scaleType = $scaleComment['scale']['type']; - if (!\in_array($scaleType, [Scale::TYPE_IMPACT, Scale::TYPE_THREAT], true)) { + if (!\in_array( + $scaleType, + [CoreEntity\ScaleSuperClass::TYPE_IMPACT, CoreEntity\ScaleSuperClass::TYPE_THREAT], + true + )) { continue; } - if ($scaleType === Scale::TYPE_THREAT) { + if ($scaleType === CoreEntity\ScaleSuperClass::TYPE_THREAT) { $commentTranslationKey = (string)Uuid::uuid4(); $scalesDataResult[$scaleType]['operationalRiskScaleComments'][] = [ 'id' => $scaleComment['id'], @@ -2329,7 +2403,9 @@ private function getExternalOperationalRiskScalesData(Anr $anr, array $data): ar 'value' => $scaleComment['comment' . $anr->getLanguage()] ?? '', ], ]; - } elseif ($scaleType === Scale::TYPE_IMPACT && $scaleComment['val'] >= $scaleMin) { + } elseif ($scaleType === CoreEntity\ScaleSuperClass::TYPE_IMPACT + && $scaleComment['val'] >= $scaleMin + ) { $commentTranslationKey = (string)Uuid::uuid4(); $scaleIndex = $scaleComment['val'] - $scaleMin; $scaleTypePosition = $scaleComment['scaleImpactType']['position']; @@ -2463,7 +2539,7 @@ private function updateScalesAndComments(Entity\Anr $anr, array $data): void $this->cachedData['scales'] = []; } - private function updateOperationalRisksScalesAndRelatedInstances(Anr $anr, array $data): void + private function updateOperationalRisksScalesAndRelatedInstances(Entity\Anr $anr, array $data): void { $operationalRiskScales = $this->operationalRiskScaleTable->findByAnr($anr); $anrLanguageCode = $this->getAnrLanguageCode($anr); @@ -2496,7 +2572,7 @@ private function updateOperationalRisksScalesAndRelatedInstances(Anr $anr, array if ($operationalRiskScaleType === null) { $isScaleTypeMatched = false; $labelTranslationKey = (string)Uuid::uuid4(); - $operationalRiskScaleType = (new OperationalRiskScaleType()) + $operationalRiskScaleType = (new Entity\OperationalRiskScaleType()) ->setAnr($anr) ->setOperationalRiskScale($operationalRiskScale) ->setLabelTranslationKey($labelTranslationKey) @@ -2541,7 +2617,7 @@ private function updateOperationalRisksScalesAndRelatedInstances(Anr $anr, array $operationalInstanceRisks = $this->instanceRiskOpTable->findByAnr($anr); foreach ($operationalInstanceRisks as $operationalInstanceRisk) { foreach ($createdScaleTypes as $createdScaleType) { - $operationalInstanceRiskScale = (new OperationalInstanceRiskScale()) + $operationalInstanceRiskScale = (new Entity\OperationalInstanceRiskScale()) ->setAnr($anr) ->setOperationalRiskScaleType($createdScaleType) ->setOperationalInstanceRisk($operationalInstanceRisk) @@ -2568,7 +2644,7 @@ private function updateOperationalRisksScalesAndRelatedInstances(Anr $anr, array } /* Manage a case when the scale (probability) is not matched and level higher than external. */ if ($maxIndexForLikelihood !== 0 - && $operationalRiskScale->getType() === OperationalRiskScale::TYPE_LIKELIHOOD + && $operationalRiskScale->getType() === CoreEntity\OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD ) { foreach ($operationalRiskScale->getOperationalRiskScaleComments() as $comment) { if ($comment->getScaleIndex() >= $maxIndexForLikelihood) { @@ -2593,8 +2669,8 @@ private function updateOperationalRisksScalesAndRelatedInstances(Anr $anr, array /* The scales type was not matched and the current scales level is lower then external, so we need to create missing empty scales comments. */ $commentIndex = $operationalRiskScale->getMax() + $currentScaleLevelDifferenceFromExternal + 1; - $commentIndexToValueMap = $externalOperationalScalesData[OperationalRiskScale::TYPE_IMPACT] - ['commentsIndexToValueMap']; + $commentIndexToValueMap = $externalOperationalScalesData + [CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT]['commentsIndexToValueMap']; while ($commentIndex <= $operationalRiskScale->getMax()) { $this->createOrUpdateOperationalRiskScaleComment( $anr, @@ -2619,8 +2695,8 @@ private function updateOperationalRisksScalesAndRelatedInstances(Anr $anr, array } if ($currentScaleLevelDifferenceFromExternal > 0) { - $commentIndexToValueMap = $externalOperationalScalesData[OperationalRiskScale::TYPE_IMPACT] - ['commentsIndexToValueMap']; + $commentIndexToValueMap = $externalOperationalScalesData + [CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT]['commentsIndexToValueMap']; $maxValue = $commentIndexToValueMap[$operationalRiskScale->getMax()]; if (\array_key_exists($operationalRiskScaleType->getId(), $matchedScaleTypes)) { /* The scales type was matched and the current scales level is higher then external, @@ -2662,13 +2738,13 @@ private function updateOperationalRisksScalesAndRelatedInstances(Anr $anr, array } private function createOrUpdateOperationalRiskScaleComment( - Anr $anr, + Entity\Anr $anr, bool $isMatchRequired, - OperationalRiskScale $operationalRiskScale, + Entity\OperationalRiskScale $operationalRiskScale, array $scaleCommentData, iterable $scaleCommentsToMatchWith, array $scalesTranslations, - ?OperationalRiskScaleType $operationalRiskScaleType = null + ?Entity\OperationalRiskScaleType $operationalRiskScaleType = null ): void { $operationalRiskScaleComment = null; if ($isMatchRequired) { @@ -2682,7 +2758,7 @@ private function createOrUpdateOperationalRiskScaleComment( if ($operationalRiskScaleComment === null) { $anrLanguageCode = $this->getAnrLanguageCode($anr); $commentTranslationKey = (string)Uuid::uuid4(); - $operationalRiskScaleComment = (new OperationalRiskScaleComment()) + $operationalRiskScaleComment = (new Entity\OperationalRiskScaleComment()) ->setAnr($anr) ->setOperationalRiskScale($operationalRiskScale) ->setCommentTranslationKey($commentTranslationKey) @@ -2711,16 +2787,16 @@ private function createOrUpdateOperationalRiskScaleComment( /** * @param array $scaleTypeData - * @param OperationalRiskScaleType[] $operationalRiskScaleTypes + * @param Entity\OperationalRiskScaleType[] $operationalRiskScaleTypes * @param Translation[] $scalesTranslations * - * @return OperationalRiskScaleType|null + * @return Entity\OperationalRiskScaleType|null */ private function matchScaleTypeDataWithScaleTypesList( array $scaleTypeData, iterable $operationalRiskScaleTypes, array $scalesTranslations - ): ?OperationalRiskScaleType { + ): ?Entity\OperationalRiskScaleType { foreach ($operationalRiskScaleTypes as $operationalRiskScaleType) { if (isset($scalesTranslations[$operationalRiskScaleType->getLabelTranslationKey()])) { $translation = $scalesTranslations[$operationalRiskScaleType->getLabelTranslationKey()]; @@ -2734,11 +2810,11 @@ private function matchScaleTypeDataWithScaleTypesList( } private function matchScaleCommentDataWithScaleCommentsList( - OperationalRiskScale $operationalRiskScale, + Entity\OperationalRiskScale $operationalRiskScale, array $scaleTypeCommentData, iterable $operationalRiskScaleComments, array $scalesTranslations - ): ?OperationalRiskScaleComment { + ): ?Entity\OperationalRiskScaleComment { foreach ($operationalRiskScaleComments as $operationalRiskScaleComment) { if ($operationalRiskScale->getId() !== $operationalRiskScaleComment->getOperationalRiskScale()->getId()) { continue; @@ -2763,7 +2839,7 @@ private function getAnrLanguageCode(Anr $anr): string return strtolower($this->configService->getLanguageCodes()[$anr->getLanguage()]); } - private function createInstanceMetadata(InstanceSuperClass $instance, $data): void + private function createInstanceMetadata(Entity\Instance $instance, $data): void { $anr = $instance->getAnr(); $anrLanguageCode = $this->getAnrLanguageCode($anr); @@ -2796,7 +2872,7 @@ private function createInstanceMetadata(InstanceSuperClass $instance, $data): vo ); if ($instanceMetadataObject === null) { $commentTranslationKey = (string)Uuid::uuid4(); - $instanceMetadataObject = (new InstanceMetadata()) + $instanceMetadataObject = (new Entity\InstanceMetadata()) ->setInstance($instance) ->setAnrInstanceMetadataField($metadata) ->setCommentTranslationKey($commentTranslationKey) @@ -2829,46 +2905,35 @@ private function createInstanceMetadata(InstanceSuperClass $instance, $data): vo $this->instanceMetadataTable->flush(); } - private function createAnrMetadataOnInstances(Anr $anr, $data): void + private function createAnrMetadataOnInstances(Entity\Anr $anr, array $data): void { - $anrLanguageCode = $this->getAnrLanguageCode($anr); $labels = array_column($this->cachedData['anrMetadataOnInstances'], 'label'); - foreach ($data as $v) { - if (!\in_array($v['label'], $labels, true)) { - $labelTranslationKey = (string)Uuid::uuid4(); - $metadata = (new AnrInstanceMetadataField()) + foreach ($data as $anrInstanceMetadataData) { + if (!\in_array($anrInstanceMetadataData['label'], $labels, true)) { + $metadata = (new Entity\AnrInstanceMetadataField()) ->setAnr($anr) - ->setLabelTranslationKey($labelTranslationKey) + ->setLabel($anrInstanceMetadataData['label']) ->setCreator($this->connectedUser->getEmail()) ->setIsDeletable(true); $this->anrInstanceMetadataFieldTable->save($metadata, false); - $translation = (new Translation()) - ->setAnr($anr) - ->setType(Translation::ANR_METADATAS_ON_INSTANCES) - ->setKey($labelTranslationKey) - ->setValue($v['label']) - ->setLang($anrLanguageCode) - ->setCreator($this->connectedUser->getEmail()); - $this->translationTable->save($translation, false); - $this->cachedData['anrMetadataOnInstances'][] = - [ - 'id' => $metadata->getId(), - 'label' => $v['label'], - 'object' => $metadata, - 'translation' => $translation, - ]; + // TODO: simplify the cache by $this->cachedData['anrMetadataOnInstances'][$anrInstanceMetadataData['id']]] = $metadata; + $this->cachedData['anrMetadataOnInstances'][] = [ + 'id' => $metadata->getId(), + 'label' => $anrInstanceMetadataData['label'], + 'object' => $metadata, + ]; } } $this->anrInstanceMetadataFieldTable->flush(); } - private function getCurrentAnrMetadataOnInstances(Anr $anr): array + private function getCurrentAnrMetadataOnInstances(Entity\Anr $anr): array { $this->cachedData['currentAnrMetadataOnInstances'] = []; $anrMetadatasOnInstancesTranslations = $this->translationTable->findByAnrTypesAndLanguageIndexedByKey( $anr, - [TranslationSuperClass::ANR_METADATAS_ON_INSTANCES], + [Translation::ANR_METADATAS_ON_INSTANCES], $this->getAnrLanguageCode($anr) ); @@ -2888,7 +2953,7 @@ private function getCurrentAnrMetadataOnInstances(Anr $anr): array /** * Updates the instance impacts from brothers for global assets. */ - private function updateInstanceMetadataFromBrothers(InstanceSuperClass $instance): void + private function updateInstanceMetadataFromBrothers(Entity\Instance $instance): void { if ($instance->getObject()->isScopeGlobal()) { $instanceBrothers = $this->getInstanceBrothers($instance); @@ -2901,7 +2966,7 @@ private function updateInstanceMetadataFromBrothers(InstanceSuperClass $instance $instanceMetadata = $this->instanceMetadataTable ->findByInstanceAndMetadataField($instance, $metadata); if ($instanceMetadata === null) { - $instanceMetadata = (new InstanceMetadata()) + $instanceMetadata = (new Entity\InstanceMetadata()) ->setInstance($instance) ->setAnrInstanceMetadataField($metadata) ->setCommentTranslationKey($instanceMetadataFromBrother->getCommentTranslationKey()) @@ -2917,8 +2982,8 @@ private function updateInstanceMetadataFromBrothers(InstanceSuperClass $instance * Updates the instance impacts from instance to Brothers for global assets. */ private function updateInstanceMetadataToBrothers( - InstanceSuperClass $instance, - InstanceMetadata $instanceMetadata + Entity\Instance $instance, + Entity\InstanceMetadata $instanceMetadata ): void { if ($instance->getObject()->isScopeGlobal()) { $instanceBrothers = $this->getInstanceBrothers($instance); @@ -2928,7 +2993,7 @@ private function updateInstanceMetadataToBrothers( $instanceMetadataBrother = $this->instanceMetadataTable ->findByInstanceAndMetadataField($instanceBrother, $metadata); if ($instanceMetadataBrother === null) { - $instanceMetadataBrother = (new InstanceMetadata()) + $instanceMetadataBrother = (new Entity\InstanceMetadata()) ->setInstance($instanceBrother) ->setAnrInstanceMetadataField($metadata) ->setCommentTranslationKey($instanceMetadata->getCommentTranslationKey()) @@ -2940,7 +3005,7 @@ private function updateInstanceMetadataToBrothers( } } - private function getCurrentSoaScaleCommentData(Anr $anr): array + private function getCurrentSoaScaleCommentData(Entity\Anr $anr): array { if (empty($this->cachedData['currentSoaScaleCommentData'])) { $scales = $this->soaScaleCommentTable->findByAnrOrderByIndex($anr); @@ -2959,7 +3024,7 @@ private function getCurrentSoaScaleCommentData(Anr $anr): array return $this->cachedData['currentSoaScaleCommentData'] ?? []; } - private function mergeSoaScaleComment(array $newScales, Anr $anr) + private function mergeSoaScaleComment(array $newScales, Entity\Anr $anr) { $soaScaleCommentTranslations = $this->translationTable->findByAnrTypesAndLanguageIndexedByKey( $anr, @@ -3021,4 +3086,30 @@ private function mergeSoaScaleComment(array $newScales, Anr $anr) } $this->soaScaleCommentTable->flush(); } + + // TODO: replace to use measure father/masterMeasure or child/linkedMeasure existence in the cached data. + // TODO: use the ReferentialImportProcessor. + private function importMeasuresLinks(Entity\Anr $anr, array $data): void + { + foreach ($data['measuresMeasures'] ?? [] as $measureMeasureData) { + if (!$this->measureMeasureTable->existsWithAnrMasterMeasureUuidAndLinkedMeasureUuid( + $anr, + $measureMeasureData['father'] ?? $measureMeasureData['masterMeasure'], + $measureMeasureData['child'] ?? $measureMeasureData['linkedMeasure'] + )) { + /** @var Entity\Measure $masterMeasure */ + $masterMeasure = $this->importCacheHelper->getItemFromArrayCache( + 'measures', + $measureMeasureData['father'] + ); + /** @var Entity\Measure $linkedMeasure */ + $linkedMeasure = $this->importCacheHelper->getItemFromArrayCache( + 'measures', + $measureMeasureData['child'] + ); + $masterMeasure->addLinkedMeasure($linkedMeasure); + $this->measureTable->save($masterMeasure, false); + } + } + } } diff --git a/src/Import/Service/ObjectImportService.php b/src/Import/Service/ObjectImportService.php index 5a7400a7..f81ed15f 100644 --- a/src/Import/Service/ObjectImportService.php +++ b/src/Import/Service/ObjectImportService.php @@ -14,7 +14,6 @@ use Monarc\Core\Service\ConnectedUserService; use Monarc\FrontOffice\Import\Helper\ImportCacheHelper; use Monarc\FrontOffice\Entity; -use Monarc\FrontOffice\Model\Table as DeprecatedTable; use Monarc\FrontOffice\Table; use Monarc\FrontOffice\Service\SoaCategoryService; @@ -26,15 +25,15 @@ class ObjectImportService private AssetImportService $assetImportService; - private DeprecatedTable\RolfTagTable $rolfTagTable; + private Table\RolfTagTable $rolfTagTable; - private DeprecatedTable\RolfRiskTable $rolfRiskTable; + private Table\RolfRiskTable $rolfRiskTable; - private DeprecatedTable\MeasureTable $measureTable; + private Table\MeasureTable $measureTable; private Table\ObjectObjectTable $objectObjectTable; - private DeprecatedTable\ReferentialTable $referentialTable; + private Table\ReferentialTable $referentialTable; private Table\ObjectCategoryTable $objectCategoryTable; @@ -48,10 +47,10 @@ public function __construct( Table\MonarcObjectTable $monarcObjectTable, Table\ObjectObjectTable $objectObjectTable, AssetImportService $assetImportService, - DeprecatedTable\RolfTagTable $rolfTagTable, - DeprecatedTable\RolfRiskTable $rolfRiskTable, - DeprecatedTable\MeasureTable $measureTable, - DeprecatedTable\ReferentialTable $referentialTable, + Table\RolfTagTable $rolfTagTable, + Table\RolfRiskTable $rolfRiskTable, + Table\MeasureTable $measureTable, + Table\ReferentialTable $referentialTable, Table\ObjectCategoryTable $objectCategoryTable, ConnectedUserService $connectedUserService, ImportCacheHelper $importCacheHelper, @@ -273,7 +272,7 @@ private function processRolfTagAndRolfRisks(array $data, Entity\Anr $anr): ?Enti $this->processMeasuresAndReferentialData($anr, $rolfRisk, $rolfRiskData['measures']); } - $this->rolfRiskTable->saveEntity($rolfRisk, false); + $this->rolfRiskTable->save($rolfRisk, false); /* The cache with IDs is required to link them with operational risks in InstanceImportService. */ $this->importCacheHelper->addItemToArrayCache('rolf_risks_by_old_ids', $rolfRisk, (int)$riskId); @@ -283,7 +282,7 @@ private function processRolfTagAndRolfRisks(array $data, Entity\Anr $anr): ?Enti } } - $this->rolfTagTable->saveEntity($rolfTag, false); + $this->rolfTagTable->save($rolfTag, false); $this->importCacheHelper->addItemToArrayCache('rolfTags', $rolfTag, $rolfTagData['code']); @@ -343,12 +342,12 @@ private function processMeasuresAndReferentialData(Entity\Anr $anr, Entity\RolfR /* Backward compatibility. Prior v2.10.3 measures data were not exported. */ $measureUuid = $measureData['uuid'] ?? $measureData; $measure = $this->importCacheHelper->getItemFromArrayCache('measures', $measureUuid) - ?: $this->measureTable->findByAnrAndUuid($anr, $measureUuid); + ?: $this->measureTable->findByUuidAndAnr($measureUuid, $anr); if ($measure === null && isset($measureData['referential'], $measureData['category'])) { $referentialUuid = $measuresData['referential']['uuid']; $referential = $this->importCacheHelper->getItemFromArrayCache('referentials', $referentialUuid) - ?: $this->referentialTable->findByAnrAndUuid($anr, $referentialUuid); + ?: $this->referentialTable->findByUuidAndAnr($referentialUuid, $anr); if ($referential === null) { $referential = (new Entity\Referential()) @@ -357,7 +356,7 @@ private function processMeasuresAndReferentialData(Entity\Anr $anr, Entity\RolfR ->setCreator($this->connectedUser->getEmail()) ->setLabels([$labelKey => $measureData['referential'][$labelKey]]); - $this->referentialTable->saveEntity($referential, false); + $this->referentialTable->save($referential, false); $this->importCacheHelper->addItemToArrayCache('referentials', $referential, $referentialUuid); } @@ -384,7 +383,7 @@ private function processMeasuresAndReferentialData(Entity\Anr $anr, Entity\RolfR if ($measure !== null) { $measure->addRolfRisk($rolfRisk); - $this->measureTable->saveEntity($measure, false); + $this->measureTable->save($measure, false); } } } diff --git a/src/Middleware/AnrValidationMiddleware.php b/src/Middleware/AnrValidationMiddleware.php index 2d4b9813..fc56561d 100644 --- a/src/Middleware/AnrValidationMiddleware.php +++ b/src/Middleware/AnrValidationMiddleware.php @@ -54,9 +54,9 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface $anrId = $routeMatch->getMatchedRouteName() === 'monarc_api_client_anr' ? (int)$routeMatch->getParam('id') : (int)$routeMatch->getParam('anrid'); - if ($anrId === 0) { - /* Anr ID for the route "client-duplicate-anr" is passed in the json body as "anr". */ - $anrId = (int)($request->getParsedBody()['anr'] ?? 0); + if ($anrId === 0 && $routeMatch->getMatchedRouteName() === 'monarc_api_duplicate_client_anr') { + /* Anr ID for the route 'client-duplicate-anr' is passed in the json body as "anr". */ + $anrId = (int)(json_decode((string)$request->getBody(), true, 512, JSON_THROW_ON_ERROR)['anr'] ?? 0); } try { diff --git a/src/Service/AnrAmvService.php b/src/Service/AnrAmvService.php index 579e0579..f7907ae6 100755 --- a/src/Service/AnrAmvService.php +++ b/src/Service/AnrAmvService.php @@ -215,6 +215,8 @@ public function createAmvItems(Entity\Anr $anr, array $data): array ->setVulnerability($vulnerability) ->setCreator($this->connectedUser->getEmail()); + $this->updatePositions($amv, $this->amvTable); + $this->createInstanceRiskForInstances($asset, $amv); $this->amvTable->save($amv); diff --git a/src/Service/AnrCartoRiskService.php b/src/Service/AnrCartoRiskService.php index 9df710f2..8cf27aae 100755 --- a/src/Service/AnrCartoRiskService.php +++ b/src/Service/AnrCartoRiskService.php @@ -17,9 +17,9 @@ */ class AnrCartoRiskService { - private array $listScales; - private array $listOpRiskScales; - private array $headers; + private array $listScales = []; + private array $listOpRiskScales = []; + private array $headers = []; public function __construct( private ScalesCacheHelper $scalesCacheHelper, @@ -108,15 +108,13 @@ public function buildListScalesAndHeaders(Entity\Anr $anr) // Only compute the listScales and headers fields if we didn't already // TODO: If we reuse the service to build the carto for 2 different ANRs in the same run, // this will cause issues! - if ($this->listScales === null) { - $this->listScales = []; + if ($this->listScales === []) { foreach ($this->scalesCacheHelper->getCachedScales($anr) as $scale) { $this->listScales[$scale->getType()] = range($scale->getMin(), $scale->getMax()); } } - if ($this->headers === null) { - $this->headers = []; + if ($this->headers === []) { foreach ($this->listScales[CoreEntity\ScaleSuperClass::TYPE_IMPACT] as $i) { foreach ($this->listScales[CoreEntity\ScaleSuperClass::TYPE_THREAT] as $m) { foreach ($this->listScales[CoreEntity\ScaleSuperClass::TYPE_VULNERABILITY] as $v) { @@ -140,7 +138,7 @@ public function buildListScalesAndHeaders(Entity\Anr $anr) public function buildListScalesOpRisk(Entity\Anr $anr) { // Only compute the listScales and headers fields if we didn't already - if ($this->listOpRiskScales === null) { + if ($this->listOpRiskScales === []) { $likelihoodScale = current($this->operationalRiskScaleTable->findWithCommentsByAnrAndType( $anr, CoreEntity\OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD diff --git a/src/Service/AnrInstanceRiskService.php b/src/Service/AnrInstanceRiskService.php index 2efc581f..cedf6999 100644 --- a/src/Service/AnrInstanceRiskService.php +++ b/src/Service/AnrInstanceRiskService.php @@ -186,8 +186,9 @@ public function createInstanceRisks( /* In case the object is global and another instance is already presented in the ANR, the same risks have to be created (including possible specific ones). */ foreach ($siblingInstance->getInstanceRisks() as $siblingInstanceRisk) { - $newInstanceRisk = $this - ->createInstanceRisk($instance, $siblingInstanceRisk->getAmv(), $siblingInstanceRisk); + /** @var Entity\Amv $amv */ + $amv = $siblingInstanceRisk->getAmv(); + $newInstanceRisk = $this->createInstanceRisk($instance, $amv, $siblingInstanceRisk); $this->duplicateRecommendationRisks($siblingInstanceRisk, $newInstanceRisk); } diff --git a/src/Service/AnrMeasureLinkService.php b/src/Service/AnrMeasureLinkService.php index 0cb986c0..f96775c6 100644 --- a/src/Service/AnrMeasureLinkService.php +++ b/src/Service/AnrMeasureLinkService.php @@ -13,7 +13,8 @@ class AnrMeasureLinkService { - public function __construct(private Table\MeasureTable $measureTable) { + public function __construct(private Table\MeasureTable $measureTable) + { } public function getList(Entity\Anr $anr): array diff --git a/src/Service/AnrObjectCategoryService.php b/src/Service/AnrObjectCategoryService.php index a0593ee6..d02c42f4 100644 --- a/src/Service/AnrObjectCategoryService.php +++ b/src/Service/AnrObjectCategoryService.php @@ -181,7 +181,7 @@ public function delete(Entity\Anr $anr, int $id): void $this->updatePositions($childCategory, $this->objectCategoryTable, ['forcePositionUpdate' => true]); - $this->objectCategoryTable->save($childCategory, false); + $this->objectCategoryTable->save($childCategory); } $this->objectCategoryTable->remove($objectCategory); diff --git a/src/Service/AnrObjectObjectService.php b/src/Service/AnrObjectObjectService.php index cf787ed0..6892144d 100644 --- a/src/Service/AnrObjectObjectService.php +++ b/src/Service/AnrObjectObjectService.php @@ -55,21 +55,32 @@ public function create(Entity\Anr $anr, array $data, bool $saveInDb = true): Ent /* Validate if one of the parents is the current child or its children. */ $this->validateIfObjectOrItsChildrenLinkedToOneOfParents($childObject, $parentObject); + $objectObject = $this->createObjectLink($parentObject, $childObject, $data, $saveInDb); + + /* Create instances of child object if necessary. */ + if ($parentObject->hasInstances()) { + $this->createInstances($parentObject, $childObject, $data); + } + + return $objectObject; + } + + public function createObjectLink( + Entity\MonarcObject $parentObject, + Entity\MonarcObject $childObject, + array $positionData, + bool $saveInDb + ): Entity\ObjectObject { $objectObject = (new Entity\ObjectObject()) - ->setAnr($anr) + ->setAnr($parentObject->getAnr()) ->setParent($parentObject) ->setChild($childObject) ->setCreator($this->connectedUser->getEmail()); - $this->updatePositions($objectObject, $this->objectObjectTable, $data); + $this->updatePositions($objectObject, $this->objectObjectTable, $positionData); $this->objectObjectTable->save($objectObject, $saveInDb); - /* Create instances of child object if necessary. */ - if ($parentObject->hasInstances()) { - $this->createInstances($parentObject, $childObject, $data); - } - return $objectObject; } diff --git a/src/Service/AnrObjectService.php b/src/Service/AnrObjectService.php index ae9d0ea8..b8af19dc 100755 --- a/src/Service/AnrObjectService.php +++ b/src/Service/AnrObjectService.php @@ -116,6 +116,7 @@ public function create(Entity\Anr $anr, array $data, bool $saveInDb = true): Ent $this->validateAssetAndDataOnCreate($asset, $data); /** @var Entity\ObjectCategory $objectCategory */ $objectCategory = $this->objectCategoryTable->findByIdAndAnr($data['category'], $anr); + /** @var ?Entity\RolfTag $rolfTag */ $rolfTag = !empty($data['rolfTag']) && !$asset->isPrimary() ? $this->rolfTagTable->findByIdAndAnr($data['rolfTag'], $anr) : null; @@ -144,12 +145,6 @@ public function createMonarcObject( $monarcObject->setUuid($data['uuid']); } - /* - * The objects positioning inside of categories was dropped from the UI, only kept in the db and passed data. - * New objects are always placed at the end. - */ - $this->updatePositions($monarcObject, $this->monarcObjectTable, $data); - $this->monarcObjectTable->save($monarcObject, $saveInDb); return $monarcObject; @@ -225,7 +220,6 @@ public function delete(Entity\Anr $anr, string $uuid): void } /* Manage the positions shift for the objects and objects_objects tables. */ - $this->shiftPositionsForRemovingEntity($monarcObject, $this->monarcObjectTable); foreach ($monarcObject->getParentsLinks() as $linkWhereTheObjectIsChild) { $this->shiftPositionsForRemovingEntity($linkWhereTheObjectIsChild, $this->objectObjectTable); } @@ -267,7 +261,6 @@ private function getPreparedObjectData(Entity\MonarcObject $object, bool $object 'label' . $anr->getLanguage() => $object->getLabel($anr->getLanguage()), 'name' . $anr->getLanguage() => $object->getName($anr->getLanguage()), 'scope' => $object->getScope(), - 'position' => $object->getPosition(), ]; if (!$objectOnly) { @@ -466,11 +459,6 @@ private function getObjectCopy(Entity\MonarcObject $monarcObjectToCopy, Entity\A if ($monarcObjectToCopy->hasRolfTag()) { $newMonarcObject->setRolfTag($monarcObjectToCopy->getRolfTag()); } - /* - * The objects positioning inside of categories was dropped from the UI, only kept in the db and passed data. - * The position is always set at the end. - */ - $this->updatePositions($newMonarcObject, $this->monarcObjectTable); return $newMonarcObject; } diff --git a/src/Service/AnrRecommendationService.php b/src/Service/AnrRecommendationService.php index e748f759..548f2442 100755 --- a/src/Service/AnrRecommendationService.php +++ b/src/Service/AnrRecommendationService.php @@ -89,7 +89,11 @@ public function create(Entity\Anr $anr, array $data, bool $saveInDb = true): Ent $recommendation->setResponsible($data['responsible']); } if (isset($data['duedate'])) { - $recommendation->setDueDate($data['duedate']); + if (!empty($data['duedate']) && !$data['duedate'] instanceof DateTime) { + $recommendation->setDueDateFromString($data['duedate']); + } else { + $recommendation->setDueDate($data['duedate']); + } } if (isset($data['counterTreated'])) { $recommendation->setCounterTreated($data['counterTreated']); diff --git a/src/Service/AnrRolfRiskService.php b/src/Service/AnrRolfRiskService.php index 83489da5..ec2601fc 100755 --- a/src/Service/AnrRolfRiskService.php +++ b/src/Service/AnrRolfRiskService.php @@ -71,12 +71,12 @@ public function create(Entity\Anr $anr, array $data, bool $saveInDb = true): Ent } } if (!empty($data['tags'])) { - /** @var Entity\RolfTag $tag */ - foreach ($this->rolfTagTable->findByIdsAndAnr($data['tags'], $anr) as $tag) { - $rolfRisk->addTag($tag); + /** @var Entity\RolfTag $rolfTag */ + foreach ($this->rolfTagTable->findByIdsAndAnr($data['tags'], $anr) as $rolfTag) { + $rolfRisk->addTag($rolfTag); /* Create operation instance risks for the linked rolf tag. */ /** @var Entity\MonarcObject $monarcObject */ - foreach ($this->monarcObjectTable->findByAnrAndRolfTag($anr, $tag) as $monarcObject) { + foreach ($rolfTag->getObjects() as $monarcObject) { foreach ($monarcObject->getInstances() as $instance) { $this->anrInstanceRiskOpService->createInstanceRiskOpWithScales( $instance, @@ -126,7 +126,7 @@ public function update(Entity\Anr $anr, int $id, array $data): Entity\RolfRisk } else { $rolfRisk->removeTag($rolfTag); /* Set the related operational risks to specific. */ - foreach ($this->monarcObjectTable->findByAnrAndRolfTag($anr, $rolfTag) as $monarcObject) { + foreach ($rolfTag->getObjects() as $monarcObject) { $instancesRisksOp = $this->instanceRiskOpTable->findByObjectAndRolfRisk($monarcObject, $rolfRisk); foreach ($instancesRisksOp as $instanceRiskOp) { $this->instanceRiskOpTable->save($instanceRiskOp->setIsSpecific(true), false); @@ -140,7 +140,7 @@ public function update(Entity\Anr $anr, int $id, array $data): Entity\RolfRisk $rolfRisk->addTag($rolfTag); /* Create operation instance risks for the linked rolf tag. */ /** @var Entity\MonarcObject $monarcObject */ - foreach ($this->monarcObjectTable->findByAnrAndRolfTag($anr, $rolfTag) as $monarcObject) { + foreach ($rolfTag->getObjects() as $monarcObject) { foreach ($monarcObject->getInstances() as $instance) { $this->anrInstanceRiskOpService->createInstanceRiskOpWithScales( $instance, @@ -156,7 +156,7 @@ public function update(Entity\Anr $anr, int $id, array $data): Entity\RolfRisk $rolfRisk->setLabels($data)->setDescriptions($data); /* If the labels or descriptions changed the operational risks labels have to be updated as well. */ foreach ($rolfRisk->getTags() as $rolfTag) { - foreach ($this->monarcObjectTable->findByAnrAndRolfTag($anr, $rolfTag) as $monarcObject) { + foreach ($rolfTag->getObjects() as $monarcObject) { $instancesRisksOp = $this->instanceRiskOpTable->findByObjectAndRolfRisk($monarcObject, $rolfRisk); foreach ($instancesRisksOp as $instanceRiskOp) { $instanceRiskOp->setRiskCacheCode($rolfRisk->getCode()) diff --git a/src/Service/AnrService.php b/src/Service/AnrService.php index 87f8ea91..504d106d 100755 --- a/src/Service/AnrService.php +++ b/src/Service/AnrService.php @@ -1152,7 +1152,6 @@ private function duplicateObjectsAndCategories( 'uuid' => $sourceUuid, 'scope' => $sourceObject->getScope(), 'setOnlyExactPosition' => true, - 'position' => $sourceObject->getPosition(), ], $sourceObject->getLabels(), $sourceObject->getNames()), false ); diff --git a/src/Service/SoaCategoryService.php b/src/Service/SoaCategoryService.php index 44d24adf..add8c671 100755 --- a/src/Service/SoaCategoryService.php +++ b/src/Service/SoaCategoryService.php @@ -41,7 +41,9 @@ public function getSoaCategoryData(Entity\Anr $anr, int $id): array public function create(Entity\Anr $anr, array $data, bool $saveInDb = true): Entity\SoaCategory { /** @var Entity\Referential $referential */ - $referential = $this->referentialTable->findByUuidAndAnr($data['referential'], $anr); + $referential = $data['referential'] instanceof Entity\Referential + ? $data['referential'] + : $this->referentialTable->findByUuidAndAnr($data['referential'], $anr); /** @var Entity\SoaCategory $soaCategory */ $soaCategory = (new Entity\SoaCategory())->setAnr($anr)->setLabels($data)->setReferential($referential); @@ -74,6 +76,7 @@ private function prepareSoaCategoryDataResult(Entity\SoaCategory $soaCategory): return array_merge(['id' => $soaCategory->getId()], $soaCategory->getLabels()); } + // TODO: use the ReferentialImportProcessor::processSoaCategoryData() instead. public function getOrCreateSoaCategory( ImportCacheHelper $importCacheHelper, Entity\Anr $anr, @@ -83,7 +86,7 @@ public function getOrCreateSoaCategory( $languageIndex = $anr->getLanguage(); $labelKey = 'label' . $languageIndex; - $importCacheHelper->prepareSoaCategoriesCacheData($anr); + $this->prepareSoaCategoriesCacheData($importCacheHelper, $anr); $cacheKey = $referential->getUuid() . '_' . $labelValue; $soaCategory = $importCacheHelper->getItemFromArrayCache('soa_categories_by_ref_and_label', $cacheKey); @@ -102,4 +105,18 @@ public function getOrCreateSoaCategory( return $soaCategory; } + + public function prepareSoaCategoriesCacheData(ImportCacheHelper $importCacheHelper, Entity\Anr $anr): void + { + if (!isset($this->arrayCache['soa_categories_by_ref_and_label'])) { + /** @var Entity\SoaCategory $soaCategory */ + foreach ($this->soaCategoryTable->findByAnr($anr) as $soaCategory) { + $importCacheHelper->addItemToArrayCache( + 'soa_categories_by_ref_and_label', + $soaCategory, + $soaCategory->getReferential()->getUuid() . '_' . $soaCategory->getLabel($anr->getLanguage()) + ); + } + } + } } diff --git a/src/Table/AmvTable.php b/src/Table/AmvTable.php index 57dda4d4..c5a40cbc 100755 --- a/src/Table/AmvTable.php +++ b/src/Table/AmvTable.php @@ -8,8 +8,6 @@ namespace Monarc\FrontOffice\Table; use Doctrine\ORM\EntityManager; -use Monarc\Core\Entity\AmvSuperClass; -use Monarc\Core\Entity\AnrSuperClass; use Monarc\Core\Table\AbstractTable; use Monarc\Core\Table\Interfaces\PositionUpdatableTableInterface; use Monarc\Core\Table\Traits\PositionIncrementTableTrait; diff --git a/src/Table/AssetTable.php b/src/Table/AssetTable.php index 0a540275..a20b8213 100755 --- a/src/Table/AssetTable.php +++ b/src/Table/AssetTable.php @@ -22,14 +22,4 @@ public function __construct(EntityManager $entityManager, string $entityName = A { parent::__construct($entityManager, $entityName); } - - public function findUuidsAndCodesByAnr(Anr $anr): array - { - return $this->getRepository()->createQueryBuilder('a') - ->select('a.uuid', 'a.code') - ->where('a.anr = :anr') - ->setParameter('anr', $anr) - ->getQuery() - ->getArrayResult(); - } } diff --git a/src/Table/InstanceRiskOpTable.php b/src/Table/InstanceRiskOpTable.php index 0e4cc4bd..3cf89839 100755 --- a/src/Table/InstanceRiskOpTable.php +++ b/src/Table/InstanceRiskOpTable.php @@ -9,11 +9,12 @@ use Doctrine\ORM\EntityManager; use Doctrine\ORM\Query\Expr\Join; +use Monarc\Core\Entity\AssetSuperClass; +use Monarc\Core\Entity\InstanceRiskOpSuperClass; use Monarc\Core\Entity\ObjectSuperClass; use Monarc\Core\Entity\RolfRiskSuperClass; use Monarc\Core\Table\InstanceRiskOpTable as CoreInstanceRiskOpTable; use Monarc\FrontOffice\Entity\Anr; -use Monarc\FrontOffice\Entity\Asset; use Monarc\FrontOffice\Entity\Instance; use Monarc\FrontOffice\Entity\InstanceRiskOp; use Monarc\FrontOffice\Entity\RolfRisk; @@ -58,7 +59,7 @@ public function findByAnrAndRolfRisk(Anr $anr, RolfRisk $rolfRisk): array /** * @return InstanceRiskOp[] */ - public function findByAnrAndInstance(Anr $anr, Instance $instance) + public function findByAnrAndInstance(Anr $anr, Instance $instance): array { return $this->getRepository() ->createQueryBuilder('oprisk') @@ -109,7 +110,7 @@ public function findByAnrInstancesAndFilterParams(Anr $anr, array $instancesIds, $language = $anr->getLanguage(); $queryBuilder = $this->getRepository()->createQueryBuilder('iro') ->innerJoin('iro.instance', 'i') - ->innerJoin('i.asset', 'a', Join::WITH, 'a.type = ' . Asset::TYPE_PRIMARY) + ->innerJoin('i.asset', 'a', Join::WITH, 'a.type = ' . AssetSuperClass::TYPE_PRIMARY) ->where('iro.anr = :anr') ->setParameter('anr', $anr); @@ -127,9 +128,9 @@ public function findByAnrInstancesAndFilterParams(Anr $anr, array $instancesIds, } if (isset($filterParams['kindOfMeasure'])) { - if ((int)$filterParams['kindOfMeasure'] === InstanceRiskOp::KIND_NOT_TREATED) { + if ((int)$filterParams['kindOfMeasure'] === InstanceRiskOpSuperClass::KIND_NOT_TREATED) { $queryBuilder->andWhere( - 'iro.kindOfMeasure IS NULL OR iro.kindOfMeasure = ' . InstanceRiskOp::KIND_NOT_TREATED + 'iro.kindOfMeasure IS NULL OR iro.kindOfMeasure = ' . InstanceRiskOpSuperClass::KIND_NOT_TREATED ); } else { $queryBuilder->andWhere('iro.kindOfMeasure = :kindOfMeasure') diff --git a/src/Table/InstanceRiskTable.php b/src/Table/InstanceRiskTable.php index 40f8b7fd..caa8a3ae 100755 --- a/src/Table/InstanceRiskTable.php +++ b/src/Table/InstanceRiskTable.php @@ -126,7 +126,7 @@ public function findByInstanceAssetThreatUuidAndVulnerabilityUuid( /** * @return InstanceRisk[] */ - public function findByAmv(Amv $amv) + public function findByAmv(Amv $amv): array { return $this->getRepository() ->createQueryBuilder('ir') @@ -142,7 +142,7 @@ public function findByAmv(Amv $amv) /** * @return InstanceRisk[] */ - public function findByInstanceAndAmv(Instance $instance, Amv $amv) + public function findByInstanceAndAmv(Instance $instance, Amv $amv): array { return $this->getRepository() ->createQueryBuilder('ir') @@ -273,7 +273,7 @@ public function existsInAnrWithInstanceThreatAndVulnerability( Instance $instance, Threat $threat, Vulnerability $vulnerability - ) { + ): bool { return (bool)$this->getRepository()->createQueryBuilder('ir') ->innerJoin('ir.threat', 't') ->innerJoin('ir.vulnerability', 'v') diff --git a/src/Table/MonarcObjectTable.php b/src/Table/MonarcObjectTable.php index c1ac2f41..2e51be20 100755 --- a/src/Table/MonarcObjectTable.php +++ b/src/Table/MonarcObjectTable.php @@ -68,20 +68,4 @@ public function findOneByAnrAndName(Anr $anr, string $nameKey, string $nameValue ->getQuery() ->getOneOrNullResult(); } - - /** - * TODO: it's called from Core\RolfRiskService, remove when the $rolfTag->getObjects() relation is added. - * @return ObjectSuperClass[] - */ - public function findByAnrAndRolfTag(AnrSuperClass $anr, RolfTagSuperClass $rolfTag): array - { - return $this->getRepository() - ->createQueryBuilder('o') - ->where('o.anr = :anr') - ->setParameter('anr', $anr) - ->andWhere('o.rolfTag = :rolfTag') - ->setParameter('rolfTag', $rolfTag) - ->getQuery() - ->getResult(); - } } diff --git a/src/Table/RecommendationRiskTable.php b/src/Table/RecommendationRiskTable.php index 496c0a24..22e88ad8 100755 --- a/src/Table/RecommendationRiskTable.php +++ b/src/Table/RecommendationRiskTable.php @@ -73,7 +73,7 @@ public function findByInstanceRiskAndRecommendation( /** * @return RecommendationRisk[] */ - public function findAllLinkedByRecommendationGlobalObjectAndAmv(RecommendationRisk $recommendationRisk) + public function findAllLinkedByRecommendationGlobalObjectAndAmv(RecommendationRisk $recommendationRisk): array { return $this->getRepository()->createQueryBuilder('rr') ->innerJoin('rr.recommendation', 'r') diff --git a/src/Table/RecommendationTable.php b/src/Table/RecommendationTable.php index 0af63e53..a52235e3 100755 --- a/src/Table/RecommendationTable.php +++ b/src/Table/RecommendationTable.php @@ -31,7 +31,7 @@ public function __construct(EntityManager $entityManager, string $entityName = R /** * @return Recommendation[] */ - public function findByAnrWithEmptyPosition(Anr $anr) + public function findByAnrWithEmptyPosition(Anr $anr): array { return $this->getRepository() ->createQueryBuilder('r') @@ -49,7 +49,7 @@ public function findLinkedWithRisksByAnrWithSpecifiedImportanceAndPositionAndExc AnrSuperClass $anr, array $excludeRecommendations = [], array $order = [] - ) { + ): array { $queryBuilder = $this->getRepository() ->createQueryBuilder('r') ->innerJoin('r.recommendationRisks', 'rr') @@ -100,7 +100,7 @@ public function findByAnrCodeAndRecommendationSet( * * @return Recommendation[] */ - public function findUnlinkedWithNotEmptyPositionByAnr(AnrSuperClass $anr) + public function findUnlinkedWithNotEmptyPositionByAnr(AnrSuperClass $anr): array { $queryBuilderLinked = $this->getRepository() ->createQueryBuilder('rec') diff --git a/src/Table/ReferentialTable.php b/src/Table/ReferentialTable.php index e23d4c44..8695200f 100644 --- a/src/Table/ReferentialTable.php +++ b/src/Table/ReferentialTable.php @@ -18,15 +18,4 @@ public function __construct(EntityManager $entityManager, string $entityName = R { parent::__construct($entityManager, $entityName); } - - public function findReferentialsUuidsWithMeasuresUuidsAndCodesByAnr(Anr $anr): array - { - return $this->getRepository()->createQueryBuilder('r') - ->innerJoin('r.measures', 'm') - ->select('r.uuid, m.uuid as measure_uuid, m.code as measure_code') - ->where('r.anr = :anr') - ->setParameter('anr', $anr) - ->getQuery() - ->getArrayResult(); - } } diff --git a/src/Table/ThemeTable.php b/src/Table/ThemeTable.php index e82a5aee..87003b3b 100755 --- a/src/Table/ThemeTable.php +++ b/src/Table/ThemeTable.php @@ -18,16 +18,4 @@ public function __construct(EntityManager $entityManager, string $entityName = T { parent::__construct($entityManager, $entityName); } - - public function findByAnrAndLabel(Anr $anr, string $labelKey, string $labelValue): ?Theme - { - return $this->getRepository()->createQueryBuilder('t') - ->where('t.anr = :anr') - ->andWhere('t.' . $labelKey . ' = :' . $labelKey) - ->setParameter('anr', $anr) - ->setParameter($labelKey, $labelValue) - ->setMaxResults(1) - ->getQuery() - ->getOneOrNullResult(); - } } diff --git a/src/Table/ThreatTable.php b/src/Table/ThreatTable.php index a22e990c..a25d7885 100755 --- a/src/Table/ThreatTable.php +++ b/src/Table/ThreatTable.php @@ -34,14 +34,4 @@ public function isEvaluationStarted(Anr $anr): bool ->getQuery() ->getOneOrNullResult(AbstractQuery::HYDRATE_SIMPLEOBJECT) !== null; } - - public function findUuidsAndCodesByAnr(Anr $anr): array - { - return $this->getRepository()->createQueryBuilder('t') - ->select('t.uuid', 't.code') - ->where('t.anr = :anr') - ->setParameter('anr', $anr) - ->getQuery() - ->getArrayResult(); - } } diff --git a/src/Table/VulnerabilityTable.php b/src/Table/VulnerabilityTable.php index bdb691f3..e22dcdee 100755 --- a/src/Table/VulnerabilityTable.php +++ b/src/Table/VulnerabilityTable.php @@ -22,14 +22,4 @@ public function __construct(EntityManager $entityManager, string $entityName = V { parent::__construct($entityManager, $entityName); } - - public function findUuidsAndCodesByAnr(Anr $anr): array - { - return $this->getRepository()->createQueryBuilder('v') - ->select('v.uuid', 'v.code') - ->where('v.anr = :anr') - ->setParameter('anr', $anr) - ->getQuery() - ->getArrayResult(); - } }