diff --git a/config/module.config.php b/config/module.config.php index 40d38899..af6ec17c 100755 --- a/config/module.config.php +++ b/config/module.config.php @@ -1104,7 +1104,7 @@ 'instance_metadata' => [ 'type' => 'segment', 'options' => [ - 'route' => 'instances/:instanceid/instances-metadata[/:id]', + 'route' => 'instances/:instanceid/metadata[/:id]', 'constraints' => [ 'instanceid' => '[0-9]+', 'id' => '[0-9]+', @@ -1404,23 +1404,23 @@ Controller\ApiAnrInstancesRisksController::class => AutowireFactory::class, Controller\ApiAnrInstancesRisksOpController::class => AutowireFactory::class, Controller\ApiSnapshotController::class => AutowireFactory::class, - Import\Controller\ApiAnrInstancesImportController::class => AutowireFactory::class, - Export\Controller\ApiAnrExportController::class => AutowireFactory::class, - Export\Controller\ApiAnrInstancesExportController::class => AutowireFactory::class, Controller\ApiAnrObjectsCategoriesController::class => AutowireFactory::class, - Export\Controller\ApiAnrObjectsExportController::class => AutowireFactory::class, - Import\Controller\ApiAnrObjectsImportController::class => AutowireFactory::class, Controller\ApiAnrDeliverableController::class => AutowireFactory::class, Controller\ApiAnrInstancesConsequencesController::class => AutowireFactory::class, Controller\ApiModelVerifyLanguageController::class => AutowireFactory::class, - Stats\Controller\StatsController::class => AutowireFactory::class, - Stats\Controller\StatsAnrsSettingsController::class => AutowireFactory::class, - Stats\Controller\StatsGeneralSettingsController::class => AutowireFactory::class, Controller\ApiOperationalRisksScalesController::class => AutowireFactory::class, Controller\ApiOperationalRisksScalesCommentsController::class => AutowireFactory::class, Controller\ApiAnrInstancesMetadataFieldsController::class => AutowireFactory::class, Controller\ApiInstanceMetadataController::class => AutowireFactory::class, Controller\ApiSoaScaleCommentController::class => AutowireFactory::class, + Export\Controller\ApiAnrExportController::class => AutowireFactory::class, + Export\Controller\ApiAnrInstancesExportController::class => AutowireFactory::class, + Export\Controller\ApiAnrObjectsExportController::class => AutowireFactory::class, + Import\Controller\ApiAnrInstancesImportController::class => AutowireFactory::class, + Import\Controller\ApiAnrObjectsImportController::class => AutowireFactory::class, + Stats\Controller\StatsController::class => AutowireFactory::class, + Stats\Controller\StatsAnrsSettingsController::class => AutowireFactory::class, + Stats\Controller\StatsGeneralSettingsController::class => AutowireFactory::class, ], ], @@ -1459,7 +1459,6 @@ DeprecatedTable\RecordProcessorTable::class => AutowireFactory::class, DeprecatedTable\RecordRecipientTable::class => AutowireFactory::class, DeprecatedTable\RecordTable::class => AutowireFactory::class, - DeprecatedTable\SoaTable::class => AutowireFactory::class, DeprecatedTable\QuestionTable::class => AutowireFactory::class, DeprecatedTable\QuestionChoiceTable::class => AutowireFactory::class, Table\AnrTable::class => ClientEntityManagerFactory::class, @@ -1493,6 +1492,7 @@ Table\RolfTagTable::class => ClientEntityManagerFactory::class, Table\ReferentialTable::class => ClientEntityManagerFactory::class, Table\SoaCategoryTable::class => ClientEntityManagerFactory::class, + Table\SoaTable::class => ClientEntityManagerFactory::class, Table\SnapshotTable::class => ClientEntityManagerFactory::class, Table\SoaScaleCommentTable::class => ClientEntityManagerFactory::class, Table\ThemeTable::class => ClientEntityManagerFactory::class, @@ -1513,7 +1513,6 @@ Entity\RecordProcessor::class => ModelFactory\RecordProcessorServiceModelEntity::class, Entity\RecordRecipient::class => ModelFactory\RecordRecipientServiceModelEntity::class, Entity\Record::class => ModelFactory\RecordServiceModelEntity::class, - Entity\Soa::class => ModelFactory\SoaServiceModelEntity::class, Entity\Question::class => ModelFactory\QuestionServiceModelEntity::class, Entity\QuestionChoice::class => ModelFactory\QuestionChoiceServiceModelEntity::class, @@ -1527,7 +1526,6 @@ Service\AnrRecordProcessorService::class => Service\AnrRecordProcessorServiceFactory::class, Service\AnrRecordRecipientService::class => Service\AnrRecordRecipientServiceFactory::class, Service\AnrRecordService::class => Service\AnrRecordServiceFactory::class, - Service\SoaService::class => Service\SoaServiceFactory::class, Service\AnrQuestionService::class => Service\AnrQuestionServiceFactory::class, Service\AnrQuestionChoiceService::class => Service\AnrQuestionChoiceServiceFactory::class, Service\AnrMeasureService::class => AutowireFactory::class, @@ -1561,14 +1559,15 @@ Service\AnrInstanceRiskOpService::class => AutowireFactory::class, Service\AnrInstanceRiskService::class => AutowireFactory::class, Service\AnrInstanceService::class => AutowireFactory::class, - Stats\Service\StatsAnrService::class => ReflectionBasedAbstractFactory::class, - Stats\Service\StatsSettingsService::class => AutowireFactory::class, Service\OperationalRiskScaleService::class => AutowireFactory::class, Service\InstanceRiskOwnerService::class => AutowireFactory::class, Service\OperationalRiskScaleCommentService::class => AutowireFactory::class, Service\AnrInstanceMetadataFieldService::class => AutowireFactory::class, Service\InstanceMetadataService::class => AutowireFactory::class, + Service\SoaService::class => AutowireFactory::class, Service\SoaScaleCommentService::class => AutowireFactory::class, + Stats\Service\StatsAnrService::class => ReflectionBasedAbstractFactory::class, + Stats\Service\StatsSettingsService::class => AutowireFactory::class, CronTask\Service\CronTaskService::class => AutowireFactory::class, /* Export services. */ Export\Service\AnrExportService::class => AutowireFactory::class, diff --git a/migrations/db/20230901112005_fix_positions_cleanup_db.php b/migrations/db/20230901112005_fix_positions_cleanup_db.php index c518a1c8..74d16580 100644 --- a/migrations/db/20230901112005_fix_positions_cleanup_db.php +++ b/migrations/db/20230901112005_fix_positions_cleanup_db.php @@ -143,7 +143,11 @@ public function change() /* Clean up unused columns. */ $this->table('clients')->removeColumn('model_id')->update(); - $this->table('instances')->removeColumn('disponibility')->update(); + $this->table('instances') + ->removeColumn('disponibility') + ->removeColumn('asset_type') + ->removeColumn('exportable') + ->update(); $this->table('objects') ->removeColumn('disponibility') ->removeColumn('token_import') @@ -222,7 +226,7 @@ public function change() ->dropForeignKey(['measure_uuid', 'anr_id']) ->update(); $this->table('soa') - ->addColumn('measure_id', 'integer', ['signed' => false, 'after' => MysqlAdapter::FIRST]) + ->addColumn('measure_id', 'integer', ['signed' => false, 'after' => 'id']) ->update(); $this->execute('UPDATE soa s INNER JOIN measures m ' . 'ON s.measure_uuid = m.`uuid` AND s.anr_id = m.anr_id SET s.measure_id = m.id;'); diff --git a/src/Controller/ApiAnrInstancesMetadataFieldsController.php b/src/Controller/ApiAnrInstancesMetadataFieldsController.php index a26d7a43..36fb6e93 100644 --- a/src/Controller/ApiAnrInstancesMetadataFieldsController.php +++ b/src/Controller/ApiAnrInstancesMetadataFieldsController.php @@ -23,26 +23,6 @@ public function __construct(AnrInstanceMetadataFieldService $anrInstanceMetadata $this->anrInstanceMetadataFieldService = $anrInstanceMetadataFieldService; } - public function getList() - { - /** @var Anr $anr */ - $anr = $this->getRequest()->getAttribute('anr'); - - return $this->getPreparedJsonResponse([ - 'data' => $this->anrInstanceMetadataFieldService->getList($anr), - ]); - } - - public function get($id) - { - /** @var Anr $anr */ - $anr = $this->getRequest()->getAttribute('anr'); - - return $this->getPreparedJsonResponse([ - 'data' => $this->anrInstanceMetadataFieldService->getAnrInstanceMetadataFieldData($anr, (int)$id), - ]); - } - /** * @param array $data */ diff --git a/src/Controller/ApiAnrRecordsController.php b/src/Controller/ApiAnrRecordsController.php index e827378d..079541c6 100644 --- a/src/Controller/ApiAnrRecordsController.php +++ b/src/Controller/ApiAnrRecordsController.php @@ -69,10 +69,8 @@ public function getList() public function get($id) { - $anrId = (int)$this->params()->fromRoute('anrid'); $entity = $this->getService()->getEntity(['id' => $id]); - $this->formatDependencies($entity, $this->dependencies); return $this->getPreparedJsonResponse($entity); @@ -101,11 +99,6 @@ public function formatDependencies(&$entity, $dependencies, $entityDependency = } else { $entity[$dependency] = $entity[$dependency]->getJsonArray(); } - unset( - $entity[$dependency][$value]['__initializer__'], - $entity[$dependency][$value]['__cloner__'], - $entity[$dependency][$value]['__isInitialized__'] - ); } elseif ($entity[$dependency] instanceof PersistentCollection) { $entity[$dependency]->initialize(); if ($entity[$dependency]->count()) { diff --git a/src/Controller/ApiSoaController.php b/src/Controller/ApiSoaController.php index 53fc6731..efb663b7 100755 --- a/src/Controller/ApiSoaController.php +++ b/src/Controller/ApiSoaController.php @@ -1,7 +1,7 @@ params()->fromQuery('page', 1); - $limit = (int)$this->params()->fromQuery('limit', 0); - $order = $this->params()->fromQuery('order'); - $filter = $this->params()->fromQuery('filter'); - $category = (int)$this->params()->fromQuery('category', 0); - $referential = $this->params()->fromQuery('referential'); - - /** @var Anr $anr */ - $anr = $this->getRequest()->getAttribute('anr'); - - $filterAnd = ['anr' => $anr->getId()]; - - if ($referential) { - if ($category !== 0) { - $filterMeasures['category'] = [ - 'op' => 'IN', - 'value' => (array)$category, - ]; - } elseif ($category === -1) { - $filterMeasures['category'] = null; - } - - $filterMeasures['r.anr'] = $anr->getId(); - $filterMeasures['r.uuid'] = $referential; - - $measuresFiltered = $this->anrMeasureService->getList(1, 0, null, null, $filterMeasures); - $measuresFilteredId = []; - foreach ($measuresFiltered as $key) { - $measuresFilteredId[] = $key['uuid']; - } - $filterAnd['m.uuid'] = [ - 'op' => 'IN', - 'value' => $measuresFilteredId, - ]; - $filterAnd['m.anr'] = $anr->getId(); - } - - if ($order === 'measure') { - $order = 'm.code'; - } elseif ($order === '-measure') { - $order = '-m.code'; - } - $entities = $this->soaService->getList($page, $limit, $order, $filter, $filterAnd); - foreach ($entities as $key => $entity) { - $amvs = []; - $rolfRisks = []; - - /** @var SoaScaleComment $soaScaleComment */ - $soaScaleComment = $entity['soaScaleComment']; - - /** @var Measure $measure */ - $measure = $entity['measure']; - foreach ($measure->getAmvs() as $amv) { - $amvs[] = $amv->getUuid(); - } - foreach ($measure->getRolfRisks() as $rolfRisk) { - $rolfRisks[] = $rolfRisk->getId(); - } - $entity['measure']->rolfRisks = []; - if (!empty($rolfRisks)) { - $entity['measure']->rolfRisks = $this->anrInstanceRiskOpService->getOperationalRisks( - $measure->getAnr(), - null, - [ - 'rolfRisks' => $rolfRisks, - 'limit' => -1, - 'order' => 'cacheNetRisk', - 'order_direction' => 'desc', - ] - ); - } - $entity['measure']->amvs = []; - if (!empty($amvs)) { - $entity['measure']->amvs = $this->anrInstanceRiskService->getInstanceRisks($measure->getAnr(), null, [ - 'amvs' => $amvs, - 'limit' => -1, - 'order' => 'maxRisk', - 'order_direction' => 'desc', - ]); - } - $entities[$key]['anr'] = [ - 'id' => $measure->getAnr()->getId(), - 'label' => $measure->getAnr()->getLabel(), - ]; - $entities[$key]['measure'] = $measure->getJsonArray(); - $entities[$key]['measure']['category'] = $measure->getCategory()->getJsonArray(); - $entities[$key]['measure']['referential'] = $measure->getReferential()->getJsonArray(); - $entities[$key]['measure']['linkedMeasures'] = []; - foreach ($measure->getLinkedMeasures() as $linkedMeasure) { - $entities[$key]['measure']['linkedMeasures'][] = $linkedMeasure->getUuid(); - } - if ($soaScaleComment !== null) { - $entities[$key]['soaScaleComment'] = $this->soaScaleCommentService - ->getPreparedSoaScaleCommentData($soaScaleComment); - } else { - $entities[$key]['soaScaleComment'] = null; - } - } - return $this->getPreparedJsonResponse([ - 'count' => $this->soaService->getFilteredCount($filter, $filterAnd), - 'soaMeasures' => $entities, + 'soaMeasures' => $this->soaService->getList($this->getFormattedInputParams($this->getSoasInputFormatter)), + 'count' => $this->soaService->getCount($this->getFormattedInputParams($this->getSoasInputFormatter)) ]); } - public function get($id) + public function patch($id, $data) { - $entity = $this->soaService->getEntity((int)$id); - /** @var Measure $measure */ - $measure = $entity['measure']; - /** @var SoaScaleComment $measure */ - $soaScaleComment = $entity['soaScaleComment']; - /** @var Anr $anr */ $anr = $this->getRequest()->getAttribute('anr'); - - $entity['anr'] = [ - 'id' => $anr->getId(), - 'label' => $anr->getLabel(), - ]; - $entity['measure'] = $measure->getJsonArray(); - $entity['measure']['category'] = $measure->getCategory()->getJsonArray(); - $entity['measure']['referential'] = $measure->getReferential()->getJsonArray(); - if ($soaScaleComment !== null) { - $entity['soaScaleComment'] = $this->soaScaleCommentService - ->getPreparedSoaScaleCommentData($soaScaleComment); - } else { - $entity['soaScaleComment'] = null; - } - - return $this->getPreparedJsonResponse($entity); - } - - public function patch($id, $data) - { - $this->soaService->patchSoa($id, $data); + $this->soaService->patchSoa($anr, (int)$id, $data); return $this->getSuccessfulJsonResponse(); } @@ -178,18 +43,6 @@ public function patchList($data) /** @var Anr $anr */ $anr = $this->getRequest()->getAttribute('anr'); - $createdObjects = []; - foreach ($data as $newData) { - $newData['anr'] = $anr->getId(); - $newData['measure'] = ['anr' => $anr->getId(), 'uuid' => $newData['measure']['uuid']]; - $id = $newData['id']; - if (\is_array($newData['soaScaleComment'])) { - $newData['soaScaleComment'] = $newData['soaScaleComment']['id']; - } - $this->soaService->patchSoa($id, $newData); - $createdObjects[] = $id; - } - - return $this->getSuccessfulJsonResponse(['id' => $createdObjects]); + return $this->getSuccessfulJsonResponse(['id' => $this->soaService->patchList($anr, $data)]); } } diff --git a/src/Entity/Instance.php b/src/Entity/Instance.php index d6a4c439..98bd8123 100755 --- a/src/Entity/Instance.php +++ b/src/Entity/Instance.php @@ -84,6 +84,17 @@ public function addInstanceMetadata(InstanceMetadata $instanceMetadata): self return $this; } + public function getInstanceMetadataByMetadataFieldLink(AnrInstanceMetadataField $metadataField): ?InstanceMetadata + { + foreach ($this->instanceMetadata as $instanceMetadata) { + if ($instanceMetadata->getAnrInstanceMetadataField()->getLabel() === $metadataField->getLabel()) { + return $instanceMetadata->getAnrInstanceMetadataField(); + } + } + + return null; + } + public function getHierarchyString(): string { return implode(' > ', array_column($this->getHierarchyArray(), 'name' . $this->anr->getLanguage())); diff --git a/src/Entity/Scale.php b/src/Entity/Scale.php index 7eed059e..b0cc3d35 100755 --- a/src/Entity/Scale.php +++ b/src/Entity/Scale.php @@ -1,7 +1,7 @@ type]['min'], $data[$this->type]['max']) + && ($data[$this->type]['min'] !== $this->min || $data[$this->type]['max'] !== $this->max); + } } diff --git a/src/Entity/Soa.php b/src/Entity/Soa.php index 90de08ed..6b69eef1 100755 --- a/src/Entity/Soa.php +++ b/src/Entity/Soa.php @@ -1,14 +1,13 @@ - 'm', - 'rel' => 'measure', - ], - ]; - $filterLeft = [ - ]; - $filtersCol = [ - 'm.label1', - 'm.label2', - 'm.label3', - 'm.label4', - 'm.code', - 'remarks', - 'actions', - 'evidences' - ]; - - return [$filterJoin, $filterLeft, $filtersCol]; - } - public function getId() { return $this->id; @@ -179,9 +154,6 @@ public function setAnr(Anr $anr): self return $this; } - /** - * @return string - */ public function getRemarks() { return $this->remarks; @@ -233,126 +205,84 @@ public function setActions($actions): self return $this; } - /** - * @return integer - */ public function getCompliance() { return $this->compliance; } - /** - * @param integer $compliance - */ - public function setCompliance($compliance): self + public function setCompliance(int $compliance): self { $this->compliance = $compliance; return $this; } - /** - * @return int - */ - public function getEx() + public function getEx(): int { return $this->EX; } - /** - * @param int $EX - */ - public function setEx($EX): self + public function setEx(int $EX): self { $this->EX = $EX; return $this; } - /** - * @return int - */ - public function getLr() + public function getLr(): int { return $this->LR; } - /** - * @param int $LR - */ - public function setLr($LR): self + public function setLr(int $LR): self { $this->LR = $LR; return $this; } - /** - * @return int - */ - public function getCo() + public function getCo(): int { return $this->CO; } - /** - * @param int $CO - */ - public function setCo($CO): self + public function setCo(int $CO): self { $this->CO = $CO; return $this; } - /** - * @return int - */ - public function getBr() + public function getBr(): int { return $this->BR; } - /** - * @param int $BR - */ - public function setBr($BR): self + public function setBr(int $BR): self { $this->BR = $BR; return $this; } - /** - * @return int - */ - public function getBp() + public function getBp(): int { return $this->BP; } - /** - * @param int $BP - */ - public function setBp($BP): self + public function setBp(int $BP): self { $this->BP = $BP; return $this; } - /** - * @return int - */ - public function getRra() + public function getRra(): int { return $this->RRA; } - /** - * @param int $RRA - */ - public function setRra($RRA): self + public function setRra(int $RRA): self { $this->RRA = $RRA; diff --git a/src/Export/Service/AnrExportService.php b/src/Export/Service/AnrExportService.php index aeee2c57..d675f54a 100644 --- a/src/Export/Service/AnrExportService.php +++ b/src/Export/Service/AnrExportService.php @@ -46,7 +46,7 @@ public function __construct( private Table\OperationalRiskScaleTable $operationalRiskScaleTable, private Table\SoaScaleCommentTable $soaScaleCommentTable, private Table\DeliveryTable $deliveryTable, - private DeprecatedTable\SoaTable $soaTable, + private Table\SoaTable $soaTable, private DeprecatedTable\QuestionTable $questionTable, private DeprecatedTable\interviewTable $interviewTable, private DeprecatedTable\RecordTable $recordTable, @@ -112,7 +112,7 @@ private function prepareExportData(Entity\Anr $anr, array $exportParams): array 'operationalRiskScales' => $withEval ? $this->prepareOperationalRiskScalesData($anr) : [], 'soaScaleComments' => $withSoas ? $this->prepareSoaScaleCommentsData($anr) : [], 'soas' => $withSoas ? $this->prepareSoasData($anr) : [], - 'method' => $withMethodSteps ? $this->prepareMethodData($anr) : [], + 'method' => $withMethodSteps ? $this->prepareMethodData($anr, !$withKnowledgeBase) : [], 'thresholds' => $withEval ? $this->prepareAnrTrashholdsData($anr) : [], 'interviews' => $withInterviews ? $this->prepareInterviewsData($anr) : [], 'gdprRecords' => $withRecords ? $this->prepareGdprRecordsData($anr) : [], @@ -124,7 +124,10 @@ private function prepareAnrInstanceMetadataFieldsData(Entity\Anr $anr): array $result = []; /** @var Entity\AnrInstanceMetadataField $anrInstanceMetadata */ foreach ($this->anrInstanceMetadataFieldTable->findByAnr($anr) as $anrInstanceMetadata) { - $result[] = ['label' => $anrInstanceMetadata->getLabel()]; + $result[] = [ + 'label' => $anrInstanceMetadata->getLabel(), + 'isDeletable' => $anrInstanceMetadata->isDeletable(), + ]; } return $result; @@ -394,6 +397,7 @@ private function prepareSoaScaleCommentsData(Entity\Anr $anr): array private function prepareSoasData(Entity\Anr $anr): array { $result = []; + /** @var Entity\Soa $soa */ foreach ($this->soaTable->findByAnr($anr) as $soa) { $result = [ 'remarks' => $soa->getRemarks(), @@ -405,7 +409,7 @@ private function prepareSoasData(Entity\Anr $anr): array 'BR' => $soa->getBr(), 'BP' => $soa->getBp(), 'RRA' => $soa->getRra(), - 'soaScaleCommentId' => $soa->getSoaScaleComment()?->getId(), + 'soaScaleCommentIndex' => $soa->getSoaScaleComment()?->getScaleIndex(), 'measureUuid' => $soa->getMeasure()->getUuid(), ]; } @@ -413,7 +417,8 @@ private function prepareSoasData(Entity\Anr $anr): array return $result; } - private function prepareMethodData(Entity\Anr $anr): array + /** The threats' data is only needed when the Knowledge Base is not exported. */ + private function prepareMethodData(Entity\Anr $anr, bool $includeThreats): array { $languageIndex = $anr->getLanguage(); $deliveriesData = []; @@ -425,7 +430,7 @@ private function prepareMethodData(Entity\Anr $anr): array Entity\Delivery::DOC_TYPE_MODEL_VALIDATION, Entity\Delivery::DOC_TYPE_FINAL_REPORT, Entity\Delivery::DOC_TYPE_IMPLEMENTATION_PLAN, - Entity\Delivery::DOC_TYPE_SOA + Entity\Delivery::DOC_TYPE_SOA, ], true)) { $deliveriesData[$docType] = [ 'id' => $delivery->getId(), @@ -446,6 +451,7 @@ private function prepareMethodData(Entity\Anr $anr): array $questionChoicesData = []; foreach ($question->getQuestionChoices() as $questionChoice) { $questionChoicesData[] = [ + 'id' => $questionChoice->getId(), 'label' => $questionChoice->getLabel($languageIndex), 'position' => $questionChoice->getPosition(), ]; @@ -484,6 +490,7 @@ private function prepareMethodData(Entity\Anr $anr): array ], 'deliveries' => $deliveriesData, 'questions' => $questionsData, + 'threats' => $includeThreats ? $this->prepareThreatsData($anr, true) : [], ]; } diff --git a/src/Export/Service/Traits/InstanceExportTrait.php b/src/Export/Service/Traits/InstanceExportTrait.php index 6502f368..fc0906f2 100644 --- a/src/Export/Service/Traits/InstanceExportTrait.php +++ b/src/Export/Service/Traits/InstanceExportTrait.php @@ -34,16 +34,14 @@ private function prepareInstanceData( return [ 'name' => $instance->getName($languageIndex), 'label' => $instance->getLabel($languageIndex), - 'assetType' => $instance->getAssetType(), - 'exportable' => $instance->getExportable(), 'level' => $instance->getLevel(), 'position' => $instance->getPosition(), 'confidentiality' => $withEval ? $instance->getConfidentiality() : -1, 'integrity' => $withEval ? $instance->getIntegrity() : -1, 'availability' => $withEval ? $instance->getAvailability() : -1, - 'confidentialityInherited' => $withEval ? (int)$instance->isConfidentialityInherited() : 1, - 'integrityInherited' => $withEval ? (int)$instance->isIntegrityInherited() : 1, - 'availabilityInherited' => $withEval ? (int)$instance->isAvailabilityInherited() : 1, + 'isConfidentialityInherited' => $withEval ? (int)$instance->isConfidentialityInherited() : 1, + 'isIntegrityInherited' => $withEval ? (int)$instance->isIntegrityInherited() : 1, + 'isAvailabilityInherited' => $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 AMVs in asset. */ 'object' => $includeCompleteObjectData @@ -84,8 +82,7 @@ private function prepareInstanceMetadataData(Entity\Instance $instance): array foreach ($instance->getInstanceMetadata() as $instanceMetadata) { $result[] = [ 'anrInstanceMetadataField' => [ - 'id' => $instanceMetadata->getAnrInstanceMetadataField()->getId(), - 'label' => $instanceMetadata->getAnrInstanceMetadataField()->getLabel(), + 'label' => $instanceMetadata->getAnrInstanceMetadataField()->getLbel(), ], 'comment' => $instanceMetadata->getComment(), ]; diff --git a/src/Import/Processor/AnrInstanceMetadataFieldImportProcessor.php b/src/Import/Processor/AnrInstanceMetadataFieldImportProcessor.php new file mode 100644 index 00000000..9d592aba --- /dev/null +++ b/src/Import/Processor/AnrInstanceMetadataFieldImportProcessor.php @@ -0,0 +1,74 @@ +prepareAnrInstanceMetadataFieldsCache($anr); + foreach ($anrInstanceMetadataFieldsData as $anrInstanceMetadataFieldData) { + $this->processAnrInstanceMetadataField($anr, $anrInstanceMetadataFieldData); + } + } + + public function processAnrInstanceMetadataField( + Entity\Anr $anr, + array $metadataFieldData + ): Entity\AnrInstanceMetadataField { + $anrInstanceMetadataField = $this->getAnrInstanceMetadataFieldsFromCache($metadataFieldData['label']); + if ($anrInstanceMetadataField !== null) { + return $anrInstanceMetadataField; + } + + $metadataField = $this->anrInstanceMetadataFieldService->createAnrInstanceMetadataField( + $anr, + $metadataFieldData['label'], + $metadataFieldData['isDeletable'] ?? true, + false + ); + $this->importCacheHelper->addItemToArrayCache( + 'anr_instance_metadata_fields', + $metadataField, + $metadataField->getLabel() + ); + + return $metadataField; + } + + public function getAnrInstanceMetadataFieldsFromCache(string $label): Entity\AnrInstanceMetadataField + { + return $this->importCacheHelper->getItemFromArrayCache('anr_instance_metadata_fields', $label); + } + + private function prepareAnrInstanceMetadataFieldsCache(Entity\Anr $anr): void + { + if (!$this->importCacheHelper->isCacheKeySet('anr_instance_metadata_fields')) { + /** @var Entity\AnrInstanceMetadataField $anrInstanceMetadataField */ + foreach ($this->anrInstanceMetadataFieldTable->findByAnr($anr) as $anrInstanceMetadataField) { + $this->importCacheHelper->addItemToArrayCache( + 'anr_instance_metadata_fields', + $anrInstanceMetadataField, + $anrInstanceMetadataField->getLabel() + ); + } + } + } +} diff --git a/src/Import/Processor/AnrMethodStepImportProcessor.php b/src/Import/Processor/AnrMethodStepImportProcessor.php new file mode 100644 index 00000000..3565395b --- /dev/null +++ b/src/Import/Processor/AnrMethodStepImportProcessor.php @@ -0,0 +1,177 @@ +connectedUser = $connectedUserService->getConnectedUser(); + } + + public function processAnrMethodStepsData(Entity\Anr $anr, array $methodStepsData): void + { + if (!empty($methodStepsData['questions'])) { + /* The only place where the data is flushed, so has to be called first. */ + $this->processQuestionsData($anr, $methodStepsData['questions']); + } + + /* Set method steps checkboxes. */ + if (!empty($methodStepsData['steps'])) { + $anr->setInitAnrContext((int)$methodStepsData['steps']['initAnrContext']) + ->setInitEvalContext((int)$methodStepsData['steps']['initEvalContext']) + ->setInitRiskContext((int)$methodStepsData['steps']['initRiskContext']) + ->setInitDefContext((int)$methodStepsData['steps']['initDefContext']) + ->setModelImpacts((int)$methodStepsData['steps']['modelImpacts']) + ->setModelSummary((int)$methodStepsData['steps']['modelSummary']) + ->setEvalRisks((int)$methodStepsData['steps']['evalRisks']) + ->setEvalPlanRisks((int)$methodStepsData['steps']['evalPlanRisks']) + ->setManageRisks((int)$methodStepsData['steps']['manageRisks']) + ->setUpdater($this->connectedUser->getEmail()); + $this->anrTable->save($anr, false); + } + + /* Set data of text-boxes. */ + if (!empty($methodStepsData['data'])) { + $anr->setContextAnaRisk($methodStepsData['data']['contextAnaRisk']) + ->setContextGestRisk($methodStepsData['data']['contextGestRisk']) + ->setSynthThreat($methodStepsData['data']['synthThreat']) + ->setSynthAct($methodStepsData['data']['synthAct']) + ->setUpdater($this->connectedUser->getEmail()); + $this->anrTable->save($anr, false); + } + + /* Recreate the generated deliveries reports. */ + if (!empty($methodStepsData['deliveries'])) { + foreach ($methodStepsData['deliveries'] as $deliveryData) { + $delivery = (new Entity\Delivery()) + ->setAnr($anr) + ->setName($deliveryData['name']) + ->setDocType($deliveryData['typedoc']) + ->setVersion($deliveryData['version']) + ->setStatus($deliveryData['status']) + ->setClassification($deliveryData['classification']) + ->setRespCustomer($deliveryData['respCustomer']) + ->setResponsibleManager($deliveryData['responsibleManager'] ?? $deliveryData['respSmile']) + ->setSummaryEvalRisk($deliveryData['summaryEvalRisk']) + ->setCreator($this->connectedUser->getEmail()); + $this->deliveryTable->save($delivery, false); + } + } + + if (!empty($methodStepsData['interviews'])) { + $this->processInterviewsData($anr, $methodStepsData['interviews']); + } + + if (!empty($methodStepsData['thresholds'])) { + $this->processThresholdsData($anr, $methodStepsData['thresholds']); + } + + /* Process the evaluation of threats. */ + if (!empty($methodStepsData['threats'])) { + $this->threatImportProcessor->processThreatsData($anr, $methodStepsData['threats']); + } + } + + public function processThresholdsData(Entity\Anr $anr, array $thresholdsData): void + { + $anr->setSeuil1((int)$thresholdsData['seuil1']) + ->setSeuil2((int)$thresholdsData['seuil2']) + ->setSeuilRolf1((int)$thresholdsData['seuilRolf1']) + ->setSeuilRolf2((int)$thresholdsData['seuilRolf2']) + ->setUpdater($this->connectedUser->getEmail()); + $this->anrTable->save($anr, false); + } + + public function processInterviewsData(Entity\Anr $anr, array $interviewsData): void + { + foreach ($interviewsData as $interviewData) { + $newInterview = (new Entity\Interview()) + ->setAnr($anr) + ->setDate($interviewData['date']) + ->setContent($interviewData['content']) + ->setService($interviewData['service']) + ->setCreator($this->connectedUser->getEmail()); + $this->interviewTable->saveEntity($newInterview, false); + } + } + + private function processQuestionsData(Entity\Anr $anr, $questionsData): void + { + foreach ($this->questionTable->findByAnr($anr) as $question) { + $this->questionTable->deleteEntity($question, false); + } + + foreach ($questionsData as $position => $questionData) { + /* In the new data structure there is only "label" field set. */ + if (isset($questionData['label'])) { + $questionData['label' . $anr->getLanguage()] = $questionData['label']; + } + $question = (new Entity\Question()) + ->setAnr($anr) + ->setLabels($questionData) + ->setMode($questionData['mode']) + ->setIsMultiChoice($questionData['isMultiChoice'] ?? (bool)$questionData['multichoice']) + ->setType($questionData['type']) + ->setResponse((string)$questionData['response']) + ->setPosition($position) + ->setCreator($this->connectedUser->getEmail()); + $this->questionTable->saveEntity($question, false); + + if ($question->isMultiChoice()) { + $choicesOldIdsToNewObjects = []; + /* Support the old structure format, prior v2.13.1 */ + $questionChoicesData = $methodStepsData['questionChoice'] ?? $questionData['questionChoices']; + foreach ($questionChoicesData as $questionChoiceData) { + if (!isset($questionChoiceData['question']) + || $questionChoiceData['question'] === $questionData['id'] + ) { + if (isset($questionChoiceData['label'])) { + $questionChoiceData['label' . $anr->getLanguage()] = $questionChoiceData['label']; + } + $questionChoice = (new Entity\QuestionChoice())->setAnr($anr)->setQuestion($question) + ->setLabels($questionChoiceData)->setPosition($questionChoiceData['position']) + ->setCreator($this->connectedUser->getEmail()); + $this->questionChoiceTable->saveEntity($questionChoice, false); + $choicesOldIdsToNewObjects[$questionChoiceData['id']] = $questionChoice; + } + } + $response = trim($question->getResponse(), '[]'); + if ($response !== '') { + /* The flush is necessary as responses are stored as array of IDs from the exported DB. + TODO: refactor the responses saving in a separate table and avoid the flush operation here. */ + $this->questionChoiceTable->getDb()->flush(); + $originQuestionChoicesIds = explode(',', $response); + $questionChoicesIds = []; + foreach ($originQuestionChoicesIds as $originQuestionChoicesId) { + if (isset($choicesOldIdsToNewObjects[$originQuestionChoicesId])) { + $questionChoicesIds[] = $choicesOldIdsToNewObjects[$originQuestionChoicesId]->getId(); + } + } + $question->setResponse('[' . implode(',', $questionChoicesIds) . ']'); + $this->questionTable->save($question, false); + } + } + } + } +} diff --git a/src/Import/Processor/InstanceImportProcessor.php b/src/Import/Processor/InstanceImportProcessor.php new file mode 100644 index 00000000..2a6b7e33 --- /dev/null +++ b/src/Import/Processor/InstanceImportProcessor.php @@ -0,0 +1,243 @@ +connectedUser = $connectedUserService->getConnectedUser(); + } + + /** + * @return int[] IDs of the created root instances. + */ + public function processInstancesData( + Entity\Anr $anr, + array $instancesData, + ?Entity\Instance $parentInstance, + string $importMode, + bool $withEval + ): array { + $this->prepareGlobalInstancesCache($anr); + $maxPositionInsideOfParentInstance = 0; + if ($parentInstance !== null) { + $maxPositionInsideOfParentInstance = $this->instanceTable->findMaxPosition( + $parentInstance->getImplicitPositionRelationsValues() + ); + } + $instancesIds = []; + foreach ($instancesData as $instanceData) { + if ($maxPositionInsideOfParentInstance > 0) { + $instanceData['position'] += ++$maxPositionInsideOfParentInstance; + } + $instancesIds[] = $this + ->processInstanceData($anr, $instanceData, $parentInstance, $importMode, $withEval) + ->getId(); + } + + return $instancesIds; + } + + /** The method is called as a starting point of the root instances import and should not be called recursively. */ + public function processInstanceData( + Entity\Anr $anr, + array $instanceData, + ?Entity\Instance $parentInstance, + string $importMode, + bool $withEval + ): Entity\Instance { + // TODO support the old format -> category is on the same level as object and object is inside of 'object'. + $objectCategory = null; + if (isset($instanceData['object']['category'])) { + $objectCategory = $this->objectCategoryImportProcessor + ->processObjectCategoryData($anr, $objectCategory, $importMode); + } + $instanceData['object'] = $this->objectImportProcessor + ->processObjectData($anr, $objectCategory, $instanceData['object'], $importMode); + $instanceData['parent'] = $parentInstance; + $instanceData['setOnlyExactPosition'] = true; + + $instance = $this->instanceService->createInstance($anr, $instanceData, $parentInstance === null, false); + /* In case if the import is into an instance the scales impacts could be adjusted ih not set directly. */ + if ($parentInstance !== null) { + $instance->refreshInheritedImpact(); + } + + $this->instanceConsequenceImportProcessor->processInstanceConsequencesData( + $instance, + $instanceData['instancesConsequences'], + $withEval + ); + + // TODO: process instanceRisks here or after the brothers update ??? + + // TODO: 1. set impacts based on the importing data, + // 2. If parent is set initially and it has impacts set (inherited or not) they have to be applied + // to the root instances and as the result to their parents. Only when !$withEval or impacts are not set. + // 3. If !$withEval than try to get global object's impact to set for the importing instances, conseq, trheats, vulns. + // But only from the DB, not created during the import (fetch from the DB or if possible check if object is now or not). + // In general: if object, linked to the instance is not new (from DB), is global and has instances with evaluations we apply to the newly created. + + /* If import is without eval and the object is global, then sibling object's 1st instance eval is applied. */ + if ($importMode === ObjectImportService::IMPORT_MODE_MERGE && $instance->getObject()->isScopeGlobal()) { + // TODO: consider a cache of the DB objects and created ones separately. + if ($withEval) { + // siblings only in the DB. + // TODO: updateInstanceEvaluationsFromSiblings + } else { + // siblings only newly created. + // TODO: applyInstanceEvaluationsToSiblings + } + } + + // TODO: ... + $this->processInstanceMetadata( + $anr, + $instance, + $instanceData['instancesMetadatas'] ?? $instanceData['instanceMetadata'] + ); + + if (!empty($instanceData['children'])) { + $this->processChildrenInstancesData($anr, $instanceData['children']); + } + + return $instance; + } + + public function processInstanceMetadata( + Entity\Anr $anr, + Entity\Instance $instance, + array $instanceMetadataData + ): void { + foreach ($instanceMetadataData as $metadataData) { + $metadataField = $this->anrInstanceMetadataFieldImportProcessor->processAnrInstanceMetadataField($anr, [ + 'label' => $instanceMetadataData['anrInstanceMetadataField']['label'] ?? $instanceMetadataData['label'], + 'isDeletable' => true, + ]); + + $instanceMetadata = $instance->getInstanceMetadataByMetadataFieldLink($metadataField); + if ($instanceMetadata === null) { + $instanceMetadata = (new Entity\InstanceMetadata()) + ->setInstance($instance) + ->setAnrInstanceMetadataField($metadataField) + ->setComment($metadataData['comment']) + ->setCreator($this->connectedUser->getEmail()); + $this->instanceMetadataTable->save($instanceMetadata, false); + + $this->applyInstanceMetadataToSiblings($instance, $instanceMetadata); + } elseif ($instanceMetadata->getComment() !== $metadataData['comment']) { + $instanceMetadata->setComment($metadataData['comment'])->setUpdater($this->connectedUser->getEmail()); + $this->instanceMetadataTable->save($instanceMetadata, false); + } + } + + $this->updateInstanceMetadataFromSiblings($instance); + } + + /** + * Applies the newly created instance metadata to the others global sibling instances. + */ + private function applyInstanceMetadataToSiblings( + Entity\Instance $instance, + Entity\InstanceMetadata $instanceMetadata + ): void { + if ($instance->getObject()->isScopeGlobal()) { + foreach ($this->getGlobalObjectInstancesFromCache($instance->getObject()->getUuid()) as $instanceSibling) { + $instanceMetadataOfSibling = $instanceSibling->getInstanceMetadataByMetadataFieldLink( + $instanceMetadata->getAnrInstanceMetadataField() + ); + if ($instanceMetadataOfSibling === null) { + $instanceMetadataOfSibling = (new Entity\InstanceMetadata()) + ->setInstance($instanceSibling) + ->setAnrInstanceMetadataField($instanceMetadata->getAnrInstanceMetadataField()) + ->setComment($instanceMetadata->getComment()) + ->setCreator($this->connectedUser->getEmail()); + $this->instanceMetadataTable->save($instanceMetadataOfSibling, false); + } + } + } + } + + /** + * Updates the instance metadata from the others global sibling instances. + */ + private function updateInstanceMetadataFromSiblings(Entity\Instance $instance): void + { + if ($instance->getObject()->isScopeGlobal()) { + $instanceSibling = current($this->getGlobalObjectInstancesFromCache($instance->getObject()->getUuid())); + if ($instanceSibling !== false) { + foreach ($instanceSibling->getInstanceMetadata() as $instanceMetadataOfSibling) { + $instanceMetadata = $instance->getInstanceMetadataByMetadataFieldLink( + $instanceMetadataOfSibling->getAnrInstanceMetadataField() + ); + if ($instanceMetadata === null) { + $instanceMetadata = (new Entity\InstanceMetadata()) + ->setInstance($instance) + ->setAnrInstanceMetadataField($instanceMetadataOfSibling->getAnrInstanceMetadataField()) + ->setComment($instanceMetadataOfSibling->getComment()) + ->setCreator($this->connectedUser->getEmail()); + $this->instanceMetadataTable->save($instanceMetadata, false); + } + } + } + } + } + + private function processChildrenInstancesData(Entity\Anr $anr, array $childrenInstanceData): void + { + // TODO + } + + /** + * @return Entity\Instance[] + */ + private function getGlobalObjectInstancesFromCache(string $objectUuid): array + { + return $this->importCacheHelper->getItemFromArrayCache('global_instances_by_object_uuids', $objectUuid) ?? []; + } + + private function prepareGlobalInstancesCache(Entity\Anr $anr): void + { + if (!$this->importCacheHelper->isCacheKeySet('global_instances_by_object_uuids')) { + foreach ($this->monarcObjectTable->findGlobalObjectsByAnr($anr) as $object) { + if ($object->hasInstances()) { + $instances = []; + foreach ($object->getInstances() as $instance) { + $instances[] = $instance; + } + $this->importCacheHelper->addItemToArrayCache( + 'global_instances_by_object_uuids', + $instances, + $object->getUuid() + ); + } + } + } + } +} diff --git a/src/Import/Processor/ObjectImportProcessor.php b/src/Import/Processor/ObjectImportProcessor.php index 83045172..64247b97 100644 --- a/src/Import/Processor/ObjectImportProcessor.php +++ b/src/Import/Processor/ObjectImportProcessor.php @@ -44,7 +44,7 @@ public function processObjectsData( public function processObjectData( Entity\Anr $anr, - Entity\ObjectCategory $objectCategory, + ?Entity\ObjectCategory $objectCategory, array $objectData, string $importMode ): Entity\MonarcObject { @@ -68,7 +68,7 @@ public function processObjectData( $objectData[$nameFiledKey], $assetData['uuid'], $objectScope, - $objectCategory->getId() + $objectCategory?->getId() ); if ($object !== null) { $this->objectObjectTable->deleteLinksByParentObject($object); @@ -105,7 +105,7 @@ public function processObjectData( $this->importCacheHelper->addItemToArrayCache( 'objects_by_name_asset_scope_category', $object, - $objectData[$nameFiledKey] . $asset->getUuid() . $object->getScope() . $objectCategory->getId() + $objectData[$nameFiledKey] . $asset->getUuid() . $object->getScope() . $objectCategory?->getId() ); $this->importCacheHelper->addItemToArrayCache( 'objects_names', diff --git a/src/Import/Processor/OperationalInstanceRiskImportProcessor.php b/src/Import/Processor/OperationalInstanceRiskImportProcessor.php new file mode 100644 index 00000000..69c2093c --- /dev/null +++ b/src/Import/Processor/OperationalInstanceRiskImportProcessor.php @@ -0,0 +1,401 @@ +connectedUser = $connectedUserService->getConnectedUser(); + } + + public function processOperationalInstanceRisks( + array $data, + Entity\Anr $anr, + Entity\Instance $instance, + Entity\MonarcObject $monarcObject, + bool $includeEval, + bool $isImportTypeAnr + ): void { + if (empty($data['risksop'])) { + return; + } + + $operationalRiskScalesData = $this->operationalRiskScaleImportProcessor + ->getCurrentOperationalRiskScalesData($anr); + $externalOperationalRiskScalesData = []; + $areScalesLevelsOfLikelihoodDifferent = false; + $areImpactScaleTypesValuesDifferent = false; + $matchedScaleTypesMap = []; + if ($includeEval && !$isImportTypeAnr) { + $externalOperationalRiskScalesData = $this->operationalRiskScaleImportProcessor + ->getExternalOperationalRiskScalesData($anr, $data); + $areScalesLevelsOfLikelihoodDifferent = $this->areLikelihoodScalesLevelsOfTypeDifferent( + $operationalRiskScalesData, + $externalOperationalRiskScalesData + ); + $areImpactScaleTypesValuesDifferent = $this->areImpactScaleTypeValuesDifferent( + $operationalRiskScalesData, + $externalOperationalRiskScalesData + ); + $matchedScaleTypesMap = $this->matchAndGetOperationalRiskScaleTypesMap( + $operationalRiskScalesData, + $externalOperationalRiskScalesData + ); + } + $oldInstanceRiskFieldsMapToScaleTypesFields = [ + ['brutR' => 'BrutValue', 'netR' => 'NetValue', 'targetedR' => 'TargetedValue'], + ['brutO' => 'BrutValue', 'netO' => 'NetValue', 'targetedO' => 'TargetedValue'], + ['brutL' => 'BrutValue', 'netL' => 'NetValue', 'targetedL' => 'TargetedValue'], + ['brutF' => 'BrutValue', 'netF' => 'NetValue', 'targetedF' => 'TargetedValue'], + ['brutP' => 'BrutValue', 'netP' => 'NetValue', 'targetedP' => 'TargetedValue'], + ]; + + // TODO: if it will be called from the new structure logic adopt to check if "withRecommendations". + if (!empty($data['recs'])) { + $this->recommendationImportProcessor->prepareRecommendationsCache($anr); + } + + foreach ($data['risksop'] as $operationalRiskData) { + $operationalInstanceRisk = (new Entity\InstanceRiskOp()) + ->setAnr($anr) + ->setInstance($instance) + ->setObject($monarcObject) + ->setRiskCacheLabels([ + 'riskCacheLabel1' => $operationalRiskData['riskCacheLabel1'], + 'riskCacheLabel2' => $operationalRiskData['riskCacheLabel2'], + 'riskCacheLabel3' => $operationalRiskData['riskCacheLabel3'], + 'riskCacheLabel4' => $operationalRiskData['riskCacheLabel4'], + ]) + ->setRiskCacheDescriptions([ + 'riskCacheDescription1' => $operationalRiskData['riskCacheDescription1'], + 'riskCacheDescription2' => $operationalRiskData['riskCacheDescription2'], + 'riskCacheDescription3' => $operationalRiskData['riskCacheDescription3'], + 'riskCacheDescription4' => $operationalRiskData['riskCacheDescription4'], + ]) + ->setBrutProb((int)$operationalRiskData['brutProb']) + ->setNetProb((int)$operationalRiskData['netProb']) + ->setTargetedProb((int)$operationalRiskData['targetedProb']) + ->setCacheBrutRisk((int)$operationalRiskData['cacheBrutRisk']) + ->setCacheNetRisk((int)$operationalRiskData['cacheNetRisk']) + ->setCacheTargetedRisk((int)$operationalRiskData['cacheTargetedRisk']) + ->setKindOfMeasure((int)$operationalRiskData['kindOfMeasure']) + ->setComment($operationalRiskData['comment'] ?? '') + ->setMitigation($operationalRiskData['mitigation'] ?? '') + ->setIsSpecific((bool)$operationalRiskData['specific']) + ->setContext($operationalRiskData['context'] ?? '') + ->setCreator($this->connectedUser->getEmail()); + + if (!empty($operationalRiskData['riskOwner'])) { + $instanceRiskOwner = $this->instanceRiskOwnerService->getOrCreateInstanceRiskOwner( + $anr, + $anr, + $operationalRiskData['riskOwner'] + ); + $operationalInstanceRisk->setInstanceRiskOwner($instanceRiskOwner); + } + + if ($areScalesLevelsOfLikelihoodDifferent) { + $this->operationalRiskScaleImportProcessor->adjustOperationalRisksProbabilityScales( + $operationalInstanceRisk, + $externalOperationalRiskScalesData[OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD], + $operationalRiskScalesData[OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD] + ); + } + + if (!empty($operationalRiskData['rolfRisk']) && $monarcObject->getRolfTag() !== null) { + /** @var Entity\RolfRisk|null $rolfRisk */ + $rolfRisk = $this->importCacheHelper->getItemFromArrayCache( + 'rolf_risks_by_old_ids', + (int)$operationalRiskData['rolfRisk'] + ); + if ($rolfRisk !== null) { + $operationalInstanceRisk->setRolfRisk($rolfRisk)->setRiskCacheCode($rolfRisk->getCode()); + } + } + + $impactScaleData = $operationalRiskScalesData[OperationalRiskScaleSuperClass::TYPE_IMPACT]; + foreach ($impactScaleData['operationalRiskScaleTypes'] as $index => $scaleType) { + /** @var Entity\OperationalRiskScaleType $operationalRiskScaleType */ + $operationalRiskScaleType = $scaleType['object']; + $operationalInstanceRiskScale = (new Entity\OperationalInstanceRiskScale()) + ->setAnr($anr) + ->setOperationalRiskScaleType($operationalRiskScaleType) + ->setOperationalInstanceRisk($operationalInstanceRisk) + ->setCreator($this->connectedUser->getEmail()); + + if ($includeEval) { + /* The format is since v2.11.0 */ + if (isset($operationalRiskData['scalesValues'])) { + $externalScaleTypeId = null; + if ($isImportTypeAnr) { + /* For anr import, match current scale type translation key with external ids. */ + $externalScaleTypeId = $this->getExternalScaleTypeIdByCurrentScaleLabel( + $operationalRiskScaleType->getLabel() + ); + } elseif (isset($matchedScaleTypesMap['currentScaleTypeLabelToExternalIds'][ + $operationalRiskScaleType->getLabel() + ])) { + /* For instance import, match current scale type label with external ids. */ + $externalScaleTypeId = $matchedScaleTypesMap['currentScaleTypeLabelToExternalIds'][ + $operationalRiskScaleType->getLabel() + ]; + } + if ($externalScaleTypeId !== null + && isset($operationalRiskData['scalesValues'][$externalScaleTypeId]) + ) { + $scalesValueData = $operationalRiskData['scalesValues'][$externalScaleTypeId]; + $operationalInstanceRiskScale->setBrutValue($scalesValueData['brutValue']); + $operationalInstanceRiskScale->setNetValue($scalesValueData['netValue']); + $operationalInstanceRiskScale->setTargetedValue($scalesValueData['targetedValue']); + if ($areImpactScaleTypesValuesDifferent) { + /* We convert from the importing new scales to the current anr scales. */ + $this->operationalRiskScaleImportProcessor->adjustOperationalInstanceRisksScales( + $operationalInstanceRiskScale, + $externalOperationalRiskScalesData[OperationalRiskScaleSuperClass::TYPE_IMPACT], + $impactScaleData + ); + } + } + /* The format before v2.11.0. Update only first 5 scales (ROLFP if not changed by user). */ + } elseif ($index < 5) { + foreach ($oldInstanceRiskFieldsMapToScaleTypesFields[$index] as $oldFiled => $typeField) { + $operationalInstanceRiskScale->{'set' . $typeField}($operationalRiskData[$oldFiled]); + } + if ($areImpactScaleTypesValuesDifferent) { + /* We convert from the importing new scales to the current anr scales. */ + $this->operationalRiskScaleImportProcessor->adjustOperationalInstanceRisksScales( + $operationalInstanceRiskScale, + $externalOperationalRiskScalesData[OperationalRiskScaleSuperClass::TYPE_IMPACT], + $impactScaleData + ); + } + } + } + + $this->operationalInstanceRiskScaleTable->save($operationalInstanceRiskScale, false); + } + + if (!empty($matchedScaleTypesMap['notMatchedScaleTypes']) && !$isImportTypeAnr) { + /* In case of instance import, there is a need to create external scale types in case if + the linked values are set for at least one operational instance risk. + The new created type has to be linked with all the existed risks. */ + foreach ($matchedScaleTypesMap['notMatchedScaleTypes'] as $extScaleTypeId => $extScaleTypeData) { + if (isset($operationalRiskData['scalesValues'][$extScaleTypeId])) { + $scalesValueData = $operationalRiskData['scalesValues'][$extScaleTypeId]; + if ($scalesValueData['netValue'] !== -1 + || $scalesValueData['brutValue'] !== -1 + || $scalesValueData['targetedValue'] !== -1 + ) { + $operationalRiskScaleType = (new Entity\OperationalRiskScaleType()) + ->setAnr($anr) + ->setOperationalRiskScale($impactScaleData['object']) + ->setLabel($extScaleTypeData['label'] ?? $extScaleTypeData['translation']['value']) + ->setCreator($this->connectedUser->getEmail()); + $this->operationalRiskScaleTypeTable->save($operationalRiskScaleType, false); + + foreach ($extScaleTypeData['operationalRiskScaleComments'] as $scaleCommentData) { + $this->operationalRiskScaleImportProcessor->createOrUpdateOperationalRiskScaleComment( + $anr, + false, + $impactScaleData['object'], + $scaleCommentData, + [], + $operationalRiskScaleType + ); + } + + $operationalInstanceRiskScale = (new Entity\OperationalInstanceRiskScale()) + ->setAnr($anr) + ->setOperationalInstanceRisk($operationalInstanceRisk) + ->setOperationalRiskScaleType($operationalRiskScaleType) + ->setBrutValue($scalesValueData['brutValue']) + ->setNetValue($scalesValueData['netValue']) + ->setTargetedValue($scalesValueData['targetedValue']) + ->setCreator($this->connectedUser->getEmail()); + $this->operationalInstanceRiskScaleTable->save($operationalInstanceRiskScale, false); + + $this->operationalRiskScaleImportProcessor->adjustOperationalInstanceRisksScales( + $operationalInstanceRiskScale, + $externalOperationalRiskScalesData[OperationalRiskScaleSuperClass::TYPE_IMPACT], + $impactScaleData + ); + + /* To swap the scale risk between the two keys in the map as it is already matched. */ + unset($matchedScaleTypesMap['notMatchedScaleTypes'][$extScaleTypeId]); + $matchedScaleTypesMap['currentScaleTypeLabelToExternalIds'][ + $operationalRiskScaleType->getLabel() + ] = $extScaleTypeId; + + /* Due to the new scale type and related comments the cached the data has to be updated. */ + [$scaleTypesData, $commentsIndexToValueMap] = $this->operationalRiskScaleImportProcessor + ->prepareScaleTypesDataAndCommentsIndexToValueMap($impactScaleData['object']); + $this->importCacheHelper->addItemToArrayCache('current_operational_risk_scales_data', [ + 'min' => $impactScaleData['min'], + 'max' => $impactScaleData['max'], + 'object' => $impactScaleData['object'], + 'commentsIndexToValueMap' => $commentsIndexToValueMap, + 'operationalRiskScaleTypes' => $scaleTypesData, + ], OperationalRiskScaleSuperClass::TYPE_IMPACT); + $operationalRiskScalesData = $this->operationalRiskScaleImportProcessor + ->getCurrentOperationalRiskScalesData($anr); + $areImpactScaleTypesValuesDifferent = true; + + /* Link the newly created scale type to all the existed operational risks. */ + $operationalInstanceRisks = $this->instanceRiskOpTable->findByAnrAndInstance( + $anr, + $instance + ); + foreach ($operationalInstanceRisks as $operationalInstanceRiskToUpdate) { + if ($operationalInstanceRiskToUpdate->getId() !== $operationalInstanceRisk->getId()) { + $this->operationalInstanceRiskScaleTable->save( + (new Entity\OperationalInstanceRiskScale()) + ->setAnr($anr) + ->setOperationalInstanceRisk($operationalInstanceRiskToUpdate) + ->setOperationalRiskScaleType($operationalRiskScaleType) + ->setCreator($this->connectedUser->getEmail()), + false + ); + } + } + } + } + } + } + + if ($includeEval) { + /* recalculate the cached risk values */ + $this->anrInstanceRiskOpService->updateRiskCacheValues($operationalInstanceRisk); + } + + $this->instanceRiskOpTable->save($operationalInstanceRisk, false); + + /* Process recommendations related to the operational risk. */ + if ($includeEval && !empty($data['recosop'][$operationalRiskData['id']])) { + foreach ($data['recosop'][$operationalRiskData['id']] as $recommendationData) { + $recommendation = $this->recommendationImportProcessor->processRecommendationDataLinkedToRisk( + $anr, + $recommendationData, + $data['recSets'], + $operationalRiskData['kindOfMeasure'] !== InstanceRiskOpSuperClass::KIND_NOT_TREATED + ); + + $this->anrRecommendationRiskService->createRecommendationRisk( + $recommendation, + $operationalInstanceRisk, + $recommendationData['commentAfter'] ?? '', + false + ); + } + } + } + } + + private function matchAndGetOperationalRiskScaleTypesMap( + array $operationalRiskScalesData, + array $externalOperationalRiskScalesData + ): array { + $matchedScaleTypesMap = [ + 'currentScaleTypeLabelToExternalIds' => [], + 'notMatchedScaleTypes' => [], + ]; + $scaleTypesData = $operationalRiskScalesData[OperationalRiskScaleSuperClass::TYPE_IMPACT][ + 'operationalRiskScaleTypes' + ]; + $externalScaleTypesData = $externalOperationalRiskScalesData[OperationalRiskScaleSuperClass::TYPE_IMPACT][ + 'operationalRiskScaleTypes' + ]; + foreach ($externalScaleTypesData as $externalScaleTypeData) { + $isMatched = false; + foreach ($scaleTypesData as $scaleTypeData) { + /** @var Entity\OperationalRiskScaleType $scaleType */ + $scaleType = $scaleTypeData['object']; + $externalLabel = $externalScaleTypeData['label'] ?? $externalScaleTypeData['translation']['value']; + if ($externalLabel === $scaleType->getLabel()) { + $matchedScaleTypesMap['currentScaleTypeLabelToExternalIds'][$scaleType->getLabel()] + = $externalScaleTypeData['id']; + $isMatched = true; + break; + } + } + if (!$isMatched) { + $matchedScaleTypesMap['notMatchedScaleTypes'][$externalScaleTypeData['id']] = $externalScaleTypeData; + } + } + + return $matchedScaleTypesMap; + } + + private function getExternalScaleTypeIdByCurrentScaleLabel(string $label): ?int + { + return $this->cachedData['operationalRiskScaleTypes']['currentScaleTypeLabelToExternalIds'][$label] ?? null; + } + + private function areLikelihoodScalesLevelsOfTypeDifferent( + array $operationalRiskScales, + array $externalOperationalRiskScalesData + ): bool { + $likelihoodType = OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD; + foreach ($operationalRiskScales as $scaleType => $operationalRiskScale) { + if ($scaleType === $likelihoodType) { + $externalScaleDataOfType = $externalOperationalRiskScalesData[$likelihoodType]; + if ($operationalRiskScale['min'] !== $externalScaleDataOfType['min'] + || $operationalRiskScale['max'] !== $externalScaleDataOfType['max']) { + return true; + } + } + } + + return false; + } + + /** + * Checks if any of the scale comments values related to the scale types have different scaleValue + * then in the new operational scale data. + */ + private function areImpactScaleTypeValuesDifferent( + array $operationalRiskScales, + array $extOperationalRiskScalesData + ): bool { + $impactType = OperationalRiskScaleSuperClass::TYPE_IMPACT; + foreach ($operationalRiskScales[$impactType]['commentsIndexToValueMap'] as $scaleIndex => $scaleValue) { + if (!isset($extOperationalRiskScalesData[$impactType]['commentsIndexToValueMap'][$scaleIndex])) { + return true; + } + $extScaleValue = $extOperationalRiskScalesData[$impactType]['commentsIndexToValueMap'][$scaleIndex]; + if ($scaleValue !== $extScaleValue) { + return true; + } + } + + return false; + } +} diff --git a/src/Import/Processor/OperationalRiskScaleImportProcessor.php b/src/Import/Processor/OperationalRiskScaleImportProcessor.php new file mode 100644 index 00000000..533227ab --- /dev/null +++ b/src/Import/Processor/OperationalRiskScaleImportProcessor.php @@ -0,0 +1,548 @@ +connectedUser = $connectedUserService->getConnectedUser(); + } + + public function adjustOperationalRisksScaleValuesBasedOnNewScales(Entity\Anr $anr, array $data): void + { + /** @var Entity\InstanceRiskOp[] $operationalInstanceRisks */ + $operationalInstanceRisks = $this->instanceRiskOpTable->findByAnr($anr); + if (!empty($operationalInstanceRisks)) { + $currentOperationalRiskScalesData = $this->getCurrentOperationalRiskScalesData($anr); + $externalOperationalRiskScalesData = $this->getExternalOperationalRiskScalesData($anr, $data); + + foreach ($operationalInstanceRisks as $operationalInstanceRisk) { + $this->adjustOperationalRisksProbabilityScales( + $operationalInstanceRisk, + $currentOperationalRiskScalesData[OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD], + $externalOperationalRiskScalesData[OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD] + ); + + foreach ($operationalInstanceRisk->getOperationalInstanceRiskScales() as $instanceRiskScale) { + $this->adjustOperationalInstanceRisksScales( + $instanceRiskScale, + $currentOperationalRiskScalesData[OperationalRiskScaleSuperClass::TYPE_IMPACT], + $externalOperationalRiskScalesData[OperationalRiskScaleSuperClass::TYPE_IMPACT] + ); + } + + $this->instanceRiskOpTable->save($operationalInstanceRisk, false); + + $this->anrInstanceRiskOpService->updateRiskCacheValues($operationalInstanceRisk); + } + } + } + + public function adjustOperationalRisksProbabilityScales( + Entity\InstanceRiskOp $operationalInstanceRisk, + array $fromOperationalRiskScalesData, + array $toOperationalRiskScalesData + ): void { + foreach (['NetProb', 'BrutProb', 'TargetedProb'] as $likelihoodScaleName) { + $operationalInstanceRisk->{'set' . $likelihoodScaleName}( + $this->convertValueWithinNewScalesRange( + $operationalInstanceRisk->{'get' . $likelihoodScaleName}(), + $fromOperationalRiskScalesData['min'], + $fromOperationalRiskScalesData['max'], + $toOperationalRiskScalesData['min'], + $toOperationalRiskScalesData['max'] + ) + ); + } + } + + public function adjustOperationalInstanceRisksScales( + Entity\OperationalInstanceRiskScale $instanceRiskScale, + array $fromOperationalRiskScalesData, + array $toOperationalRiskScalesData + ): void { + foreach (['NetValue', 'BrutValue', 'TargetedValue'] as $impactScaleName) { + $scaleImpactValue = $instanceRiskScale->{'get' . $impactScaleName}(); + if ($scaleImpactValue === -1) { + continue; + } + $scaleImpactIndex = array_search( + $scaleImpactValue, + $fromOperationalRiskScalesData['commentsIndexToValueMap'], + true + ); + if ($scaleImpactIndex === false) { + continue; + } + + $approximatedIndex = $this->convertValueWithinNewScalesRange( + $scaleImpactIndex, + $fromOperationalRiskScalesData['min'], + $fromOperationalRiskScalesData['max'], + $toOperationalRiskScalesData['min'], + $toOperationalRiskScalesData['max'] + ); + + $approximatedValueToNewScales = $toOperationalRiskScalesData['commentsIndexToValueMap'][$approximatedIndex] + ?? $scaleImpactValue; + $instanceRiskScale->{'set' . $impactScaleName}($approximatedValueToNewScales); + + $this->operationalInstanceRiskScaleTable->save($instanceRiskScale, false); + } + } + + public function getCurrentOperationalRiskScalesData(Entity\Anr $anr): array + { + if (empty($this->importCacheHelper->isCacheKeySet('current_operational_risk_scales_data'))) { + /** @var Entity\OperationalRiskScale $operationalRiskScale */ + foreach ($this->operationalRiskScaleTable->findByAnr($anr) as $operationalRiskScale) { + [$scaleTypesData, $commentsIndexToValueMap] = $this->prepareScaleTypesDataAndCommentsIndexToValueMap( + $operationalRiskScale + ); + + $this->importCacheHelper->addItemToArrayCache('current_operational_risk_scales_data', [ + 'min' => $operationalRiskScale->getMin(), + 'max' => $operationalRiskScale->getMax(), + 'object' => $operationalRiskScale, + 'commentsIndexToValueMap' => $commentsIndexToValueMap, + 'operationalRiskScaleTypes' => $scaleTypesData, + ], $operationalRiskScale->getType()); + } + } + + return $this->importCacheHelper->getItemFromArrayCache('current_operational_risk_scales_data'); + } + + /** + * 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). + */ + public function getExternalOperationalRiskScalesData(Entity\Anr $anr, array $data): array + { + if (!$this->importCacheHelper->isCacheKeySet('external_operational_risk_scales_data')) { + /* Populate with informational risks scales if there is an import of a file exported prior v2.11.0. */ + $scalesDataResult = [ + OperationalRiskScaleSuperClass::TYPE_IMPACT => [ + 'min' => 0, + 'max' => $data['scales'][ScaleSuperClass::TYPE_IMPACT]['max'] + - $data['scales'][ScaleSuperClass::TYPE_IMPACT]['min'], + 'commentsIndexToValueMap' => [], + 'operationalRiskScaleTypes' => [], + 'operationalRiskScaleComments' => [], + ], + OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD => [ + 'min' => $data['scales'][ScaleSuperClass::TYPE_THREAT]['min'], + 'max' => $data['scales'][ScaleSuperClass::TYPE_THREAT]['max'], + 'commentsIndexToValueMap' => [], + 'operationalRiskScaleTypes' => [], + 'operationalRiskScaleComments' => [], + ], + ]; + if (!empty($data['operationalRiskScales'])) { + /* Overwrite the values for the version >= v2.11.0. */ + foreach ($data['operationalRiskScales'] as $operationalRiskScaleData) { + $scaleType = $operationalRiskScaleData['type']; + $scalesDataResult[$scaleType]['min'] = $operationalRiskScaleData['min']; + $scalesDataResult[$scaleType]['max'] = $operationalRiskScaleData['max']; + + /* Build the map of the comments index <=> values relation. */ + foreach ($operationalRiskScaleData['operationalRiskScaleTypes'] as $scaleTypeData) { + $scalesDataResult[$scaleType]['operationalRiskScaleTypes'][] = $scaleTypeData; + /* All the scale comment have the same index->value corresponding values, so populating once. */ + if (empty($scalesDataResult[$scaleType]['commentsIndexToValueMap'])) { + foreach ($scaleTypeData['operationalRiskScaleComments'] as $scaleTypeComment) { + if (!$scaleTypeComment['isHidden']) { + $scalesDataResult[$scaleType]['commentsIndexToValueMap'] + [$scaleTypeComment['scaleIndex']] = $scaleTypeComment['scaleValue']; + } + } + } + } + + $scalesDataResult[$scaleType]['operationalRiskScaleComments'] = $operationalRiskScaleData[ + 'operationalRiskScaleComments' + ]; + } + } else { + /* Convert comments and types from informational risks to operational (new format). */ + $scaleMin = $data['scales'][ScaleSuperClass::TYPE_IMPACT]['min']; + foreach (OperationalRiskScaleTypeSuperClass::getDefaultScalesImpacts() as $index => $scaleTypeLabels) { + /* Previous ROLFP scales impact types (types indexes were [4, 5, 6, 7, 8]). */ + $scalesDataResult[ScaleSuperClass::TYPE_IMPACT]['operationalRiskScaleTypes'][$index + 4] = [ + 'isHidden' => false, + 'label' => $scaleTypeLabels[$anr->getLanguageCode()], + ]; + } + foreach ($data['scalesComments'] as $scaleComment) { + $scaleType = $scaleComment['scale']['type']; + if (!\in_array($scaleType, [ScaleSuperClass::TYPE_IMPACT, ScaleSuperClass::TYPE_THREAT], true)) { + continue; + } + + $scaleCommentLabel = $scaleComment['comment'] ?? $scaleComment['comment' . $anr->getLanguage()]; + if ($scaleType === ScaleSuperClass::TYPE_THREAT) { + $scalesDataResult[$scaleType]['operationalRiskScaleComments'][] = [ + 'scaleIndex' => $scaleComment['val'], + 'scaleValue' => $scaleComment['val'], + 'isHidden' => false, + 'comment' => $scaleCommentLabel, + ]; + } elseif ($scaleType === ScaleSuperClass::TYPE_IMPACT + && $scaleComment['val'] >= $scaleMin + ) { + $scaleIndex = $scaleComment['val'] - $scaleMin; + $scaleTypePosition = $scaleComment['scaleImpactType']['position']; + if (isset($scalesDataResult[$scaleType]['operationalRiskScaleTypes'][$scaleTypePosition])) { + $scalesDataResult[$scaleType]['operationalRiskScaleTypes'][$scaleTypePosition][ + 'operationalRiskScaleComments' + ][] = [ + 'scaleIndex' => $scaleIndex, + 'scaleValue' => $scaleComment['val'], + 'isHidden' => false, + 'comment' => $scaleCommentLabel, + ]; + + $scalesDataResult[$scaleType]['commentsIndexToValueMap'][ + $scaleIndex + ] = $scaleComment['val']; + } + } + } + } + + $this->importCacheHelper->addItemToArrayCache( + 'external_operational_risk_scales_data', + $scalesDataResult[OperationalRiskScaleSuperClass::TYPE_IMPACT], + OperationalRiskScaleSuperClass::TYPE_IMPACT + ); + $this->importCacheHelper->addItemToArrayCache( + 'external_operational_risk_scales_data', + $scalesDataResult[OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD], + OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD + ); + } + + return $this->importCacheHelper->getItemFromArrayCache('external_operational_risk_scales_data'); + } + + public function updateOperationalRisksScalesAndRelatedInstances(Entity\Anr $anr, array $data): void + { + $externalOperationalScalesData = $this->getExternalOperationalRiskScalesData($anr, $data); + /** @var Entity\OperationalRiskScale $operationalRiskScale */ + foreach ($this->operationalRiskScaleTable->findByAnr($anr) as $operationalRiskScale) { + $externalScaleData = $externalOperationalScalesData[$operationalRiskScale->getType()]; + $currentScaleLevelDifferenceFromExternal = $operationalRiskScale->getMax() - $externalScaleData['max']; + $operationalRiskScale + ->setAnr($anr) + ->setMin($externalScaleData['min']) + ->setMax($externalScaleData['max']) + ->setUpdater($this->connectedUser->getEmail()); + + /* This is currently only applicable for impact scales type. */ + $createdScaleTypes = []; + $matchedScaleTypes = []; + foreach ($externalScaleData['operationalRiskScaleTypes'] as $scaleTypeData) { + $isScaleTypeMatched = true; + $externalScaleTypeLabel = $scaleTypeData['label'] ?? $scaleTypeData['translation']['value']; + $operationalRiskScaleType = $this->matchScaleTypeWithScaleTypesListByLabel( + $operationalRiskScale->getOperationalRiskScaleTypes(), + $externalScaleTypeLabel + ); + if ($operationalRiskScaleType === null) { + $isScaleTypeMatched = false; + $operationalRiskScaleType = (new Entity\OperationalRiskScaleType()) + ->setAnr($anr) + ->setOperationalRiskScale($operationalRiskScale) + ->setLabel($externalScaleTypeLabel) + ->setCreator($this->connectedUser->getEmail()); + + $createdScaleTypes[$operationalRiskScaleType->getLabel()] = $operationalRiskScaleType; + } elseif ($currentScaleLevelDifferenceFromExternal !== 0) { + $matchedScaleTypes[$operationalRiskScaleType->getId()] = $operationalRiskScaleType; + } + + /* The map is used to match for the importing operational risks, scale values with scale types. */ + $this->cachedData['operationalRiskScaleTypes']['currentScaleTypeLabelToExternalIds'][ + $operationalRiskScaleType->getLabel() + ] = $scaleTypeData['id']; + + $operationalRiskScaleType->setIsHidden($scaleTypeData['isHidden']); + $this->operationalRiskScaleTypeTable->save($operationalRiskScaleType, false); + + foreach ($scaleTypeData['operationalRiskScaleComments'] as $scaleTypeCommentData) { + $this->createOrUpdateOperationalRiskScaleComment( + $anr, + $isScaleTypeMatched, + $operationalRiskScale, + $scaleTypeCommentData, + $operationalRiskScaleType->getOperationalRiskScaleComments(), + $operationalRiskScaleType + ); + } + } + + /* Create relations of all the created scales with existed risks. */ + if (!empty($createdScaleTypes)) { + /** @var Entity\InstanceRiskOp $operationalInstanceRisk */ + foreach ($this->instanceRiskOpTable->findByAnr($anr) as $operationalInstanceRisk) { + foreach ($createdScaleTypes as $createdScaleType) { + $operationalInstanceRiskScale = (new Entity\OperationalInstanceRiskScale()) + ->setAnr($anr) + ->setOperationalRiskScaleType($createdScaleType) + ->setOperationalInstanceRisk($operationalInstanceRisk) + ->setCreator($this->connectedUser->getEmail()); + $this->operationalInstanceRiskScaleTable->save($operationalInstanceRiskScale, false); + } + } + } + + $maxIndexForLikelihood = 0; + /* This is currently applicable only for likelihood scales type */ + foreach ($externalScaleData['object']->getOperationalRiskScaleComments() as $scaleCommentData) { + + $this->createOrUpdateOperationalRiskScaleComment( + $anr, + true, + $operationalRiskScale, + $scaleCommentData, + $operationalRiskScale->getOperationalRiskScaleComments(), + ); + $maxIndexForLikelihood = (int)$scaleCommentData['scaleIndex'] > $maxIndexForLikelihood + ? (int)$scaleCommentData['scaleIndex'] + : $maxIndexForLikelihood; + } + /* Manage a case when the scale (probability) is not matched and level higher than external. */ + if ($maxIndexForLikelihood !== 0 + && $operationalRiskScale->getType() === OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD + ) { + foreach ($operationalRiskScale->getOperationalRiskScaleComments() as $comment) { + if ($comment->getScaleIndex() >= $maxIndexForLikelihood) { + $comment->setIsHidden(true); + $this->operationalRiskScaleCommentTable->save($comment, false); + } + } + } + + /* Validate if any existed comments are now out of the new scales bound and if their values are valid. + Also, if their comments are complete per scale's level. */ + if ($currentScaleLevelDifferenceFromExternal !== 0) { + /** @var Entity\OperationalRiskScaleType $operationalRiskScaleType */ + foreach ($operationalRiskScale->getOperationalRiskScaleTypes() as $operationalRiskScaleType) { + /* Ignore the currently created scale types. */ + if (\array_key_exists($operationalRiskScaleType->getLabel(), $createdScaleTypes)) { + continue; + } + + if ($currentScaleLevelDifferenceFromExternal < 0 + && !\array_key_exists($operationalRiskScaleType->getId(), $matchedScaleTypes) + ) { + /* The scales type was not matched and the current scales level is lower than external, + so we need to create missing empty scales comments. */ + $commentIndex = $operationalRiskScale->getMax() + $currentScaleLevelDifferenceFromExternal + 1; + $commentIndexToValueMap = $externalOperationalScalesData[ + OperationalRiskScaleSuperClass::TYPE_IMPACT + ]['commentsIndexToValueMap']; + while ($commentIndex <= $operationalRiskScale->getMax()) { + $this->createOrUpdateOperationalRiskScaleComment($anr, false, $operationalRiskScale, [ + 'scaleIndex' => $commentIndex, + 'scaleValue' => $commentIndexToValueMap[$commentIndex], + 'isHidden' => false, + 'comment' => '', + ], [], $operationalRiskScaleType); + $commentIndex++; + } + + continue; + } + + if ($currentScaleLevelDifferenceFromExternal > 0) { + $commentIndexToValueMap = $externalOperationalScalesData[ + 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 than external, + so we need to hide their comments and validate values. */ + foreach ($matchedScaleTypes as $matchedScaleType) { + $this->processCurrentScalesCommentsWhenLimitIsHigher( + $operationalRiskScale, + $matchedScaleType, + $maxValue + ); + } + } else { + /* Manage a case when the scale is not matched and level higher than external */ + $this->processCurrentScalesCommentsWhenLimitIsHigher( + $operationalRiskScale, + $operationalRiskScaleType, + $maxValue + ); + } + } + } + } + + $this->operationalRiskScaleTable->save($operationalRiskScale, false); + + [$scaleTypesData, $commentsIndexToValueMap] = $this->prepareScaleTypesDataAndCommentsIndexToValueMap( + $operationalRiskScale + ); + /* Update the values in the cache. */ + $this->importCacheHelper->addItemToArrayCache('current_operational_risk_scales_data', [ + 'min' => $operationalRiskScale->getMin(), + 'max' => $operationalRiskScale->getMax(), + 'object' => $operationalRiskScale, + 'commentsIndexToValueMap' => $commentsIndexToValueMap, + 'operationalRiskScaleTypes' => $scaleTypesData, + ], $operationalRiskScale->getType()); + } + } + + public function createOrUpdateOperationalRiskScaleComment( + Entity\Anr $anr, + bool $isMatchRequired, + Entity\OperationalRiskScale $operationalRiskScale, + array $scaleCommentData, + iterable $scaleCommentsToMatchWith, + ?Entity\OperationalRiskScaleType $operationalRiskScaleType = null + ): Entity\OperationalRiskScaleComment { + $operationalRiskScaleComment = null; + if ($isMatchRequired) { + $operationalRiskScaleComment = $this->matchScaleCommentDataWithScaleCommentsList( + $operationalRiskScale, + $scaleCommentData, + $scaleCommentsToMatchWith, + ); + } + if ($operationalRiskScaleComment === null) { + $operationalRiskScaleComment = (new Entity\OperationalRiskScaleComment()) + ->setAnr($anr) + ->setOperationalRiskScale($operationalRiskScale) + ->setComment($scaleCommentData['comment'] ?? $scaleCommentData['translation']['value']) + ->setCreator($this->connectedUser->getEmail()); + } + + if ($operationalRiskScaleType !== null) { + $operationalRiskScaleComment->setOperationalRiskScaleType($operationalRiskScaleType); + } + + $operationalRiskScaleComment + ->setScaleIndex($scaleCommentData['scaleIndex']) + ->setScaleValue($scaleCommentData['scaleValue']) + ->setIsHidden($scaleCommentData['isHidden']); + $this->operationalRiskScaleCommentTable->save($operationalRiskScaleComment, false); + + return $operationalRiskScaleComment; + } + + public function prepareScaleTypesDataAndCommentsIndexToValueMap( + Entity\OperationalRiskScale $operationalRiskScale + ): array { + $scaleTypesData = []; + $commentsIndexToValueMap = []; + /* Build the map of the comments index <=> values relation. */ + foreach ($operationalRiskScale->getOperationalRiskScaleTypes() as $scaleType) { + /* The operational risk scale types object is used to recreate operational instance risk scales. */ + $scaleTypesData[]['object'] = $scaleType; + /* All the scale comment have the same index -> value corresponding values, so populating once. */ + if (empty($commentsIndexToValueMap)) { + foreach ($scaleType->getOperationalRiskScaleComments() as $scaleTypeComment) { + if (!$scaleTypeComment->isHidden()) { + $commentsIndexToValueMap[$scaleTypeComment->getScaleIndex()] = + $scaleTypeComment->getScaleValue(); + } + } + } + } + + return [$scaleTypesData, $commentsIndexToValueMap]; + } + + /** + * @param Entity\OperationalRiskScaleComment[] $operationalRiskScaleComments + */ + private function matchScaleCommentDataWithScaleCommentsList( + Entity\OperationalRiskScale $operationalRiskScale, + array $scaleTypeCommentData, + iterable $operationalRiskScaleComments, + ): ?Entity\OperationalRiskScaleComment { + foreach ($operationalRiskScaleComments as $operationalRiskScaleComment) { + if ($operationalRiskScaleComment->getScaleIndex() === $scaleTypeCommentData['scaleIndex'] + && $operationalRiskScale->getId() === $operationalRiskScaleComment->getOperationalRiskScale()->getId() + ) { + $commentLabel = $scaleTypeCommentData['comment'] ?? $scaleTypeCommentData['translation']['value']; + if ($operationalRiskScaleComment->getComment() !== $commentLabel) { + $this->operationalRiskScaleCommentTable->save($operationalRiskScaleComment, false); + } + + return $operationalRiskScaleComment; + } + } + + return null; + } + + /** + * Matches local operational risks' scale types with importing ones by label or translation value (prior v2.13.1). + * + * @param Entity\OperationalRiskScaleType[] $operationalRiskScaleTypes + */ + private function matchScaleTypeWithScaleTypesListByLabel( + iterable $operationalRiskScaleTypes, + string $scaleTypeLabel, + ): ?Entity\OperationalRiskScaleType { + foreach ($operationalRiskScaleTypes as $operationalRiskScaleType) { + if ($operationalRiskScaleType->getLabel() === $scaleTypeLabel) { + return $operationalRiskScaleType; + } + } + + return null; + } + + private function processCurrentScalesCommentsWhenLimitIsHigher( + Entity\OperationalRiskScale $operationalRiskScale, + Entity\OperationalRiskScaleType $operationalRiskScaleType, + int $maxValue + ): void { + foreach ($operationalRiskScaleType->getOperationalRiskScaleComments() as $comment) { + $isHidden = $operationalRiskScale->getMin() > $comment->getScaleIndex() + || $operationalRiskScale->getMax() < $comment->getScaleIndex(); + $comment->setIsHidden($isHidden); + if ($isHidden && $maxValue >= $comment->getScaleValue()) { + $comment->setScaleValue(++$maxValue); + } + + $this->operationalRiskScaleCommentTable->save($comment, false); + } + } +} diff --git a/src/Import/Processor/RecommendationImportProcessor.php b/src/Import/Processor/RecommendationImportProcessor.php index c7d6404d..24a26a88 100644 --- a/src/Import/Processor/RecommendationImportProcessor.php +++ b/src/Import/Processor/RecommendationImportProcessor.php @@ -11,12 +11,15 @@ use Monarc\FrontOffice\Import\Helper\ImportCacheHelper; use Monarc\FrontOffice\Service\AnrRecommendationService; use Monarc\FrontOffice\Service\AnrRecommendationSetService; -use Monarc\FrontOffice\Table\RecommendationSetTable; +use Monarc\FrontOffice\Table; class RecommendationImportProcessor { + private int $currentMaxRecommendationPosition = 0; + public function __construct( - private RecommendationSetTable $recommendationSetTable, + private Table\RecommendationSetTable $recommendationSetTable, + private Table\RecommendationTable $recommendationTable, private ImportCacheHelper $importCacheHelper, private AnrRecommendationSetService $anrRecommendationSetService, private AnrRecommendationService $anrRecommendationService @@ -31,8 +34,10 @@ public function processRecommendationSetsData(Entity\Anr $anr, array $recommenda } } - public function processRecommendationSetData(Entity\Anr $anr, array $recommendationSetData): void - { + public function processRecommendationSetData( + Entity\Anr $anr, + array $recommendationSetData + ): Entity\RecommendationSet { $labelKey = 'label' . $anr->getLanguage(); /* Supports the structure format prior v2.13.1 */ if (isset($recommendationSetData[$labelKey]) && !isset($recommendationSetData['label'])) { @@ -54,6 +59,8 @@ public function processRecommendationSetData(Entity\Anr $anr, array $recommendat if (!empty($recommendationSetData['recommendations'])) { $this->processRecommendationsData($recommendationSet, $recommendationSetData['recommendations']); } + + return $recommendationSet; } public function processRecommendationsData( @@ -97,7 +104,7 @@ public function processRecommendationData( public function getRecommendationSetFromCache(string $uuid, string $label): ?Entity\RecommendationSet { $recommendationSet = $this->importCacheHelper->getItemFromArrayCache('recommendations_sets', $uuid); - if ($recommendationSet === null) { + if ($recommendationSet === null && $label !== '') { /** @var Entity\RecommendationSet $set */ foreach ($this->importCacheHelper->getItemFromArrayCache('recommendations_sets') ?? [] as $set) { if ($set->getLabel() === $label) { @@ -118,6 +125,7 @@ public function getRecommendationFromCache(string $uuid): ?Entity\Recommendation public function prepareRecommendationsCache(Entity\Anr $anr): void { if (!$this->importCacheHelper->isCacheKeySet('recommendations_sets')) { + $this->currentMaxRecommendationPosition = $this->recommendationTable->findMaxPosition(['anr' => $anr]); /** @var Entity\RecommendationSet $recommendationSet */ foreach ($this->recommendationSetTable->findByAnr($anr) as $recommendationSet) { $this->importCacheHelper->addItemToArrayCache( @@ -142,4 +150,82 @@ public function prepareRecommendationsCache(Entity\Anr $anr): void } } } + + /* Is called from the risks import processors. */ + public function processRecommendationDataLinkedToRisk( + Entity\Anr $anr, + array $recommendationData, + array $recommendationsSetsData, + bool $isRiskTreated, + ): Entity\Recommendation { + $recommendation = $this->getRecommendationFromCache($recommendationData['uuid']); + if ($recommendation !== null) { + if ($isRiskTreated && $recommendation->isPositionEmpty()) { + $recommendation->setPosition(++$this->currentMaxRecommendationPosition); + $this->recommendationTable->save($recommendation, false); + } + + return $recommendation; + } + [$recommendationSetUuid, $recommendationSetLabel] = $this->getRecommendationSetParams( + $anr, + $recommendationData, + $recommendationsSetsData + ); + + $recommendationSet = $this->getRecommendationSetFromCache($recommendationSetUuid, $recommendationSetLabel); + if ($recommendationSet === null) { + $recommendationSetData = [ + 'uuid' => $recommendationSetUuid, + 'label' => $recommendationSetLabel, + ]; + + $recommendationSet = $this->processRecommendationSetData($anr, $recommendationSetData); + } + $recommendationData['recommendationSet'] = $recommendationSet; + + /* 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(); + } + + if ($isRiskTreated) { + $recommendationData['position'] = ++$this->currentMaxRecommendationPosition; + } + /* Support the structure fields names prior v2.13.1. */ + if (!isset($recommendationData['responsible']) && isset($recommendationData['responsible'])) { + $recommendationData['responsible'] = $recommendationData['responsable']; + } + + if (isset($recommendationData['duedate']['date'])) { + $recommendationData['duedate'] = $recommendationData['duedate']['date']; + } + + $recommendation = $this->anrRecommendationService->create($anr, $recommendationData, false); + + $this->recommendationTable->save($recommendation, false); + + $this->importCacheHelper->addItemToArrayCache('recommendations', $recommendation, $recommendation->getUuid()); + + return $recommendation; + } + + private function getRecommendationSetParams( + Entity\Anr $anr, + array $recommendationData, + array $recommendationsSetsData + ): array { + /** @var string|array $recommendationSetData Depending on the Monarc version, the structure is different. */ + $recommendationSetData = $recommendationData['recommandationSet'] ?? $recommendationData['recommendationSet']; + $recommendationSetLabel = $recommendationSetData['label'] ?? 'Imported'; + $recommendationSetUuid = (string)($recommendationSetData['uuid'] ?? $recommendationSetData); + if (isset($recommendationsSetsData[$recommendationSetUuid]) && $recommendationSetLabel === 'Imported') { + $recommendationSetLabel = $recommendationsSetsData[$recommendationSetUuid]['label' . $anr->getLanguage()]; + } + + return [$recommendationSetUuid, $recommendationSetLabel]; + } } diff --git a/src/Import/Processor/ReferentialImportProcessor.php b/src/Import/Processor/ReferentialImportProcessor.php index 99fde50a..5fb70b16 100644 --- a/src/Import/Processor/ReferentialImportProcessor.php +++ b/src/Import/Processor/ReferentialImportProcessor.php @@ -40,7 +40,7 @@ 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']); + $referential = $this->getReferentialFromCache($referentialData['uuid']); if ($referential === null) { /* In the new data structure there is only "label" field set. */ if (isset($referentialData['label'])) { @@ -58,6 +58,11 @@ public function processReferentialData(Entity\Anr $anr, array $referentialData): return $referential; } + public function getReferentialFromCache(string $referentialUuid): ?Entity\Referential + { + return $this->importCacheHelper->getItemFromArrayCache('referentials', $referentialUuid); + } + public function processMeasuresData( Entity\Anr $anr, Entity\Referential $referential, @@ -74,35 +79,6 @@ public function processMeasuresData( } } - public function processSoaCategoryData( - Entity\Anr $anr, - Entity\Referential $referential, - array $measureData - ): ?Entity\SoaCategory { - $soaCategory = null; - if (!empty($measureData['category'])) { - /* Support the previous structure format. */ - $soaCategoryLabel = $measureData['category']['label'] ?? $measureData['category']; - $soaCategory = $this->importCacheHelper->getItemFromArrayCache( - 'referential_' . $referential->getUuid() . '_soa_categories_by_labels', - $soaCategoryLabel - ); - if ($soaCategory === null) { - $soaCategory = $this->soaCategoryService->create($anr, [ - 'referential' => $referential, - 'label' . $anr->getLanguage() => $soaCategoryLabel, - ], false); - $this->importCacheHelper->addItemToArrayCache( - 'referential_' . $referential->getUuid() . '_soa_categories_by_labels', - $soaCategory, - $soaCategoryLabel - ); - } - } - - return $soaCategory; - } - public function getMeasureFromCache(string $measureUuid): ?Entity\Measure { return $this->importCacheHelper->getItemFromArrayCache('measures', $measureUuid); @@ -140,24 +116,62 @@ public function processMeasureData( 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 + public function processLinkedMeasures(Entity\Measure $measure, array $measureData): void { if (!empty($measureData['linkedMeasures'])) { foreach ($measureData['linkedMeasures'] as $linkedMeasureData) { - $linkedMeasure = $this->importCacheHelper->getItemFromArrayCache( - 'measures', - $linkedMeasureData['uuid'] - ); + $linkedMeasure = $this->getMeasureFromCache($linkedMeasureData['uuid']); if ($linkedMeasure !== null) { $measure->addLinkedMeasure($linkedMeasure); - $this->measureTable->save($linkedMeasure, false); + $this->measureTable->save($measure, false); } } } } + public function processSoaCategoryData( + Entity\Anr $anr, + Entity\Referential $referential, + array $measureData + ): ?Entity\SoaCategory { + $soaCategory = null; + if (!empty($measureData['category'])) { + /* Support the previous structure format. */ + $soaCategoryLabel = $measureData['category']['label'] ?? $measureData['category']; + $soaCategory = $this->importCacheHelper->getItemFromArrayCache( + 'soa_categories_by_referential_uuid_and_label', + $referential->getUuid() . '_' . $soaCategoryLabel + ); + if ($soaCategory === null) { + $soaCategory = $this->soaCategoryService->create($anr, [ + 'referential' => $referential, + 'label' . $anr->getLanguage() => $soaCategoryLabel, + ], false); + $this->importCacheHelper->addItemToArrayCache( + 'soa_categories_by_referential_uuid_and_label', + $soaCategory, + $referential->getUuid() . '_' . $soaCategoryLabel + ); + } + } + + return $soaCategory; + } + + public function prepareSoaCategoriesCache(Entity\Anr $anr): void + { + if (!$this->importCacheHelper->isCacheKeySet('soa_categories_by_referential_uuid_and_label')) { + /** @var Entity\SoaCategory $soaCategory */ + foreach ($this->soaCategoryTable->findByAnr($anr) as $soaCategory) { + $this->importCacheHelper->addItemToArrayCache( + 'soa_categories_by_referential_uuid_and_label', + $soaCategory, + $soaCategory->getReferential()->getUuid() . '_' . $soaCategory->getLabel($anr->getLanguage()) + ); + } + } + } + private function prepareReferentialsAndMeasuresCache(Entity\Anr $anr): void { if (!$this->importCacheHelper->isCacheKeySet('referentials')) { @@ -177,19 +191,4 @@ private function prepareReferentialsAndMeasuresCache(Entity\Anr $anr): void } } } - - private function prepareSoaCategoriesCache(Entity\Anr $anr): void - { - if (!$this->importCacheHelper->isCacheKeySet('soa_categories_cache')) { - $this->importCacheHelper->addItemToArrayCache('soa_categories_cache', true); - /** @var Entity\SoaCategory $soaCategory */ - foreach ($this->soaCategoryTable->findByAnr($anr) as $soaCategory) { - $this->importCacheHelper->addItemToArrayCache( - 'referential_' . $soaCategory->getReferential()->getUuid() . '_soa_categories_by_labels', - $soaCategory, - $soaCategory->getLabel($anr->getLanguage()) - ); - } - } - } } diff --git a/src/Import/Processor/ScaleImportProcessor.php b/src/Import/Processor/ScaleImportProcessor.php new file mode 100644 index 00000000..65e0b2d7 --- /dev/null +++ b/src/Import/Processor/ScaleImportProcessor.php @@ -0,0 +1,553 @@ +connectedUser = $connectedUserService->getConnectedUser(); + } + + public function applyNewScalesFromData(Entity\Anr $anr, array $newScalesData): void + { + $scalesDiff = []; + /** @var Entity\Scale $scale */ + foreach ($this->scaleTable->findByAnr($anr) as $scale) { + /* Update the scales impact types and comments. */ + if (!empty($newScalesData[ScaleSuperClass::TYPE_IMPACT]['scaleImpactTypes'])) { + $this->applyNewScaleImpactTypesAndComments($anr, $scale, $newScalesData['scaleImpactTypes']); + } + + if ($scale->isScaleRangeDifferentFromData($newScalesData)) { + if ($scale->getType() === ScaleSuperClass::TYPE_IMPACT) { + /* All the instance's risks and consequences values have to be updated. */ + $scalesDiff[ScaleSuperClass::TYPE_IMPACT] = [ + 'currentRange' => ['min' => $scale->getMin(), 'max', $scale->getMax()], + 'newRange' => [ + 'min' => $newScalesData[ScaleSuperClass::TYPE_IMPACT]['min'], + 'max' => $newScalesData[ScaleSuperClass::TYPE_IMPACT]['max'], + ], + ]; + } + + if ($scale->getType() === ScaleSuperClass::TYPE_THREAT) { + /* All the threats rates' values and qualification have to be updated. */ + $this->updateThreatsQualification($anr, $scale, $newScalesData[ScaleSuperClass::TYPE_THREAT]); + + $scalesDiff[ScaleSuperClass::TYPE_THREAT] = [ + 'currentRange' => ['min' => $scale->getMin(), 'max', $scale->getMax()], + 'newRange' => [ + 'min' => $newScalesData[ScaleSuperClass::TYPE_THREAT]['min'], + 'max' => $newScalesData[ScaleSuperClass::TYPE_THREAT]['max'], + ], + ]; + } + + if ($scale->getType() === ScaleSuperClass::TYPE_VULNERABILITY) { + /* All the vulnerabilities' risks values have to be updated. */ + $scalesDiff[ScaleSuperClass::TYPE_VULNERABILITY] = [ + 'currentRange' => ['min' => $scale->getMin(), 'max', $scale->getMax()], + 'newRange' => [ + 'min' => $newScalesData[ScaleSuperClass::TYPE_VULNERABILITY]['min'], + 'max' => $newScalesData[ScaleSuperClass::TYPE_VULNERABILITY]['max'], + ], + ]; + } + + $scale->setMin($newScalesData[$scale->getType()]['min']) + ->setMax($newScalesData[$scale->getType()]['max']) + ->setUpdater($this->connectedUser->getEmail()); + $this->scaleTable->save($scale, false); + + $this->importCacheHelper->addItemToArrayCache( + 'scales_ranges', + ['min' => $scale->getMin(), 'max' => $scale->getMax()], + $scale->getType() + ); + } + } + + /* If any type of the scale has difference in range than the risks and other values have to be updated. */ + $this->updateInstancesConsequencesThreatsVulnerabilitiesAndRisks($anr, $scalesDiff); + } + + public function updateInstancesConsequencesThreatsVulnerabilitiesAndRisks(Entity\Anr $anr, array $scalesDiff): void + { + if (empty($scalesDiff)) { + return; + } + + if (isset($scalesDiff[ScaleSuperClass::TYPE_IMPACT])) { + /** @var Entity\InstanceConsequence $consequence */ + foreach ($this->instanceConsequenceTable->findByAnr($anr) as $consequence) { + $consequence->setConfidentiality( + $consequence->isHidden() ? -1 : $this->convertValueWithinNewScalesRange( + $consequence->getConfidentiality(), + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['currentRange']['min'], + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['currentRange']['max'], + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['newRange']['min'], + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['newRange']['max'] + ) + ); + $consequence->setIntegrity( + $consequence->isHidden() ? -1 : $this->convertValueWithinNewScalesRange( + $consequence->getIntegrity(), + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['currentRange']['min'], + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['currentRange']['max'], + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['newRange']['min'], + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['newRange']['max'] + ) + ); + $consequence->setAvailability( + $consequence->isHidden() ? -1 : $this->convertValueWithinNewScalesRange( + $consequence->getAvailability(), + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['currentRange']['min'], + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['currentRange']['max'], + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['newRange']['min'], + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['newRange']['max'] + ) + ); + + $this->instanceConsequenceTable->save($consequence, false); + } + } + + /** @var Entity\Instance $instance */ + foreach ($this->instanceTable->findByAnr($anr) as $instance) { + if (isset($scalesDiff[ScaleSuperClass::TYPE_IMPACT])) { + if ($instance->getConfidentiality() !== -1) { + $instance->setConfidentiality($this->convertValueWithinNewScalesRange( + $instance->getConfidentiality(), + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['currentRange']['min'], + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['currentRange']['max'], + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['newRange']['min'], + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['newRange']['max'] + )); + } + if ($instance->getIntegrity() !== -1) { + $instance->setIntegrity($this->convertValueWithinNewScalesRange( + $instance->getIntegrity(), + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['currentRange']['min'], + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['currentRange']['max'], + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['newRange']['min'], + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['newRange']['max'] + )); + } + if ($instance->getAvailability() !== -1) { + $instance->setAvailability($this->convertValueWithinNewScalesRange( + $instance->getAvailability(), + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['currentRange']['min'], + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['currentRange']['max'], + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['newRange']['min'], + $scalesDiff[ScaleSuperClass::TYPE_IMPACT]['newRange']['max'] + )); + } + + $this->instanceTable->save($instance, false); + } + + if (isset($scalesDiff[ScaleSuperClass::TYPE_THREAT]) + || isset($scalesDiff[ScaleSuperClass::TYPE_VULNERABILITY]) + ) { + foreach ($instance->getInstanceRisks() as $instanceRisk) { + if (isset($scalesDiff[ScaleSuperClass::TYPE_THREAT])) { + $instanceRisk->setThreatRate($this->convertValueWithinNewScalesRange( + $instanceRisk->getThreatRate(), + $scalesDiff[ScaleSuperClass::TYPE_THREAT]['currentRange']['min'], + $scalesDiff[ScaleSuperClass::TYPE_THREAT]['currentRange']['max'], + $scalesDiff[ScaleSuperClass::TYPE_THREAT]['newRange']['min'], + $scalesDiff[ScaleSuperClass::TYPE_THREAT]['newRange']['max'] + )); + } + if (isset($scalesDiff[ScaleSuperClass::TYPE_VULNERABILITY])) { + $oldVulRate = $instanceRisk->getVulnerabilityRate(); + $instanceRisk->setVulnerabilityRate($this->convertValueWithinNewScalesRange( + $instanceRisk->getVulnerabilityRate(), + $scalesDiff[ScaleSuperClass::TYPE_VULNERABILITY]['currentRange']['min'], + $scalesDiff[ScaleSuperClass::TYPE_VULNERABILITY]['currentRange']['max'], + $scalesDiff[ScaleSuperClass::TYPE_VULNERABILITY]['newRange']['min'], + $scalesDiff[ScaleSuperClass::TYPE_VULNERABILITY]['newRange']['max'] + )); + $newVulRate = $instanceRisk->getVulnerabilityRate(); + if ($instanceRisk->getReductionAmount() !== 0) { + $instanceRisk->setReductionAmount($this->convertValueWithinNewScalesRange( + $instanceRisk->getReductionAmount(), + 0, + $oldVulRate, + 0, + $newVulRate, + 0 + )); + } + } + + $this->recalculateRiskRates($instanceRisk); + + $this->instanceRiskTable->save($instanceRisk, false); + } + } + } + } + + public function updateThreatsQualification( + Entity\Anr $anr, + Entity\Scale $currentScale, + array $newScaleImpactData + ): void { + /** @var Entity\Threat $threat */ + foreach ($this->threatTable->findByAnr($anr) as $threat) { + $threat->setQualification($this->convertValueWithinNewScalesRange( + $threat->getQualification(), + $currentScale->getMin(), + $currentScale->getMax(), + $newScaleImpactData['min'], + $newScaleImpactData['max'] + )); + $this->threatTable->save($threat, false); + } + } + + /** + * @return array [(int)scaleType => ['min' => int, max => int]], scaleType is one of Scale::getAvailableTypes. + */ + public function getScalesRangesFromCache(Entity\Anr $anr, int $type = null): array + { + if (!$this->importCacheHelper->isCacheKeySet('scales_ranges')) { + /** @var Entity\Scale $scale */ + foreach ($this->scaleTable->findByAnr($anr) as $scale) { + $this->importCacheHelper->addItemToArrayCache( + 'scales_ranges', + ['min' => $scale->getMin(), 'max' => $scale->getMax()], + $scale->getType() + ); + } + } + + return $this->importCacheHelper->getItemFromArrayCache('scales_ranges', $type); + } + + public function getScalesImpactTypesFromCacheByLabel(Entity\Anr $anr, string $typeLabel): ?Entity\ScaleImpactType + { + if (!$this->importCacheHelper->isCacheKeySet('scale_impact_types_by_label')) { + /** @var Entity\ScaleImpactType $scaleImpactType */ + foreach ($this->scaleImpactTypeTable->findByAnr($anr) as $scaleImpactType) { + $this->importCacheHelper->addItemToArrayCache( + 'scale_impact_types_by_label', + $scaleImpactType, + $scaleImpactType->getLabel($anr->getLanguage()) + ); + } + } + + return $this->importCacheHelper->getItemFromArrayCache('scale_impact_types_by_label', $typeLabel); + } + + private function applyNewScaleImpactTypesAndComments( + Entity\Anr $anr, + Entity\Scale $scale, + array $newScaleImpactTypesData + ): void { + $existingImpactTypesNumber = 0; + /* Process the existing scales impact types in order to update labels and visibility if necessary. */ + foreach ($scale->getScaleImpactTypes() as $scaleImpactType) { + $typeKey = array_search( + $scaleImpactType->getType(), + array_column($newScaleImpactTypesData, 'type'), + true + ); + if ($scaleImpactType->isSys()) { + if ($typeKey !== false && isset($newScaleImpactTypesData[$typeKey]['label']) && ( + $newScaleImpactTypesData[$typeKey]['label'] !== $scaleImpactType->getLabel($anr->getLanguage()) + || $newScaleImpactTypesData[$typeKey]['isHidden'] !== $scaleImpactType->isHidden() + )) { + /* The current system impact type visibility is changing to hidden, + so all the consequences have to be updated and the risks recalculated if needed. */ + if ($newScaleImpactTypesData[$typeKey]['isHidden'] && !$scaleImpactType->isHidden()) { + $this->validateConsequencesAndRisksForHidingImpactType($scaleImpactType); + } + + $scaleImpactType->setLabels( + ['label' . $anr->getLanguage() => $newScaleImpactTypesData[$typeKey]['label']] + )->setIsHidden($newScaleImpactTypesData[$typeKey]['isHidden']) + ->setUpdater($this->connectedUser->getEmail()); + $this->scaleImpactTypeTable->save($scaleImpactType, false); + } + } + + $this->importCacheHelper->addItemToArrayCache( + 'scale_impact_types_by_label', + $scaleImpactType, + $scaleImpactType->getLabel($anr->getLanguage()) + ); + $existingImpactTypesNumber++; + + if ($typeKey !== false && !empty($newScaleImpactTypesData[$typeKey]['scaleComments'])) { + $this->applyNewScalesImpactsTypesComments( + $anr, + $scaleImpactType, + $newScaleImpactTypesData[$typeKey]['scaleComments'] + ); + } + } + + /* Process the importing scale impact types data. */ + foreach ($newScaleImpactTypesData as $newScaleImpactTypeData) { + if (!$newScaleImpactTypeData['isSys'] && !empty($newScaleImpactTypeData['label'])) { + /** @var Entity\ScaleImpactType $existingCustomImpactType */ + $existingCustomImpactType = $this->importCacheHelper->getItemFromArrayCache( + 'scale_impact_types_by_label', + $newScaleImpactTypeData['label'] + ); + if ($existingCustomImpactType !== null) { + if ($existingCustomImpactType->isHidden() !== $newScaleImpactTypeData['isHidden']) { + if ($newScaleImpactTypesData['isHidden'] && !$existingCustomImpactType->isHidden()) { + $this->validateConsequencesAndRisksForHidingImpactType($existingCustomImpactType); + } + $existingCustomImpactType->setIsHidden($newScaleImpactTypeData['isHidden']) + ->setUpdater($this->connectedUser->getEmail()); + $this->scaleImpactTypeTable->save($existingCustomImpactType, false); + } + } else { + $newScaleImpactType = $this->anrScaleImpactTypeService->create($anr, array_merge( + $newScaleImpactTypeData, + ['scale' => $scale, 'type' => ++$existingImpactTypesNumber] + ), false); + + $this->importCacheHelper->addItemToArrayCache( + 'scale_impact_types_by_label', + $newScaleImpactType, + $newScaleImpactTypeData['label'] + ); + + $this->applyNewScalesImpactsTypesComments( + $anr, + $newScaleImpactType, + $newScaleImpactTypeData['scaleComments'] + ); + } + } + } + } + + private function applyNewScalesImpactsTypesComments( + Entity\Anr $anr, + Entity\ScaleImpactType $scaleImpactType, + array $newScaleCommentsData + ): void { + $langIndex = $anr->getLanguage(); + foreach ($scaleImpactType->getScaleComments() as $existingScaleComment) { + $commentKey = array_search( + $existingScaleComment->getScaleValue(), + array_column($newScaleCommentsData, 'scaleValue'), + true + ); + if ($commentKey !== false) { + if (!empty($newScaleCommentsData[$commentKey]['comment']) + && $existingScaleComment->getComment($langIndex) !== $newScaleCommentsData[$commentKey]['comment'] + ) { + $existingScaleComment->setComments( + ['comment' . $langIndex => $newScaleCommentsData[$commentKey]['comment']] + )->setUpdater($this->connectedUser->getEmail()); + $this->scaleCommentTable->save($existingScaleComment, false); + } + unset($newScaleCommentsData[$commentKey]); + } + } + + foreach ($newScaleCommentsData as $newScaleCommentData) { + $newScaleCommentData['comment' . $langIndex] = $newScaleCommentData['comment']; + $newScaleCommentData['scale'] = $scaleImpactType->getScale(); + $newScaleCommentData['scaleImpactType'] = $scaleImpactType; + $this->anrScaleCommentService->create($anr, $newScaleCommentData, false); + } + } + + private function validateConsequencesAndRisksForHidingImpactType(Entity\ScaleImpactType $hidingImpactType): void + { + foreach ($hidingImpactType->getInstanceConsequences() as $consequence) { + $confidentialityOfHiding = $consequence->getConfidentiality(); + $integrityOfHiding = $consequence->getIntegrity(); + $availabilityOfHiding = $consequence->getAvailability(); + $consequence->setIsHidden(true) + ->setConfidentiality(-1) + ->setIntegrity(-1) + ->setAvailability(-1) + ->setUpdater($this->connectedUser->getEmail()); + $this->instanceConsequenceTable->save($consequence, false); + + if (max($confidentialityOfHiding, $integrityOfHiding, $availabilityOfHiding) !== -1) { + $maxConfidentiality = -1; + $maxIntegrity = -1; + $maxAvailability = -1; + $validatingInstance = $consequence->getInstance(); + foreach ($validatingInstance->getInstanceConsequences() as $consequenceToValidate) { + if ($consequenceToValidate->getId() !== $consequence->getId()) { + $maxConfidentiality = $consequenceToValidate->getConfidentiality() > $maxConfidentiality + ? $consequenceToValidate->getConfidentiality() + : $maxConfidentiality; + $maxIntegrity = $consequenceToValidate->getIntegrity() > $maxIntegrity + ? $consequenceToValidate->getIntegrity() + : $maxIntegrity; + $maxAvailability = $consequenceToValidate->getAvailability() > $maxAvailability + ? $consequenceToValidate->getAvailability() + : $maxAvailability; + } + } + if ($confidentialityOfHiding > $maxConfidentiality + || $integrityOfHiding > $maxIntegrity + || $availabilityOfHiding > $maxAvailability + ) { + /* Recalculate the instances and instance risks values. */ + $validatingInstance->setConfidentiality($maxConfidentiality) + ->setIntegrity($maxIntegrity) + ->setAvailability($maxAvailability) + ->setUpdater($this->connectedUser->getEmail()); + $this->instanceTable->save($validatingInstance, false); + foreach ($validatingInstance->getInstanceRisks() as $validatingInstanceRisk) { + $this->recalculateRiskRates($validatingInstanceRisk); + $validatingInstanceRisk->setUpdater($this->connectedUser->getEmail()); + $this->instanceRiskTable->save($validatingInstanceRisk, false); + } + } + } + } + } + + /* Is used for the imports of data exported prior v2.13.1 */ + public function updateScalesAndComments(Entity\Anr $anr, array $data): void + { + $scalesByType = []; + $scalesRangesByType = []; + /** @var Entity\Scale $scale */ + foreach ($this->scaleTable->findByAnr($anr) as $scale) { + if (isset($data['scales'][$scale->getType()])) { + $scale->setMin((int)$data['scales'][$scale->getType()]['min']); + $scale->setMax((int)$data['scales'][$scale->getType()]['max']); + + $scalesByType[$scale->getType()] = $scale; + $scalesRangesByType[$scale->getType()] = [ + 'min' => $scale->getMin(), + 'max' => $scale->getMax(), + ]; + + $this->scaleTable->save($scale, false); + } + } + $this->importCacheHelper->addItemToArrayCache('scales_for_old_structure', $scalesRangesByType, 'current'); + + if (!empty($data['scalesComments'])) { + /** @var Entity\ScaleComment $scaleComment */ + foreach ($this->scaleCommentTable->findByAnr($anr) as $scaleComment) { + if ($scaleComment->getScaleImpactType() === null || $scaleComment->getScaleImpactType()->isSys()) { + $this->scaleCommentTable->remove($scaleComment, false); + } + } + $this->scaleCommentTable->flush(); + + $scaleImpactTypeData = []; + /** @var Entity\ScaleImpactType $scaleImpactType */ + foreach ($this->scaleImpactTypeTable->findByAnr($anr) as $index => $scaleImpactType) { + $scaleImpactTypeData[$index + 1] = $scaleImpactType; + } + + foreach ($data['scalesComments'] as $scalesCommentData) { + /* + * Comments, which are not matched with a scale impact type, should not be created. + * This is possible only for exported files before v2.11.0. + */ + if (isset($scalesCommentData['scaleImpactType']) + && !isset($scalesCommentData['scaleImpactType']['labels']) + && !isset( + $scalesCommentData['scaleImpactType']['position'], + $scaleImpactTypeData[$scalesCommentData['scaleImpactType']['position']] + ) + ) { + continue; + } + + $scale = $scalesByType[$scalesCommentData['scale']['type']]; + $scaleComment = (new Entity\ScaleComment()) + ->setAnr($anr) + ->setScale($scale) + ->setScaleIndex($scalesCommentData['scaleIndex'] ?? $scalesCommentData['val']) + ->setScaleValue($scalesCommentData['scaleValue'] ?? $scalesCommentData['val']) + ->setComments([ + 'comment1' => $scalesCommentData['comment1'], + 'comment2' => $scalesCommentData['comment2'], + 'comment3' => $scalesCommentData['comment3'], + 'comment4' => $scalesCommentData['comment4'], + ]) + ->setCreator($this->connectedUser->getEmail()); + + if (isset($scalesCommentData['scaleImpactType']['position'])) { + $scaleImpactTypePos = $scalesCommentData['scaleImpactType']['position']; + $scaleImpactType = $scaleImpactTypeData[$scaleImpactTypePos] ?? null; + $isSystem = $scaleImpactType !== null && $scaleImpactType->isSys(); + /* Scale impact types are presented in the export separately since v2.11.0 */ + if (isset($scalesCommentData['scaleImpactType']['labels']) + && !$isSystem + && ($scaleImpactType === null || $scaleImpactType->getLabel( + $anr->getLanguage() + ) !== $scalesCommentData['scaleImpactType']['labels']['label' . $anr->getLanguage()] + ) + ) { + $scaleImpactType = (new Entity\ScaleImpactType()) + ->setType($scalesCommentData['scaleImpactType']['type']) + ->setLabels($scalesCommentData['scaleImpactType']['labels']) + ->setIsSys($scalesCommentData['scaleImpactType']['isSys']) + ->setIsHidden($scalesCommentData['scaleImpactType']['isHidden']) + ->setAnr($anr) + ->setScale($scale) + ->setCreator($this->connectedUser->getEmail()); + + $this->scaleImpactTypeTable->save($scaleImpactType, false); + + $scaleImpactTypesData[$scaleImpactTypePos] = $scaleImpactType; + } + if ($scaleImpactType === null) { + continue; + } + + /* We may overwrite the comments if position is matched but scale type labels are different */ + $scaleComment->setScaleImpactType($scaleImpactType); + } + + $this->scaleCommentTable->save($scaleComment, false); + } + $this->scaleCommentTable->flush(); + } + } +} diff --git a/src/Import/Processor/SoaScaleCommentImportProcessor.php b/src/Import/Processor/SoaScaleCommentImportProcessor.php new file mode 100644 index 00000000..e6170fda --- /dev/null +++ b/src/Import/Processor/SoaScaleCommentImportProcessor.php @@ -0,0 +1,100 @@ +importCacheHelper->isCacheKeySet('soa_scale_comments_data')) { + foreach ($this->soaScaleCommentTable->findByAnrOrderByIndex($anr, true) as $soaScaleComment) { + $this->importCacheHelper->addItemToArrayCache('soa_scale_comments_data', [ + 'scaleIndex' => $soaScaleComment->getScaleIndex(), + 'isHidden' => $soaScaleComment->isHidden(), + 'colour' => $soaScaleComment->getColour(), + 'object' => $soaScaleComment, + ], $soaScaleComment->getScaleIndex()); + } + } + + return $this->importCacheHelper->getItemFromArrayCache('soa_scale_comments_data'); + } + + public function mergeSoaScaleComment(Entity\Anr $anr, array $newScales) + { + $soaScaleCommentTranslations = $this->translationTable->findByAnrTypesAndLanguageIndexedByKey( + $anr, + [CoreEntity\TranslationSuperClass::SOA_SCALE_COMMENT], + $anr->getLanguageCode() + ); + $scales = $this->soaScaleCommentTable->findByAnrIndexedByScaleIndex($anr); + // we have scales to create + if (\count($newScales) > \count($scales)) { + $anrLanguageCode = $anr->getLanguageCode(); + for ($i = \count($scales); $i < \count($newScales); $i++) { + // todo: no translations anymore ! +// $translationKey = (string)Uuid::uuid4(); +// $translation = (new Translation()) +// ->setAnr($anr) +// ->setType(TranslationSuperClass::SOA_SCALE_COMMENT) +// ->setKey($translationKey) +// ->setValue('') +// ->setLang($anrLanguageCode) +// ->setCreator($this->connectedUser->getEmail()); +// $this->translationTable->save($translation, false); +// $soaScaleCommentTranslations[$translationKey] = $translation; + + $scales[$i] = (new Entity\SoaScaleComment()) + ->setScaleIndex($i) + ->setAnr($anr) + ->setCommentTranslationKey($translationKey) + ->setCreator($this->connectedUser->getEmail()); + $this->soaScaleCommentTable->save($scales[$i], false); + } + } + //we have scales to hide + if (\count($newScales) < \count($scales)) { + for ($i = \count($newScales); $i < \count($scales); $i++) { + $scales[$i]->setIsHidden(true); + $this->soaScaleCommentTable->save($scales[$i], false); + } + } + //we process the scales + foreach ($newScales as $id => $newScale) { + $scales[$newScale['scaleIndex']] + ->setColour($newScale['colour']) + ->setIsHidden($newScale['isHidden']); + $this->soaScaleCommentTable->save($scales[$newScale['scaleIndex']], false); + + $translationKey = $scales[$newScale['scaleIndex']]->getCommentTranslationKey(); + $translation = $soaScaleCommentTranslations[$translationKey]; + $translation->setValue($newScale['comment']); + + $this->translationTable->save($translation, false); + + $this->importCacheHelper->addItemToArrayCache( + 'newSoaScaleCommentIndexedByScale', + $scales[$newScale['scaleIndex']], + $newScale['scaleIndex'] + ); + $this->importCacheHelper + ->addItemToArrayCache('soaScaleCommentExternalIdMapToNewObject', $scales[$newScale['scaleIndex']], $id); + } + $this->soaScaleCommentTable->flush(); + } +} diff --git a/src/Import/Processor/ThreatImportProcessor.php b/src/Import/Processor/ThreatImportProcessor.php index 543092f1..fffde928 100644 --- a/src/Import/Processor/ThreatImportProcessor.php +++ b/src/Import/Processor/ThreatImportProcessor.php @@ -25,7 +25,7 @@ public function __construct( ) { } - public function processThreatsData(Entity\Anr $anr, array $threatsData, array $themesData): void + public function processThreatsData(Entity\Anr $anr, array $threatsData, array $themesData = []): void { $this->prepareThreatsAndCodesCache($anr); $this->prepareThemesCache($anr); diff --git a/src/Import/Processor/VulnerabilityImportProcessor.php b/src/Import/Processor/VulnerabilityImportProcessor.php index e3810049..30ee30df 100644 --- a/src/Import/Processor/VulnerabilityImportProcessor.php +++ b/src/Import/Processor/VulnerabilityImportProcessor.php @@ -7,7 +7,6 @@ namespace Monarc\FrontOffice\Import\Processor; -use Monarc\Core\Entity\Anr; use Monarc\FrontOffice\Entity; use Monarc\FrontOffice\Import\Helper\ImportCacheHelper; use Monarc\FrontOffice\Service\AnrVulnerabilityService; diff --git a/src/Import/Service/AssetImportService.php b/src/Import/Service/AssetImportService.php index ded4f816..0ecd63e4 100644 --- a/src/Import/Service/AssetImportService.php +++ b/src/Import/Service/AssetImportService.php @@ -19,7 +19,6 @@ use Monarc\FrontOffice\Entity\Referential; use Monarc\FrontOffice\Import\Processor; use Monarc\FrontOffice\Table; -use Monarc\FrontOffice\Service\SoaCategoryService; class AssetImportService { @@ -29,12 +28,12 @@ public function __construct( private Processor\AssetImportProcessor $assetImportProcessor, private Processor\ThreatImportProcessor $threatImportProcessor, private Processor\VulnerabilityImportProcessor $vulnerabilityImportProcessor, + private Processor\ReferentialImportProcessor $referentialImportProcessor, private Table\MeasureTable $measureTable, private Table\AmvTable $amvTable, private Table\InstanceRiskTable $instanceRiskTable, private Table\ReferentialTable $referentialTable, private ImportCacheHelper $importCacheHelper, - private SoaCategoryService $soaCategoryService, ConnectedUserService $connectedUserService ) { $this->connectedUser = $connectedUserService->getConnectedUser(); @@ -143,10 +142,12 @@ private function processInformationRisksData(array $amvsData, Anr $anr, Asset $a } } + // TODO: use the ReferentialImportProcessor. private function processMeasuresAndReferentialData(array $measuresData, Anr $anr, Amv $amv): void { $languageIndex = $anr->getLanguage(); $labelKey = 'label' . $languageIndex; + $this->referentialImportProcessor->prepareSoaCategoriesCache($anr); foreach ($measuresData as $measureUuid) { $measure = $this->importCacheHelper->getItemFromArrayCache('measures', $measureUuid) ?: $this->measureTable->findByUuidAndAnr($measureUuid, $anr); @@ -178,12 +179,16 @@ private function processMeasuresAndReferentialData(array $measuresData, Anr $anr continue; } - $soaCategory = $this->soaCategoryService->getOrCreateSoaCategory( - $this->importCacheHelper, - $anr, - $referential, - $data['measures'][$measureUuid]['category'][$labelKey] ?? '' - ); + $soaCategory = null; + if (!empty($data['measures'][$measureUuid]['category'][$labelKey])) { + $soaCategory = $this->referentialImportProcessor->processSoaCategoryData( + $anr, + $referential, + [ + 'category' => ['label' => $data['measures'][$measureUuid]['category'][$labelKey]], + ] + ); + } $measure = (new Measure()) ->setAnr($anr) diff --git a/src/Import/Service/InstanceImportService.php b/src/Import/Service/InstanceImportService.php index 4ca1cce8..e7fe193e 100755 --- a/src/Import/Service/InstanceImportService.php +++ b/src/Import/Service/InstanceImportService.php @@ -14,17 +14,20 @@ use Monarc\Core\Service\ConfigService; use Monarc\Core\Service\ConnectedUserService; use Monarc\Core\Helper\EncryptDecryptHelperTrait; +use Monarc\Core\Service\Traits\RiskCalculationTrait; use Monarc\FrontOffice\Import\Helper\ImportCacheHelper; use Monarc\FrontOffice\Entity; use Monarc\FrontOffice\Import\Processor; +use Monarc\FrontOffice\Import\Traits\EvaluationConverterTrait; use Monarc\FrontOffice\Table; use Monarc\FrontOffice\Model\Table as DeprecatedTable; use Monarc\FrontOffice\Service; -use Ramsey\Uuid\Uuid; class InstanceImportService { use EncryptDecryptHelperTrait; + use EvaluationConverterTrait; + use RiskCalculationTrait; private string $monarcVersion; @@ -48,6 +51,13 @@ public function __construct( private Processor\OperationalRisksImportProcessor $operationalRisksImportProcessor, private Processor\RecommendationImportProcessor $recommendationImportProcessor, private Processor\ObjectCategoryImportProcessor $objectCategoryImportProcessor, + private Processor\AnrInstanceMetadataFieldImportProcessor $anrInstanceMetadataFieldImportProcessor, + private Processor\AnrMethodStepImportProcessor $anrMethodStepImportProcessor, + private Processor\InstanceImportProcessor $instanceImportProcessor, + private Processor\ScaleImportProcessor $scaleImportProcessor, + private Processor\OperationalRiskScaleImportProcessor $operationalRiskScaleImportProcessor, + private Processor\OperationalInstanceRiskImportProcessor $operationalInstanceRiskImportProcessor, + private Processor\SoaScaleCommentImportProcessor $soaScaleCommentImportProcessor, private Service\AnrInstanceRiskService $anrInstanceRiskService, private Service\InstanceRiskOwnerService $instanceRiskOwnerService, private Service\AnrInstanceService $anrInstanceService, @@ -69,7 +79,7 @@ public function __construct( private Table\RecommendationRiskTable $recommendationRiskTable, private DeprecatedTable\QuestionTable $questionTable, private DeprecatedTable\QuestionChoiceTable $questionChoiceTable, - private DeprecatedTable\SoaTable $soaTable, + private Table\SoaTable $soaTable, private Table\MeasureTable $measureTable, private Table\ThemeTable $themeTable, private Table\ReferentialTable $referentialTable, @@ -88,6 +98,7 @@ public function __construct( private ImportCacheHelper $importCacheHelper, private Service\AnrThemeService $anrThemeService, private Service\SoaCategoryService $soaCategoryService, + private Service\AnrRecommendationRiskService $anrRecommendationRiskService, ConnectedUserService $connectedUserService ) { $this->connectedUser = $connectedUserService->getConnectedUser(); @@ -183,10 +194,12 @@ private function importFromArray( $result = $this->processInstanceImport($anr, $data, $parentInstance, $importMode); } + // TODO: remove the temporary test case + $this->anrTable->flush(); + return $result; } - $this->currentAnalyseMaxRecommendationPosition = $this->recommendationTable->findMaxPosition(['anr' => $anr]); $this->currentMaxInstancePosition = $this->instanceTable->findMaxPosition( ['anr' => $anr, 'parent' => $parentInstance] ); @@ -211,15 +224,50 @@ private function processAnrImport( string $importMode ): array { $result = []; + if ($data['withMethodSteps']) { + /* Process all the method's steps. */ + $this->anrMethodStepImportProcessor->processAnrMethodStepsData($anr, $data['method']); + } + if ($data['withEval']) { + /* Process all the analysis' thresholds. */ + $this->anrMethodStepImportProcessor->processThresholdsData($anr, $data['thresholds']); + + /* Apply the importing information risks scales. */ + $this->scaleImportProcessor->applyNewScalesFromData($anr, $data['scales']); + + /* Apply the importing operational risks scales. */ + $this->operationalRiskScaleImportProcessor->adjustOperationalRisksScaleValuesBasedOnNewScales($anr, $data); + + } + if ($data['withInterviews']) { + /* Process the interviews' data. */ + $this->anrMethodStepImportProcessor->processInterviewsData($anr, $data['interviews']); + } if ($data['withKnowledgeBase']) { + /* Process the Knowledge Base data. */ $this->processKnowledgeBaseData($anr, $data['knowledgeBase']); } if ($data['withLibrary']) { + /* Process the Assets Library data. */ $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. + /* Process the analysis metadata fields data. */ + $this->anrInstanceMetadataFieldImportProcessor->processAnrInstanceMetadataFields( + $anr, + $data['anrInstanceMetadataFields'] + ); + + /* Process the Instances, Instance Risks, Consequences and evaluations data. */ + $this->instanceImportProcessor + ->processInstancesData($anr, $data['instances'], $parentInstance, $importMode, $data['withEval']); + + // TODO: check 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. + // TODO: If we update the scales later, then the post update or right during the process have to be done. + + // TODO: soaScaleComments & SOAs + + // TODO: process RoPA. return $result; } @@ -227,7 +275,7 @@ private function processAnrImport( private function processKnowledgeBaseData(Entity\Anr $anr, array $knowledgeBaseData): void { $this->assetImportProcessor->processAssetsData($anr, $knowledgeBaseData['assets']); - $this->threatImportProcessor->processThreatsData($anr, $knowledgeBaseData['threats'], []); + $this->threatImportProcessor->processThreatsData($anr, $knowledgeBaseData['threats']); $this->vulnerabilityImportProcessor->processVulnerabilitiesData($anr, $knowledgeBaseData['vulnerabilities']); $this->referentialImportProcessor->processReferentialsData($anr, $knowledgeBaseData['referentials']); $this->informationRiskImportProcessor->processInformationRisksData( @@ -291,14 +339,13 @@ private function importInstanceFromArray( return false; } - // TODO: move it to a processor and use service. $instance = $this->createInstance($data, $anr, $parentInstance, $monarcObject); // TODO: The instance risks are processed later again and considered that here we save or not... - // 1. why do we need to do it twice processInstanceRisks and what is is going on inside that method... + // 1. why do we need to do it twice processInstanceRisks and what is going on inside that method... $this->anrInstanceRiskService->createInstanceRisks($instance, $monarcObject, $data, false); - $this->createInstanceMetadata($instance, $data); + $this->instanceImportProcessor->processInstanceMetadata($anr, $instance, $data['instancesMetadatas']); $includeEval = !empty($data['with_eval']); @@ -312,7 +359,14 @@ private function importInstanceFromArray( $this->processInstanceRisks($data, $anr, $instance, $monarcObject, $includeEval, $importMode); - $this->processOperationalInstanceRisks($data, $anr, $instance, $monarcObject, $includeEval); + $this->operationalInstanceRiskImportProcessor->processOperationalInstanceRisks( + $data, + $anr, + $instance, + $monarcObject, + $includeEval, + $this->isImportTypeAnr() + ); if (!empty($data['children'])) { usort($data['children'], function ($a, $b) { @@ -336,372 +390,78 @@ private function importInstanceFromArray( return $instance->getId(); } - /** - * TODO: refactor the method entirely. - */ private function importAnrFromArray( array $data, Entity\Anr $anr, ?CoreEntity\InstanceSuperClass $parentInstance, string $modeImport ): array { - $labelKey = 'label' . $anr->getLanguage(); - - // Method information - if (!empty($data['method'])) { //Steps checkboxes - if (!empty($data['method']['steps'])) { - foreach ($data['method']['steps'] as $key => $v) { - if ($anr->get($key) === 0) { - $anr->set($key, $v); - $this->anrTable->save($anr, false); - } - } - $this->anrTable->flush(); - } - - if (!empty($data['method']['data'])) { //Data of textboxes - foreach ($data['method']['data'] as $key => $v) { - if ($anr->get($key) === null) { - $anr->set($key, $v); - $this->anrTable->save($anr, false); - } - } - $this->anrTable->flush(); - } - - if (!empty($data['method']['interviews'])) { //Data of interviews - foreach ($data['method']['interviews'] as $key => $v) { - $toExchange = $data['method']['interviews'][$key]; - $toExchange['anr'] = $anr->getId(); - $newInterview = new Entity\Interview(); - $newInterview->setLanguage($anr->getLanguage()); - $newInterview->exchangeArray($toExchange); - $newInterview->setAnr($anr); - $this->interviewTable->saveEntity($newInterview, false); - } - $this->interviewTable->getDb()->flush(); - } - - if (!empty($data['method']['thresholds'])) { // Value of thresholds - foreach ($data['method']['thresholds'] as $key => $v) { - $anr->set($key, $v); - $this->anrTable->save($anr, false); - } - $this->anrTable->flush(); - } - - if (!empty($data['method']['deliveries'])) { // Data of deliveries generation - foreach ($data['method']['deliveries'] as $key => $v) { - $toExchange = $data['method']['deliveries'][$key]; - $toExchange['anr'] = $anr->getId(); - $newDelivery = new Entity\Delivery(); - $newDelivery->setLanguage($anr->getLanguage()); - $newDelivery->exchangeArray($toExchange); - $newDelivery->setAnr($anr); - $this->deliveryTable->save($newDelivery, false); - } - $this->deliveryTable->getDb()->flush(); - } - - if (!empty($data['method']['questions'])) { // Questions of trends evaluation - $questions = $this->questionTable->findByAnr($anr); - foreach ($questions as $question) { - $this->questionTable->deleteEntity($question); - } - - foreach ($data['method']['questions'] as $position => $questionData) { - $newQuestion = new Entity\Question(); - $newQuestion->setLanguage($anr->getLanguage()); - $newQuestion->exchangeArray($questionData); - $newQuestion->setAnr($anr); - // TODO: use setter. - $newQuestion->set('position', $position); - $this->questionTable->saveEntity($newQuestion, false); - - if ((int)$questionData['multichoice'] === 1) { - foreach ($data['method']['questionChoice'] as $questionChoiceData) { - if ($questionChoiceData['question'] === $questionData['id']) { - $newQuestionChoice = new Entity\QuestionChoice(); - $newQuestionChoice->setLanguage($anr->getLanguage()); - $newQuestionChoice->exchangeArray($questionChoiceData); - $newQuestionChoice->setAnr($anr) - ->setQuestion($newQuestion); - $this->questionChoiceTable->save($newQuestionChoice, false); - } - } - } - } - - $this->questionTable->getDb()->flush(); - - /** @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 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()]); - - foreach ($data['method']['questions'] as $questionAnswerData) { - foreach ($questions as $question) { - if ($question->get($labelKey) === $questionAnswerData[$labelKey]) { - if ($question->isMultiChoice()) { - $originQuestionChoices = []; - $response = $questionAnswerData['response'] ?? ''; - if (trim($response, '[]')) { - $originQuestionChoices = explode(',', trim($response, '[]')); - } - $questionChoicesIds = []; - foreach ($originQuestionChoices as $originQuestionChoice) { - $chosenQuestionLabel = - $data['method']['questionChoice'][$originQuestionChoice][$labelKey] ?? ''; - foreach ($questionChoices as $questionChoice) { - if ($questionChoice->get($labelKey) === $chosenQuestionLabel) { - $questionChoicesIds[] = $questionChoice->getId(); - } - } - } - $question->response = '[' . implode(',', $questionChoicesIds) . ']'; - } else { - $question->response = $questionAnswerData['response']; - } - $this->questionTable->saveEntity($question, false); - } - } - } - - $this->questionTable->getDb()->flush(); - } - - /* Process the evaluation of threats. */ - if (!empty($data['method']['threats'])) { - $this->threatImportProcessor->prepareThemesCache($anr); - foreach ($data['method']['threats'] as $threatUuid => $threatData) { - /** @var ?Entity\Threat $threat */ - $threat = $this->importCacheHelper->getItemFromArrayCache('threats', $threatUuid) - ?: $this->threatTable->findByUuidAndAnr($threatUuid, $anr, false); - if ($threat === null) { - $threatData = $data['method']['threats'][$threatUuid]; - // TODO: inject and use the threatService->create - // TODO: implement the same way as in AssetImpSrv - /* The code should be unique. */ - $threatData['code'] = $this->importCacheHelper - ->getItemFromArrayCache('threats_codes', $threatData['code']) !== null - || $this->threatTable->doesCodeAlreadyExist($threatData['code'], $anr) - ? $threatData['code'] . '-' . time() - : $threatData['code']; - - $threat = (new Entity\Threat()) - ->setUuid($threatData['uuid']) - ->setAnr($anr) - ->setCode($threatData['code']) - ->setLabels($threatData) - ->setDescriptions($threatData); - if (isset($threatData['c'])) { - $threat->setConfidentiality((int)$threatData['c']); - } - if (isset($threatData['i'])) { - $threat->setIntegrity((int)$threatData['i']); - } - if (isset($threatData['a'])) { - $threat->setAvailability((int)$threatData['a']); - } - - if (!empty($data['method']['threats'][$threatUuid]['theme'])) { - $themeData = $data['method']['threats'][$threatUuid]['theme']; - $labelValue = $themeData[$labelKey]; - $theme = $this->importCacheHelper->getItemFromArrayCache('themes_by_labels', $labelValue); - if ($theme === null) { - $theme = (new Entity\Theme()) - ->setAnr($anr) - ->setLabels($themeData) - ->setCreator($this->connectedUser->getEmail()); - - $this->themeTable->save($theme, false); - $this->importCacheHelper->addItemToArrayCache('themes_by_labels', $theme, $labelValue); - } - - $threat->setTheme($theme); - } - } - - $threat->setTrend((int)$data['method']['threats'][$threatUuid]['trend']); - $threat->setComment((string)$data['method']['threats'][$threatUuid]['comment']); - $threat->setQualification((int)$data['method']['threats'][$threatUuid]['qualification']); - - $this->threatTable->save($threat, false); - - $this->importCacheHelper->addItemToArrayCache('threats', $threat, $threat->getUuid()); - $this->importCacheHelper - ->addItemToArrayCache('threats_codes', $threat->getCode(), $threat->getCode()); - } - } + if (!empty($data['method'])) { + $this->anrMethodStepImportProcessor->processAnrMethodStepsData($anr, $data['method']); } - /* Import the referentials. */ if (!empty($data['referentials'])) { - foreach ($data['referentials'] as $referentialUuid => $referentialData) { - $referential = $this->importCacheHelper->getItemFromArrayCache('referentials', $referentialUuid) - ?: $this->referentialTable->findByUuidAndAnr($referentialUuid, $anr); - if ($referential === null) { - // 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->save($referential, false); - } - - $this->importCacheHelper->addItemToArrayCache('referentials', $referential, $referentialUuid); - } + $this->referentialImportProcessor->processReferentialsData($anr, $data['referentials']); } /* - * Import the soa categories. + * Import measures and soa categories. */ - if (!empty($data['soacategories'])) { - foreach ($data['soacategories'] as $soaCategoryData) { - $referential = $this->importCacheHelper - ->getItemFromArrayCache('referentials', $soaCategoryData['referential']); - if ($referential !== null) { - $this->soaCategoryService->getOrCreateSoaCategory( - $this->importCacheHelper, - $anr, - $referential, - $soaCategoryData[$labelKey] - ); + foreach ($data['measures'] ?? [] as $measureData) { + $referential = $this->referentialImportProcessor->getReferentialFromCache($measureData['referential']); + if ($referential !== null) { + $measure = $this->referentialImportProcessor->processMeasureData($anr, $referential, $measureData); + if (!isset($data['soas'])) { + $this->soaTable->save((new Entity\Soa())->setAnr($anr)->setMeasure($measure), false); } } } - /* - * Import the measures. - */ - // TODO: use the ReferentialImportProcessor. - if (isset($data['measures'])) { - foreach ($data['measures'] ?? [] as $measureUuid => $measureData) { - $measure = $this->importCacheHelper->getItemFromArrayCache('measures', $measureUuid) - ?: $this->measureTable->findByUuidAndAnr($measureUuid, $anr); - $referential = $this->importCacheHelper - ->getItemFromArrayCache('referentials', $measureData['referential']); - if ($measure === null && $referential !== null) { - $soaCategory = $this->soaCategoryService->getOrCreateSoaCategory( - $this->importCacheHelper, - $anr, - $referential, - $measureData['category'] - ); - - $measure = (new Entity\Measure()) - ->setUuid($measureUuid) - ->setAnr($anr) - ->setLabels($measureData) - ->setCode($measureData['code']) - ->setStatus((int)$measureData['status']) - ->setReferential($referential) - ->setCategory($soaCategory) - ->setCreator($this->connectedUser->getEmail()); - $this->measureTable->save($measure, false); - - $this->importCacheHelper->addItemToArrayCache('measures', $measure, $measureUuid); - - if (!isset($data['soas'])) { - $newSoa = (new Entity\Soa()) - ->setAnr($anr) - ->setMeasure($measure); - $this->soaTable->saveEntity($newSoa, false); - } - } + foreach ($data['measuresMeasures'] ?? [] as $measureMeasureData) { + $measure = $this->referentialImportProcessor->getMeasureFromCache($measureMeasureData['father']); + if ($measure !== null) { + $this->referentialImportProcessor->processLinkedMeasures( + $measure, + ['linkedMeasures' => [['uuid' => $measureMeasureData['child']]]] + ); } - - $this->measureTable->flush(); } - $this->importMeasuresLinks($anr, $data); - - // import soaScaleComment - $maxOrig = null; //used below for soas - $maxDest = null; //used below for soas + /* Import SOA Scale Comments if they are passed. Only in the new structure, when the functionality exists. */ if (isset($data['soaScaleComment'])) { - $oldSoaScaleCommentData = $this->getCurrentSoaScaleCommentData($anr); - $maxDest = \count($oldSoaScaleCommentData) - 1; - $maxOrig = \count($data['soaScaleComment']) - 1; - $this->mergeSoaScaleComment($data['soaScaleComment'], $anr); - } elseif (!isset($data['soaScaleComment']) && isset($data['soas'])) { - // old import case. TODO: move it to the Entity. - $defaultSoaScaleCommentdatas = [ - 'fr' => [ - ['scaleIndex' => 0, 'colour' => '#FFFFFF', 'isHidden' => false, 'comment' => 'Inexistant'], - ['scaleIndex' => 1, 'colour' => '#FD661F', 'isHidden' => false, 'comment' => 'Initialisé'], - ['scaleIndex' => 2, 'colour' => '#FD661F', 'isHidden' => false, 'comment' => 'Reproductible'], - ['scaleIndex' => 3, 'colour' => '#FFBC1C', 'isHidden' => false, 'comment' => 'Défini'], - ['scaleIndex' => 4, 'colour' => '#FFBC1C', 'isHidden' => false, - 'comment' => 'Géré quantitativement'], - ['scaleIndex' => 5, 'colour' => '#D6F107', 'isHidden' => false, 'comment' => 'Optimisé'], - ], - 'en' => [ - ['scaleIndex' => 0, 'colour' => '#FFFFFF', 'isHidden' => false, 'comment' => 'Non-existent'], - ['scaleIndex' => 1, 'colour' => '#FD661F', 'isHidden' => false, 'comment' => 'Initial'], - ['scaleIndex' => 2, 'colour' => '#FD661F', 'isHidden' => false, 'comment' => 'Managed'], - ['scaleIndex' => 3, 'colour' => '#FFBC1C', 'isHidden' => false, 'comment' => 'Defined'], - ['scaleIndex' => 4, 'colour' => '#FFBC1C', 'isHidden' => false, - 'comment' => 'Quantitatively managed'], - ['scaleIndex' => 5, 'colour' => '#D6F107', 'isHidden' => false, 'comment' => 'Optimized'], - ], - 'de' => [ - ['scaleIndex' => 0, 'colour' => '#FFFFFF', 'isHidden' => false, 'comment' => 'Nicht vorhanden'], - ['scaleIndex' => 1, 'colour' => '#FD661F', 'isHidden' => false, 'comment' => 'Initial'], - ['scaleIndex' => 2, 'colour' => '#FD661F', 'isHidden' => false, 'comment' => 'Reproduzierbar'], - ['scaleIndex' => 3, 'colour' => '#FFBC1C', 'isHidden' => false, 'comment' => 'Definiert'], - ['scaleIndex' => 4, 'colour' => '#FFBC1C', 'isHidden' => false, - 'comment' => 'Quantitativ verwaltet'], - ['scaleIndex' => 5, 'colour' => '#D6F107', 'isHidden' => false, 'comment' => 'Optimiert'], - ], - 'nl' => [ - ['scaleIndex' => 0, 'colour' => '#FFFFFF', 'isHidden' => false, 'comment' => 'Onbestaand'], - ['scaleIndex' => 1, 'colour' => '#FD661F', 'isHidden' => false, 'comment' => 'Initieel'], - ['scaleIndex' => 2, 'colour' => '#FD661F', 'isHidden' => false, 'comment' => 'Beheerst'], - ['scaleIndex' => 3, 'colour' => '#FFBC1C', 'isHidden' => false, 'comment' => 'Gedefinieerd'], - ['scaleIndex' => 4, 'colour' => '#FFBC1C', 'isHidden' => false, - 'comment' => 'Kwantitatief beheerst'], - ['scaleIndex' => 5, 'colour' => '#D6F107', 'isHidden' => false, 'comment' => 'Optimaliserend'], - ], - ]; - $data['soaScaleComment'] = - $defaultSoaScaleCommentdatas[$this->getAnrLanguageCode($anr)] ?? $defaultSoaScaleCommentdatas['en']; - $oldSoaScaleCommentData = $this->getCurrentSoaScaleCommentData($anr); - $this->mergeSoaScaleComment($data['soaScaleComment'], $anr); - $maxOrig = 5; // default value for old import - $maxDest = \count($oldSoaScaleCommentData) - 1; - } - // manage the current SOA - if ($maxDest !== null && $maxOrig !== null) { - $existedSoas = $this->soaTable->findByAnr($anr); - foreach ($existedSoas as $existedSoa) { - $soaComment = $existedSoa->getSoaScaleComment(); - if ($soaComment !== null) { - $valueToApprox = $soaComment->getScaleIndex(); - $newScaleIndex = $this->approximate( - $valueToApprox, - 0, - $maxDest, - 0, - $maxOrig, - 0 - ); - $soaScaleComment = $this->importCacheHelper - ->getItemFromArrayCache('newSoaScaleCommentIndexedByScale', $newScaleIndex); - if ($soaScaleComment !== null) { - $existedSoa->setSoaScaleComment($soaScaleComment); + $currentSoaScaleCommentData = $this->soaScaleCommentImportProcessor + ->prepareCacheAndGetCurrentSoaScaleCommentsData($anr); + $maxSoaScaleCommentDestination = \count($currentSoaScaleCommentData) - 1; + $maxSoaScaleCommentOrigin = \count($data['soaScaleComment']) - 1; + $this->soaScaleCommentImportProcessor->mergeSoaScaleComment($anr, $data['soaScaleComment']); + if ($maxSoaScaleCommentDestination !== $maxSoaScaleCommentOrigin) { + /** @var Entity\Soa $existedSoa */ + foreach ($this->soaTable->findByAnr($anr) as $existedSoa) { + $soaComment = $existedSoa->getSoaScaleComment(); + if ($soaComment !== null) { + $newScaleIndex = $this->scaleImportProcessor->convertValueWithinNewScalesRange( + $soaComment->getScaleIndex(), + 0, + $maxSoaScaleCommentDestination, + 0, + $maxSoaScaleCommentOrigin, + 0 + ); + $soaScaleComment = $this->importCacheHelper->getItemFromArrayCache( + 'newSoaScaleCommentIndexedByScale', + $newScaleIndex + ); + if ($soaScaleComment !== null) { + $existedSoa->setSoaScaleComment($soaScaleComment); + } + $this->soaTable->save($existedSoa, false); } - $this->soaTable->saveEntity($existedSoa, false); } } } // import the SOAs - if (isset($data['soas'])) { + if (!empty($data['soas'])) { foreach ($data['soas'] as $soaData) { $measure = $this->importCacheHelper->getItemFromArrayCache('measures', $soaData['measure_id']) ?: $this->measureTable->findByUuidAndAnr($soaData['measure_id'], $anr); @@ -731,11 +491,9 @@ private function importAnrFromArray( $soa->setSoaScaleComment($soaScaleComment); } } - $this->soaTable->saveEntity($soa, false); + $this->soaTable->save($soa, false); } } - - $this->soaTable->getDb()->flush(); } // import the GDPR records @@ -746,18 +504,21 @@ private function importAnrFromArray( } /* - * Import AnrMetadatasOnInstances + * Import AnrInstanceMetadataFields. */ - if (!empty($data['anrMetadataOnInstances'])) { - $this->cachedData['anrMetadataOnInstances'] = $this->getCurrentAnrMetadataOnInstances($anr); - $this->createAnrMetadataOnInstances($anr, $data['anrMetadataOnInstances']); + if (!empty($data['anrMetadatasOnInstances'])) { + $this->anrInstanceMetadataFieldImportProcessor->processAnrInstanceMetadataFields( + $anr, + $data['anrMetadatasOnInstances'] + ); } /* * Import scales. + * TODO: use the processor!!! */ if (!empty($data['scales'])) { - /* Approximate values based on the scales from the destination analysis */ + /* Approximate values based on the scales from the destination/importing analysis. */ $scalesData = $this->getCurrentAndExternalScalesData($anr, $data); @@ -774,7 +535,7 @@ private function importAnrFromArray( $instance->set($t, -1); } else { $instance->set($t . 'h', 0); - $instance->set($t, $this->approximate( + $instance->set($t, $this->convertValueWithinNewScalesRange( $instance->get($t), $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['min'], $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['max'], @@ -787,7 +548,7 @@ private function importAnrFromArray( } // Impacts & Consequences. foreach ($consequences as $conseq) { - $conseq->set($t, $conseq->isHidden() ? -1 : $this->approximate( + $conseq->set($t, $conseq->isHidden() ? -1 : $this->convertValueWithinNewScalesRange( $conseq->get($t), $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['min'], $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['max'], @@ -800,7 +561,7 @@ private function importAnrFromArray( /* Threats qualification. */ foreach ($this->threatTable->findByAnr($anr) as $threat) { - $threat->setQualification($this->approximate( + $threat->setQualification($this->convertValueWithinNewScalesRange( $threat->getQualification(), $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['min'], $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['max'], @@ -810,10 +571,9 @@ private function importAnrFromArray( $this->threatTable->save($threat, false); } - // Informational Risks /** @var Entity\InstanceRisk $instancesRisk */ foreach ($this->instanceRiskTable->findByAnr($anr) as $instanceRisk) { - $instanceRisk->setThreatRate($this->approximate( + $instanceRisk->setThreatRate($this->convertValueWithinNewScalesRange( $instanceRisk->getThreatRate(), $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['min'], $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['max'], @@ -821,29 +581,35 @@ private function importAnrFromArray( $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['max'], )); $oldVulRate = $instanceRisk->getVulnerabilityRate(); - $instanceRisk->setVulnerabilityRate($this->approximate( + $instanceRisk->setVulnerabilityRate($this->convertValueWithinNewScalesRange( $instanceRisk->getVulnerabilityRate(), $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( - $instanceRisk->getReductionAmount() !== 0 - ? $this->approximate($instanceRisk->getReductionAmount(), 0, $oldVulRate, 0, $newVulRate, 0) - : 0 - ); + if ($instanceRisk->getReductionAmount() !== 0) { + $newVulRate = $instanceRisk->getVulnerabilityRate(); + $instanceRisk->setReductionAmount($this->convertValueWithinNewScalesRange( + $instanceRisk->getReductionAmount(), + 0, + $oldVulRate, + 0, + $newVulRate, + 0 + )); + } - $this->anrInstanceRiskService->recalculateRiskRatesAndUpdateRecommendationsPositions($instanceRisk); + $this->recalculateRiskRates($instanceRisk); + $this->instanceRiskTable->save($instanceRisk, false); } /* Adjust the values of operational risks scales. */ - $this->adjustOperationalRisksScaleValuesBasedOnNewScales($anr, $data); + $this->operationalRiskScaleImportProcessor->adjustOperationalRisksScaleValuesBasedOnNewScales($anr, $data); - $this->updateScalesAndComments($anr, $data); + $this->scaleImportProcessor->updateScalesAndComments($anr, $data); - $this->updateOperationalRisksScalesAndRelatedInstances($anr, $data); + $this->operationalRiskScaleImportProcessor->updateOperationalRisksScalesAndRelatedInstances($anr, $data); } $first = true; @@ -880,13 +646,12 @@ private function importAnrFromArray( $instanceConsequence = $this->instanceConsequenceTable->getEntityByFields([ 'anr' => $anr->getId(), 'instance' => $instance->getId(), - 'scaleImpactType' => $siType->getId() + 'scaleImpactType' => $siType->getId(), ]); if (empty($instanceConsequence)) { $consequence = (new Entity\InstanceConsequence()) ->setAnr($anr) ->setInstance($instance) - ->setObject($instance->getObject()) ->setScaleImpactType($siType) ->setCreator($this->connectedUser->getEmail()); @@ -900,46 +665,7 @@ private function importAnrFromArray( return $instanceIds; } - /** - * Method to approximate the value within new bounds, typically when the exported object had a min/max bound - * bigger than the target's ANR bounds. - * - * @param int $value The value to approximate - * @param int $minorig The source min bound - * @param int $maxorig The source max bound - * @param int $mindest The target min bound - * @param int $maxdest The target max bound - * @param int $defaultvalue - * - * @return int The approximated value - */ - private function approximate( - int $value, - int $minorig, - int $maxorig, - int $mindest, - int $maxdest, - int $defaultvalue = -1 - ): int { - if ($value === $maxorig) { - return $maxdest; - } - - if ($value !== -1 && ($maxorig - $minorig) !== -1) { - return (int)min(max( - round(($value / ($maxorig - $minorig + 1)) * ($maxdest - $mindest + 1)), - $mindest - ), $maxdest); - } - - return $defaultvalue; - } - - private function isMonarcVersionLowerThen(string $version): bool - { - return version_compare($this->monarcVersion, $version) < 0; - } - + // TODO: use the processor. private function createSetOfRecommendations(array $data, Entity\Anr $anr): void { if (!empty($data['recSets'])) { @@ -1004,91 +730,6 @@ private function createSetOfRecommendations(array $data, Entity\Anr $anr): void } } - private function processRecommendationDataLinkedToRisk( - Entity\Anr $anr, - array $recommendationData, - bool $isRiskTreated - ): Entity\Recommendation { - if (isset($this->cachedData['recs'][$recommendationData['uuid']])) { - /** @var Entity\Recommendation $recommendation */ - $recommendation = $this->cachedData['recs'][$recommendationData['uuid']]; - if ($isRiskTreated && $recommendation->isPositionEmpty()) { - $recommendation->setPosition(++$this->currentAnalyseMaxRecommendationPosition); - $this->recommendationTable->save($recommendation, false); - } - - return $recommendation; - } - - $recommendationSetUuid = $recommendationData['recommandationSet'] ?? $recommendationData['recommendationSet']; - if (isset($this->cachedData['recSets'][$recommendationSetUuid])) { - $recommendationSet = $this->cachedData['recSets'][$recommendationSetUuid]; - } else { - $recommendationSet = $this->recommendationSetTable->findByUuidAndAnr($recommendationSetUuid, $anr); - - $this->cachedData['recSets'][$recommendationSet->getUuid()] = $recommendationSet; - } - - $recommendation = $this->recommendationTable->findByAnrCodeAndRecommendationSet( - $anr, - $recommendationData['code'], - $recommendationSet - ); - if ($recommendation === null) { - $recommendation = (new Entity\Recommendation())->setUuid($recommendationData['uuid']) - ->setCreator($this->connectedUser->getEmail()); - } else { - $recommendation->setUpdater($this->connectedUser->getEmail()); - } - - $recommendation->setAnr($anr) - ->setRecommendationSet($recommendationSet) - ->setComment($recommendationData['comment'] ?? '') - ->setResponsible($recommendationData['responsable'] ?? '') - ->setStatus($recommendationData['status']) - ->setImportance($recommendationData['importance']) - ->setCode($recommendationData['code']) - ->setDescription($recommendationData['description']) - ->setCounterTreated($recommendationData['counterTreated']); - if (!empty($recommendationData['duedate']['date'])) { - $recommendation->setDueDate(new DateTime($recommendationData['duedate']['date'])); - } - - if ($isRiskTreated && $recommendation->isPositionEmpty()) { - $recommendation->setPosition(++$this->currentAnalyseMaxRecommendationPosition); - } - - $this->recommendationTable->save($recommendation, false); - - $this->cachedData['recs'][$recommendation->getUuid()] = $recommendation; - - return $recommendation; - } - - /** - * Validates if the data can be imported into the anr. - */ - private function validateIfImportIsPossible(Entity\Anr $anr, ?Entity\Instance $parent, array $data): void - { - if ($parent !== null - && ($parent->getLevel() === CoreEntity\InstanceSuperClass::LEVEL_INTER || $parent->getAnr() !== $anr) - ) { - 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['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( array $data, Entity\Anr $anr, @@ -1107,21 +748,15 @@ private function prepareInstanceConsequences( foreach (Entity\Instance::getAvailableScalesCriteria() as $scaleCriteria) { if ($instance->{'getInherited' . $scaleCriteria}()) { - $instance->{'setInherited' . $scaleCriteria}(1); $instance->{'set' . $scaleCriteria}(-1); - } else { - $instance->{'setInherited' . $scaleCriteria}(0); - if (!$this->isImportTypeAnr()) { - $instance->{'set' . $scaleCriteria}( - $this->approximate( - $instance->{'get' . $scaleCriteria}(), - $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['min'], - $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['max'], - $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['min'], - $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['max'] - ) - ); - } + } elseif (!$this->isImportTypeAnr()) { + $instance->{'set' . $scaleCriteria}($this->convertValueWithinNewScalesRange( + $instance->{'get' . $scaleCriteria}(), + $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['min'], + $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['max'], + $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['min'], + $scalesData['current'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['max'] + )); } } @@ -1153,7 +788,6 @@ private function prepareInstanceConsequences( $instanceConsequence = (new Entity\InstanceConsequence()) ->setAnr($anr) - ->setObject($monarcObject) ->setInstance($instance) ->setScaleImpactType($localScalesImpactTypes[$consequenceData['scaleImpactType'][$labelKey]]) ->setIsHidden((bool)$consequenceData['isHidden']) @@ -1165,7 +799,7 @@ private function prepareInstanceConsequences( } else { $value = $this->isImportTypeAnr() ? $consequenceData[$criteriaKey] - : $this->approximate( + : $this->convertValueWithinNewScalesRange( $consequenceData[$criteriaKey], $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['min'], $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['max'], @@ -1184,26 +818,26 @@ private function prepareInstanceConsequences( } /** - * The prepared cachedData['scales'] are used to convert(approximate) the risks' values of importing instance(s), + * The prepared scales cache data is used to convert(approximate) the risks' values of importing instance(s), * from external to current scales (in case of instance(s) import). * For ANR import, the current analysis risks' values are converted from current to external scales. */ private function getCurrentAndExternalScalesData(Entity\Anr $anr, array $data): array { - if (empty($this->cachedData['scales'])) { - $scales = $this->scaleTable->findByAnr($anr); - $this->cachedData['scales']['current'] = []; - $this->cachedData['scales']['external'] = $data['scales']; - - foreach ($scales as $scale) { - $this->cachedData['scales']['current'][$scale->getType()] = [ + if (!$this->importCacheHelper->isCacheKeySet('scales_for_old_structure')) { + $this->importCacheHelper->addItemToArrayCache('scales_for_old_structure', $data['scales'], 'external'); + $currentScalesData = []; + /** @var Entity\Scale $scale */ + foreach ($this->scaleTable->findByAnr($anr) as $scale) { + $currentScalesData[$scale->getType()] = [ 'min' => $scale->getMin(), 'max' => $scale->getMax(), ]; } + $this->importCacheHelper->addItemToArrayCache('scales_for_old_structure', $currentScalesData, 'current'); } - return $this->cachedData['scales']; + return $this->importCacheHelper->getItemFromArrayCache('scales_for_old_structure'); } private function processInstanceRisks( @@ -1349,7 +983,7 @@ private function processInstanceRisks( $instanceRisk->setThreatRate( $this->isImportTypeAnr() ? $instanceRiskData['threatRate'] - : $this->approximate( + : $this->convertValueWithinNewScalesRange( $instanceRiskData['threatRate'], $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['min'], $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['max'], @@ -1360,7 +994,7 @@ private function processInstanceRisks( $instanceRisk->setVulnerabilityRate( $this->isImportTypeAnr() ? $instanceRiskData['vulnerabilityRate'] - : $this->approximate( + : $this->convertValueWithinNewScalesRange( $instanceRiskData['vulnerabilityRate'], $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_VULNERABILITY]['min'], $scalesData['external'][CoreEntity\ScaleSuperClass::TYPE_VULNERABILITY]['max'], @@ -1379,7 +1013,7 @@ private function processInstanceRisks( // 1 - 7 vers 1 - 3 on peut pas avoir une réduction de 4, 5, 6 ou 7 $instanceRisk->setReductionAmount( $instanceRiskData['reductionAmount'] !== -1 - ? $this->approximate( + ? $this->convertValueWithinNewScalesRange( $instanceRiskData['reductionAmount'], 0, $instanceRiskData['vulnerabilityRate'], @@ -1406,7 +1040,7 @@ private function processInstanceRisks( 'amv' => [ 'anr' => $anr->getId(), 'uuid' => $instanceRisk->getAmv()->getUuid(), - ] + ], ])); if ($instanceRiskBrothers !== null) { @@ -1433,13 +1067,16 @@ private function processInstanceRisks( // Process recommendations. if (!empty($data['recos'][$instanceRiskData['id']])) { + $this->recommendationImportProcessor->prepareRecommendationsCache($anr); foreach ($data['recos'][$instanceRiskData['id']] as $reco) { - $recommendation = $this->processRecommendationDataLinkedToRisk( + $recommendation = $this->recommendationImportProcessor->processRecommendationDataLinkedToRisk( $anr, $reco, + $data['recSets'], $instanceRiskData['kindOfMeasure'] !== CoreEntity\InstanceRiskSuperClass::KIND_NOT_TREATED ); -// TODO: check why do we have it here: + + /* Avoid linking the recommendation 2 times. */ foreach ($instanceRisk->getRecommendationRisks() as $recommendationRisk) { if ($recommendationRisk->getRecommendation() !== null && $recommendationRisk->getRecommendation()->getUuid() === $recommendation->getUuid() @@ -1448,19 +1085,12 @@ private function processInstanceRisks( } } - $recommendationRisk = (new Entity\RecommendationRisk()) - ->setAnr($anr) - ->setInstance($instance) - ->setInstanceRisk($instanceRisk) - ->setGlobalObject($monarcObject->isScopeGlobal() ? $monarcObject : null) - ->setAsset($instanceRisk->getAsset()) - ->setThreat($instanceRisk->getThreat()) - ->setVulnerability($instanceRisk->getVulnerability()) - ->setCommentAfter((string)$reco['commentAfter']) - ->setRecommendation($recommendation) - ->setCreator($this->connectedUser->getEmail()); - - $this->recommendationRiskTable->save($recommendationRisk, false); + $recommendationRisk = $this->anrRecommendationRiskService->createRecommendationRisk( + $recommendation, + $instanceRisk, + $reco['commentAfter'] ?? '', + false + ); // Replicate recommendation to brothers. if ($modeImport === 'merge' && $recommendationRisk->hasGlobalObjectRelation()) { @@ -1531,7 +1161,7 @@ private function processInstanceRisks( 'globalObject' => [ 'anr' => $anr->getId(), 'uuid' => $monarcObject->getUuid(), - ] + ], ]); if (!empty($brotherRecoRisks)) { @@ -1568,6 +1198,10 @@ private function processInstanceRisks( $recoToCreate = []; // Get all specific risks of instance foreach ($instance->getInstanceRisks() as $instanceRisk) { + + $this->recalculateRiskRates($instanceRisk); + $this->instanceRiskTable->save($instanceRisk, false); + if (!$instanceRisk->isSpecific()) { continue; } @@ -1608,7 +1242,7 @@ private function processInstanceRisks( 'vulnerability' => [ 'anr' => $anr->getId(), 'uuid' => $recommendationRiskToCreate->getVulnerability()->getUuid(), - ] + ], ]); if (empty($recoCreated)) { @@ -1646,408 +1280,31 @@ private function processInstanceRisks( $this->recommendationRiskTable->save($recommendationRisk, false); } } - $this->recommendationRiskTable->flush(); - // on met finalement à jour les risques en cascade - $this->anrInstanceService->recalculateRiskRates($instance); + $this->recommendationRiskTable->flush(); } - private function processOperationalInstanceRisks( - array $data, - Entity\Anr $anr, - Entity\Instance $instance, - Entity\MonarcObject $monarcObject, - bool $includeEval - ): void { - if (empty($data['risksop'])) { - return; - } - - $operationalRiskScalesData = $this->getCurrentOperationalRiskScalesData($anr); - $externalOperationalRiskScalesData = []; - $areScalesLevelsOfLikelihoodDifferent = false; - $areImpactScaleTypesValuesDifferent = false; - $matchedScaleTypesMap = []; - if ($includeEval && !$this->isImportTypeAnr()) { - $externalOperationalRiskScalesData = $this->getExternalOperationalRiskScalesData($anr, $data); - $areScalesLevelsOfLikelihoodDifferent = $this->areScalesLevelsOfTypeDifferent( - CoreEntity\OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD, - $operationalRiskScalesData, - $externalOperationalRiskScalesData - ); - $areImpactScaleTypesValuesDifferent = $this->areScaleTypeValuesDifferent( - CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT, - $operationalRiskScalesData, - $externalOperationalRiskScalesData - ); - $matchedScaleTypesMap = $this->matchAndGetOperationalRiskScaleTypesMap( - $anr, - $operationalRiskScalesData, - $externalOperationalRiskScalesData - ); - } - $oldInstanceRiskFieldsMapToScaleTypesFields = [ - ['brutR' => 'BrutValue', 'netR' => 'NetValue', 'targetedR' => 'TargetedValue'], - ['brutO' => 'BrutValue', 'netO' => 'NetValue', 'targetedO' => 'TargetedValue'], - ['brutL' => 'BrutValue', 'netL' => 'NetValue', 'targetedL' => 'TargetedValue'], - ['brutF' => 'BrutValue', 'netF' => 'NetValue', 'targetedF' => 'TargetedValue'], - ['brutP' => 'BrutValue', 'netP' => 'NetValue', 'targetedP' => 'TargetedValue'], - ]; - - foreach ($data['risksop'] as $operationalRiskData) { - $operationalInstanceRisk = (new Entity\InstanceRiskOp()) - ->setAnr($anr) - ->setInstance($instance) - ->setObject($monarcObject) - ->setRiskCacheLabels([ - 'riskCacheLabel1' => $operationalRiskData['riskCacheLabel1'], - 'riskCacheLabel2' => $operationalRiskData['riskCacheLabel2'], - 'riskCacheLabel3' => $operationalRiskData['riskCacheLabel3'], - 'riskCacheLabel4' => $operationalRiskData['riskCacheLabel4'], - ]) - ->setRiskCacheDescriptions([ - 'riskCacheDescription1' => $operationalRiskData['riskCacheDescription1'], - 'riskCacheDescription2' => $operationalRiskData['riskCacheDescription2'], - 'riskCacheDescription3' => $operationalRiskData['riskCacheDescription3'], - 'riskCacheDescription4' => $operationalRiskData['riskCacheDescription4'], - ]) - ->setBrutProb((int)$operationalRiskData['brutProb']) - ->setNetProb((int)$operationalRiskData['netProb']) - ->setTargetedProb((int)$operationalRiskData['targetedProb']) - ->setCacheBrutRisk((int)$operationalRiskData['cacheBrutRisk']) - ->setCacheNetRisk((int)$operationalRiskData['cacheNetRisk']) - ->setCacheTargetedRisk((int)$operationalRiskData['cacheTargetedRisk']) - ->setKindOfMeasure((int)$operationalRiskData['kindOfMeasure']) - ->setComment($operationalRiskData['comment'] ?? '') - ->setMitigation($operationalRiskData['mitigation'] ?? '') - ->setIsSpecific((bool)$operationalRiskData['specific']) - ->setContext($operationalRiskData['context'] ?? '') - ->setCreator($this->connectedUser->getEmail()); - - if (!empty($operationalRiskData['riskOwner'])) { - $instanceRiskOwner = $this->anrInstanceRiskOwnerService->getOrCreateInstanceRiskOwner( - $anr, - $operationalRiskData['riskOwner'] - ); - $operationalInstanceRisk->setInstanceRiskOwner($instanceRiskOwner); - } - - if ($areScalesLevelsOfLikelihoodDifferent) { - $this->adjustOperationalRisksProbabilityScales( - $operationalInstanceRisk, - $externalOperationalRiskScalesData[CoreEntity\OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD], - $operationalRiskScalesData[CoreEntity\OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD] - ); - } - - if (!empty($operationalRiskData['rolfRisk']) && $monarcObject->getRolfTag() !== null) { - /** @var RolfRisk|null $rolfRisk */ - $rolfRisk = $this->importCacheHelper - ->getItemFromArrayCache('rolf_risks_by_old_ids', (int)$operationalRiskData['rolfRisk']); - if ($rolfRisk !== null) { - $operationalInstanceRisk - ->setRolfRisk($rolfRisk) - ->setRiskCacheCode($rolfRisk->getCode()); - } - } - - $impactScale = $operationalRiskScalesData[CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT]; - foreach ($impactScale['operationalRiskScaleTypes'] as $index => $scaleType) { - /** @var Entity\OperationalRiskScaleType $operationalRiskScaleType */ - $operationalRiskScaleType = $scaleType['object']; - $operationalInstanceRiskScale = (new Entity\OperationalInstanceRiskScale()) - ->setAnr($anr) - ->setOperationalRiskScaleType($operationalRiskScaleType) - ->setOperationalInstanceRisk($operationalInstanceRisk) - ->setCreator($this->connectedUser->getEmail()); - - if ($includeEval) { - /* The format is since v2.11.0 */ - if (isset($operationalRiskData['scalesValues'])) { - $externalScaleTypeId = null; - if ($this->isImportTypeAnr()) { - /* For anr import, match current scale type translation key with external ids. */ - $externalScaleTypeId = $this->getExternalScaleTypeIdByCurrentScaleLabelTranslationKey( - $operationalRiskScaleType->getLabelTranslationKey() - ); - } elseif (isset($matchedScaleTypesMap['currentScaleTypeKeysToExternalIds'] - [$operationalRiskScaleType->getLabelTranslationKey()]) - ) { - /* For instance import, match current scale type translation key with external ids. */ - $externalScaleTypeId = $matchedScaleTypesMap['currentScaleTypeKeysToExternalIds'] - [$operationalRiskScaleType->getLabelTranslationKey()]; - } - if ($externalScaleTypeId !== null - && isset($operationalRiskData['scalesValues'][$externalScaleTypeId]) - ) { - $scalesValueData = $operationalRiskData['scalesValues'][$externalScaleTypeId]; - $operationalInstanceRiskScale->setBrutValue($scalesValueData['brutValue']); - $operationalInstanceRiskScale->setNetValue($scalesValueData['netValue']); - $operationalInstanceRiskScale->setTargetedValue($scalesValueData['targetedValue']); - if ($areImpactScaleTypesValuesDifferent) { - /* We convert from the importing new scales to the current anr scales. */ - $this->adjustOperationalInstanceRisksScales( - $operationalInstanceRiskScale, - $externalOperationalRiskScalesData[ - CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT - ], - $impactScale - ); - } - } - /* The format before v2.11.0. Update only first 5 scales (ROLFP if not changed by user). */ - } elseif ($index < 5) { - foreach ($oldInstanceRiskFieldsMapToScaleTypesFields[$index] as $oldFiled => $typeField) { - $operationalInstanceRiskScale->{'set' . $typeField}($operationalRiskData[$oldFiled]); - } - if ($areImpactScaleTypesValuesDifferent) { - /* We convert from the importing new scales to the current anr scales. */ - $this->adjustOperationalInstanceRisksScales( - $operationalInstanceRiskScale, - $externalOperationalRiskScalesData[ - CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT - ], - $impactScale - ); - } - } - } - - $this->operationalInstanceRiskScaleTable->save($operationalInstanceRiskScale, false); - } - - if (!empty($matchedScaleTypesMap['notMatchedScaleTypes']) && !$this->isImportTypeAnr()) { - /* In case of instance import, there is a need to create external scale types in case if - the linked values are set for at least one operational instance risk. - The new created type has to be linked with all the existed risks. */ - $anrLanguageCode = $this->getAnrLanguageCode($anr); - foreach ($matchedScaleTypesMap['notMatchedScaleTypes'] as $extScaleTypeId => $extScaleTypeData) { - if (isset($operationalRiskData['scalesValues'][$extScaleTypeId])) { - $scalesValueData = $operationalRiskData['scalesValues'][$extScaleTypeId]; - if ($scalesValueData['netValue'] !== -1 - || $scalesValueData['brutValue'] !== -1 - || $scalesValueData['targetedValue'] !== -1 - ) { - $labelTranslationKey = (string)Uuid::uuid4(); - $operationalRiskScaleType = (new Entity\OperationalRiskScaleType()) - ->setAnr($anr) - ->setOperationalRiskScale($impactScale['object']) - ->setLabelTranslationKey($labelTranslationKey) - ->setCreator($this->connectedUser->getEmail()); - $this->operationalRiskScaleTypeTable->save($operationalRiskScaleType, false); - - $translation = (new Translation()) - ->setAnr($anr) - ->setType(Translation::OPERATIONAL_RISK_SCALE_TYPE) - ->setKey($labelTranslationKey) - ->setValue($extScaleTypeData['translation']['value']) - ->setLang($anrLanguageCode) - ->setCreator($this->connectedUser->getEmail()); - $this->translationTable->save($translation, false); - - foreach ($extScaleTypeData['operationalRiskScaleComments'] as $scaleCommentData) { - $this->createOrUpdateOperationalRiskScaleComment( - $anr, - false, - $impactScale['object'], - $scaleCommentData, - [], - [], - $operationalRiskScaleType - ); - } - - $operationalInstanceRiskScale = (new Entity\OperationalInstanceRiskScale()) - ->setAnr($anr) - ->setOperationalInstanceRisk($operationalInstanceRisk) - ->setOperationalRiskScaleType($operationalRiskScaleType) - ->setBrutValue($scalesValueData['brutValue']) - ->setNetValue($scalesValueData['netValue']) - ->setTargetedValue($scalesValueData['targetedValue']) - ->setCreator($this->connectedUser->getEmail()); - $this->operationalInstanceRiskScaleTable->save($operationalInstanceRiskScale); - - $this->adjustOperationalInstanceRisksScales( - $operationalInstanceRiskScale, - $externalOperationalRiskScalesData[ - CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT - ], - $impactScale - ); - - /* To swap the scale risk between the to keys in the map as it is already matched. */ - unset($matchedScaleTypesMap['notMatchedScaleTypes'][$extScaleTypeId]); - $matchedScaleTypesMap['currentScaleTypeKeysToExternalIds'] - [$operationalRiskScaleType->getLabelTranslationKey()] = $extScaleTypeId; - - /* Due to the new scale type and related comments the cache has to be reset. */ - $this->cachedData['currentOperationalRiskScalesData'] = []; - $operationalRiskScalesData = $this->getCurrentOperationalRiskScalesData($anr); - $areImpactScaleTypesValuesDifferent = true; - - /* Link the newly created scale type to all the existed operational risks. */ - $operationalInstanceRisks = $this->instanceRiskOpTable->findByAnrAndInstance( - $anr, - $instance - ); - foreach ($operationalInstanceRisks as $operationalInstanceRiskToUpdate) { - if ($operationalInstanceRiskToUpdate->getId() !== $operationalInstanceRisk->getId()) { - $operationalInstanceRiskScale = (new Entity\OperationalInstanceRiskScale()) - ->setAnr($anr) - ->setOperationalInstanceRisk($operationalInstanceRiskToUpdate) - ->setOperationalRiskScaleType($operationalRiskScaleType) - ->setCreator($this->connectedUser->getEmail()); - $this->operationalInstanceRiskScaleTable->save( - $operationalInstanceRiskScale, - false - ); - } - } - } - } - } - } - - if ($includeEval) { - /* recalculate the cached risk values */ - $this->anrInstanceRiskOpService->updateRiskCacheValues($operationalInstanceRisk, false); - } - - $this->instanceRiskOpTable->save($operationalInstanceRisk, false); - - /* Process recommendations related to the operational risk. */ - if ($includeEval && !empty($data['recosop'][$operationalRiskData['id']])) { - foreach ($data['recosop'][$operationalRiskData['id']] as $recommendationData) { - $recommendation = $this->processRecommendationDataLinkedToRisk( - $anr, - $recommendationData, - $operationalRiskData['kindOfMeasure'] !== InstanceRiskOpSuperClass::KIND_NOT_TREATED - ); - - $recommendationRisk = (new RecommendationRisk()) - ->setInstance($instance) - ->setInstanceRiskOp($operationalInstanceRisk) - ->setGlobalObject($monarcObject->isScopeGlobal() ? $monarcObject : null) - ->setCommentAfter($recommendationData['commentAfter'] ?? '') - ->setRecommendation($recommendation); - - // TODO: remove the trick when #240 is done. - $this->recommendationRiskTable->save($recommendationRisk); - $this->recommendationRiskTable->save($recommendationRisk->setAnr($anr), false); - } - } - - $this->recommendationRiskTable->flush(); - } - } - - private function matchAndGetOperationalRiskScaleTypesMap( - Entity\Anr $anr, - array $operationalRiskScalesData, - array $externalOperationalRiskScalesData - ): array { - $matchedScaleTypesMap = [ - 'currentScaleTypeKeysToExternalIds' => [], - 'notMatchedScaleTypes' => [], - ]; - $anrLanguageCode = $this->getAnrLanguageCode($anr); - $scaleTypesTranslations = $this->translationTable->findByAnrTypesAndLanguageIndexedByKey( - $anr, - [Translation::OPERATIONAL_RISK_SCALE_TYPE], - $anrLanguageCode - ); - $scaleTypesData = $operationalRiskScalesData[CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT] - ['operationalRiskScaleTypes']; - $externalScaleTypesData = $externalOperationalRiskScalesData - [CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT]['operationalRiskScaleTypes']; - foreach ($externalScaleTypesData as $externalScaleTypeData) { - $isMatched = false; - foreach ($scaleTypesData as $scaleTypeData) { - /** @var Entity\OperationalRiskScaleType $scaleType */ - $scaleType = $scaleTypeData['object']; - $scaleTypeTranslation = $scaleTypesTranslations[$scaleType->getLabelTranslationKey()]; - if ($externalScaleTypeData['translation']['value'] === $scaleTypeTranslation->getValue()) { - $matchedScaleTypesMap['currentScaleTypeKeysToExternalIds'][$scaleType->getLabelTranslationKey()] - = $externalScaleTypeData['id']; - $isMatched = true; - break; - } - } - if (!$isMatched) { - $matchedScaleTypesMap['notMatchedScaleTypes'][$externalScaleTypeData['id']] = $externalScaleTypeData; - } - } - - return $matchedScaleTypesMap; - } - - private function getExternalScaleTypeIdByCurrentScaleLabelTranslationKey(string $labelTranslationKey): ?int - { - return $this->cachedData['operationalRiskScaleTypes']['currentScaleTypeLabelTranslationKeyToExternalIds'] - [$labelTranslationKey] ?? null; - } - - private function areScalesLevelsOfTypeDifferent( - int $type, - array $operationalRiskScales, - array $externalOperationalRiskScalesData - ): bool { - foreach ($operationalRiskScales as $scaleType => $operationalRiskScale) { - if ($scaleType === $type) { - $externalScaleDataOfType = $externalOperationalRiskScalesData[$type]; - if ($operationalRiskScale['min'] !== $externalScaleDataOfType['min'] - || $operationalRiskScale['max'] !== $externalScaleDataOfType['max']) { - return true; - } - } - } - - return false; - } - - /** - * Checks if any of the scale comments values related to the scale types have different scaleValue - * then in the new operational scale data. - */ - public function areScaleTypeValuesDifferent( - int $type, - array $operationalRiskScales, - array $extOperationalRiskScalesData - ): bool { - foreach ($operationalRiskScales[$type]['commentsIndexToValueMap'] as $scaleIndex => $scaleValue) { - if (!isset($extOperationalRiskScalesData[$type]['commentsIndexToValueMap'][$scaleIndex])) { - return true; - } - $extScaleValue = $extOperationalRiskScalesData[$type]['commentsIndexToValueMap'][$scaleIndex]; - if ($scaleValue !== $extScaleValue) { - return true; - } - } - - return false; - } - - /** - * Update the instance impacts from brothers for global assets. - */ - 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 (Entity\InstanceConsequence::getAvailableScalesCriteria() as $scaleCriteria) { - if ($instanceBrother->{'getInherited' . $scaleCriteria}() === 0) { - $instance->{'setInherited' . $scaleCriteria}(0); - $instance->{'set' . $scaleCriteria}($instanceBrother->{'get' . $scaleCriteria}()); - } elseif ($instance->getParent()) { - $instance->{'setInherited' . $scaleCriteria}(1); - $instance->{'set' . $scaleCriteria}($instance->getParent()->{'get' . $scaleCriteria}()); - } else { - $instance->{'setInherited' . $scaleCriteria}(1); - $instance->{'set' . $scaleCriteria}($instanceBrother->{'get' . $scaleCriteria}()); - } + /** + * Update the instance impacts from brothers for global assets. + */ + 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 (Entity\InstanceConsequence::getAvailableScalesCriteria() as $scaleCriteria) { + if ($instanceBrother->{'getInherited' . $scaleCriteria}() === 0) { + $instance->{'setInherited' . $scaleCriteria}(0); + $instance->{'set' . $scaleCriteria}($instanceBrother->{'get' . $scaleCriteria}()); + } elseif ($instance->getParent()) { + $instance->{'setInherited' . $scaleCriteria}(1); + $instance->{'set' . $scaleCriteria}($instance->getParent()->{'get' . $scaleCriteria}()); + } else { + $instance->{'setInherited' . $scaleCriteria}(1); + $instance->{'set' . $scaleCriteria}($instanceBrother->{'get' . $scaleCriteria}()); + } } // Update consequences for all brothers. @@ -2076,9 +1333,9 @@ private function updateInstanceImpactsFromBrothers(Entity\Instance $instance, st } /** - * @return Instance[] + * @return Entity\Instance[] */ - private function getInstanceBrothers(InstanceSuperClass $instance): array + private function getInstanceBrothers(Entity\Instance $instance): array { if (!isset($this->cachedData['instanceBrothers'][$instance->getId()])) { $this->cachedData['instanceBrothers'][$instance->getId()] = $this->instanceTable @@ -2108,7 +1365,7 @@ private function createInstanceRiskFromData( ->setThreat($threat) ->setVulnerability($vulnerability) ->setSpecific($instanceRiskData['specific']) - ->setMh((int)$instanceRiskData['mh']) + ->setIsThreatRateNotSetOrModifiedExternally((bool)$instanceRiskData['mh']) ->setThreatRate((int)$instanceRiskData['threatRate']) ->setVulnerabilityRate((int)$instanceRiskData['vulnerabilityRate']) ->setKindOfMeasure($instanceRiskData['kindOfMeasure']) @@ -2124,7 +1381,8 @@ private function createInstanceRiskFromData( ->setCreator($this->connectedUser->getEmail()); if (!empty($instanceRiskData['riskOwner'])) { - $instanceRiskOwner = $this->anrInstanceRiskOwnerService->getOrCreateInstanceRiskOwner( + $instanceRiskOwner = $this->instanceRiskOwnerService->getOrCreateInstanceRiskOwner( + $anr, $anr, $instanceRiskData['riskOwner'] ); @@ -2138,982 +1396,57 @@ private function createInstance( array $data, Entity\Anr $anr, ?CoreEntity\InstanceSuperClass $parentInstance, - Entity\MonarcObject $monarcObject, - bool $saveInDb = true + Entity\MonarcObject $monarcObject ): Entity\Instance { $instanceData = $data['instance']; - $instance = (new Entity\Instance()) - ->setAnr($anr) - ->setLabels($instanceData) - ->setNames($instanceData) - ->setLevel($parentInstance === null ? Entity\Instance::LEVEL_ROOT : $instanceData['level']) - ->setRoot($parentInstance === null ? null : ($parentInstance->getRoot() ?? $parentInstance)) - ->setParent($parentInstance) - ->setAssetType($instanceData['assetType']) - ->setExportable($instanceData['exportable']) - ->setPosition(++$this->currentMaxInstancePosition) - ->setObject($monarcObject) - ->setAsset($monarcObject->getAsset()) - ->setCreator($this->connectedUser->getEmail()); - if (isset($instanceData['c'])) { - $instance->setConfidentiality((int)$instanceData['c']); - } - if (isset($instanceData['i'])) { - $instance->setIntegrity((int)$instanceData['i']); - } - if (isset($instanceData['d'])) { - $instance->setAvailability((int)$instanceData['d']); - } - if (isset($instanceData['ch'])) { - $instance->setInheritedConfidentiality((int)$instanceData['ch']); - } - if (isset($instanceData['ih'])) { - $instance->setInheritedIntegrity((int)$instanceData['ih']); - } - if (isset($instanceData['dh'])) { - $instance->setInheritedAvailability((int)$instanceData['dh']); - } - - $this->instanceTable->save($instance, $saveInDb); - - return $instance; - } - - /** - * @throws Exception - */ - private function setAndValidateMonarcVersion($data): void - { - if (isset($data['monarc_version'])) { - $this->monarcVersion = strpos($data['monarc_version'], 'master') === false ? $data['monarc_version'] : '99'; - } - - if ($this->isMonarcVersionLowerThen('2.8.2')) { - throw new Exception('Import of files exported from MONARC v2.8.1 or lower are not supported.' - . ' Please contact us for more details.'); - } - } - - private function adjustOperationalRisksScaleValuesBasedOnNewScales(Entity\Anr $anr, array $data): void - { - $operationalInstanceRisks = $this->instanceRiskOpTable->findByAnr($anr); - if (!empty($operationalInstanceRisks)) { - $currentOperationalRiskScalesData = $this->getCurrentOperationalRiskScalesData($anr); - $externalOperationalRiskScalesData = $this->getExternalOperationalRiskScalesData($anr, $data); - - foreach ($operationalInstanceRisks as $operationalInstanceRisk) { - $this->adjustOperationalRisksProbabilityScales( - $operationalInstanceRisk, - $currentOperationalRiskScalesData[CoreEntity\OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD], - $externalOperationalRiskScalesData[CoreEntity\OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD] - ); - - foreach ($operationalInstanceRisk->getOperationalInstanceRiskScales() as $instanceRiskScale) { - $this->adjustOperationalInstanceRisksScales( - $instanceRiskScale, - $currentOperationalRiskScalesData[CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT], - $externalOperationalRiskScalesData[CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT] - ); - } - - $this->instanceRiskOpTable->save($operationalInstanceRisk, false); - - $this->anrInstanceRiskOpService->updateRiskCacheValues($operationalInstanceRisk); - } - - $this->instanceRiskOpTable->flush(); - } - } - - private function adjustOperationalRisksProbabilityScales( - Entity\InstanceRiskOp $operationalInstanceRisk, - array $fromOperationalRiskScalesData, - array $toOperationalRiskScalesData - ): void { - foreach (['NetProb', 'BrutProb', 'TargetedProb'] as $likelihoodScaleName) { - $operationalInstanceRisk->{'set' . $likelihoodScaleName}($this->approximate( - $operationalInstanceRisk->{'get' . $likelihoodScaleName}(), - $fromOperationalRiskScalesData['min'], - $fromOperationalRiskScalesData['max'], - $toOperationalRiskScalesData['min'], - $toOperationalRiskScalesData['max'] - )); - } - } - - private function adjustOperationalInstanceRisksScales( - Entity\OperationalInstanceRiskScale $instanceRiskScale, - array $fromOperationalRiskScalesData, - array $toOperationalRiskScalesData - ): void { - foreach (['NetValue', 'BrutValue', 'TargetedValue'] as $impactScaleName) { - $scaleImpactValue = $instanceRiskScale->{'get' . $impactScaleName}(); - if ($scaleImpactValue === -1) { - continue; - } - $scaleImpactIndex = array_search( - $scaleImpactValue, - $fromOperationalRiskScalesData['commentsIndexToValueMap'], - true - ); - if ($scaleImpactIndex === false) { - continue; - } - - $approximatedIndex = $this->approximate( - $scaleImpactIndex, - $fromOperationalRiskScalesData['min'], - $fromOperationalRiskScalesData['max'], - $toOperationalRiskScalesData['min'], - $toOperationalRiskScalesData['max'] - ); - - $approximatedValueToNewScales = $toOperationalRiskScalesData['commentsIndexToValueMap'][$approximatedIndex] - ?? $scaleImpactValue; - $instanceRiskScale->{'set' . $impactScaleName}($approximatedValueToNewScales); - - $this->operationalInstanceRiskScaleTable->save($instanceRiskScale, false); - } - } + $instanceData['object'] = $monarcObject; + $instanceData['position'] = ++$this->currentMaxInstancePosition; + $instanceData['setOnlyExactPosition'] = true; - private function getCurrentOperationalRiskScalesData(Anr $anr): array - { - if (empty($this->cachedData['currentOperationalRiskScalesData'])) { - $operationalRisksScales = $this->operationalRiskScaleTable->findByAnr($anr); - foreach ($operationalRisksScales as $operationalRisksScale) { - $scaleTypesData = []; - $commentsIndexToValueMap = []; - /* Build the map of the comments index <=> values relation. */ - foreach ($operationalRisksScale->getOperationalRiskScaleTypes() as $typeIndex => $scaleType) { - /* The operational risk scale types object is used to recreate operational instance risk scales. */ - $scaleTypesData[$typeIndex]['object'] = $scaleType; - /* All the scale comment have the same index -> value corresponding values, so populating once. */ - if (empty($commentsIndexToValueMap)) { - foreach ($scaleType->getOperationalRiskScaleComments() as $scaleTypeComment) { - if (!$scaleTypeComment->isHidden()) { - $commentsIndexToValueMap[$scaleTypeComment->getScaleIndex()] = - $scaleTypeComment->getScaleValue(); - } - } - } - } - - $this->cachedData['currentOperationalRiskScalesData'][$operationalRisksScale->getType()] = [ - 'min' => $operationalRisksScale->getMin(), - 'max' => $operationalRisksScale->getMax(), - 'object' => $operationalRisksScale, - 'commentsIndexToValueMap' => $commentsIndexToValueMap, - 'operationalRiskScaleTypes' => $scaleTypesData, - 'operationalRiskScaleComments' => $operationalRisksScale->getOperationalRiskScaleComments(), - ]; - } - } - - return $this->cachedData['currentOperationalRiskScalesData']; + return $this->anrInstanceService->createInstance($anr, $instanceData, $parentInstance === null); } /** - * 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(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 = [ - CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT => [ - 'min' => 0, - 'max' => $data['scales'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['max'] - - $data['scales'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['min'], - 'commentsIndexToValueMap' => [], - 'operationalRiskScaleTypes' => [], - 'operationalRiskScaleComments' => [], - ], - CoreEntity\OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD => [ - 'min' => $data['scales'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['min'], - 'max' => $data['scales'][CoreEntity\ScaleSuperClass::TYPE_THREAT]['max'], - 'commentsIndexToValueMap' => [], - 'operationalRiskScaleTypes' => [], - 'operationalRiskScaleComments' => [], - ], - ]; - if (!empty($data['operationalRiskScales'])) { - /* Overwrite the values for the version >= 2.10.5. */ - foreach ($data['operationalRiskScales'] as $scaleType => $operationalRiskScaleData) { - $scalesDataResult[$scaleType]['min'] = $operationalRiskScaleData['min']; - $scalesDataResult[$scaleType]['max'] = $operationalRiskScaleData['max']; - - /* Build the map of the comments index <=> values relation. */ - foreach ($operationalRiskScaleData['operationalRiskScaleTypes'] as $typeIndex => $scaleTypeData) { - $scalesDataResult[$scaleType]['operationalRiskScaleTypes'][$typeIndex] = $scaleTypeData; - /* All the scale comment have the same index->value corresponding values, so populating once. */ - if (empty($scalesDataResult[$scaleType]['commentsIndexToValueMap'])) { - foreach ($scaleTypeData['operationalRiskScaleComments'] as $scaleTypeComment) { - if (!$scaleTypeComment['isHidden']) { - $scalesDataResult[$scaleType]['commentsIndexToValueMap'] - [$scaleTypeComment['scaleIndex']] = $scaleTypeComment['scaleValue']; - } - } - } - } - - $scalesDataResult[$scaleType]['operationalRiskScaleComments'] = - $operationalRiskScaleData['operationalRiskScaleComments']; - } - } else { - /* Convert comments and types from informational risks to operational (new format). */ - $anrLanguageCode = $this->getAnrLanguageCode($anr); - $scaleMin = $data['scales'][CoreEntity\ScaleSuperClass::TYPE_IMPACT]['min']; - foreach ($this->scaleImpactTypeTable->findByAnrOrderedByPosition($anr) as $index => $scaleImpactType) { - if ($scaleImpactType->isSys() && \in_array( - $scaleImpactType->getType(), - CoreEntity\ScaleImpactTypeSuperClass::getScaleImpactTypesRolfp(), - true - )) { - $labelTranslationKey = (string)Uuid::uuid4(); - $scalesDataResult[CoreEntity\ScaleSuperClass::TYPE_IMPACT]['operationalRiskScaleTypes'][$index] = [ - 'id' => $scaleImpactType->getId(), - 'isHidden' => $scaleImpactType->isHidden(), - 'labelTranslationKey' => $labelTranslationKey, - 'translation' => [ - 'key' => $labelTranslationKey, - 'lang' => $anrLanguageCode, - 'value' => $scaleImpactType->getLabel($anr->getLanguage()), - ], - ]; - } - } - foreach ($data['scalesComments'] as $scaleComment) { - $scaleType = $scaleComment['scale']['type']; - if (!\in_array( - $scaleType, - [CoreEntity\ScaleSuperClass::TYPE_IMPACT, CoreEntity\ScaleSuperClass::TYPE_THREAT], - true - )) { - continue; - } - - if ($scaleType === CoreEntity\ScaleSuperClass::TYPE_THREAT) { - $commentTranslationKey = (string)Uuid::uuid4(); - $scalesDataResult[$scaleType]['operationalRiskScaleComments'][] = [ - 'id' => $scaleComment['id'], - 'scaleIndex' => $scaleComment['val'], - 'scaleValue' => $scaleComment['val'], - 'isHidden' => false, - 'commentTranslationKey' => $commentTranslationKey, - 'translation' => [ - 'key' => $commentTranslationKey, - 'lang' => $anrLanguageCode, - 'value' => $scaleComment['comment' . $anr->getLanguage()] ?? '', - ], - ]; - } elseif ($scaleType === CoreEntity\ScaleSuperClass::TYPE_IMPACT - && $scaleComment['val'] >= $scaleMin - ) { - $commentTranslationKey = (string)Uuid::uuid4(); - $scaleIndex = $scaleComment['val'] - $scaleMin; - $scaleTypePosition = $scaleComment['scaleImpactType']['position']; - if (isset($scalesDataResult[$scaleType]['operationalRiskScaleTypes'][$scaleTypePosition])) { - $scalesDataResult[$scaleType]['operationalRiskScaleTypes'][$scaleTypePosition] - ['operationalRiskScaleComments'][] = [ - 'id' => $scaleComment['id'], - 'scaleIndex' => $scaleIndex, - 'scaleValue' => $scaleComment['val'], - 'isHidden' => false, - 'commentTranslationKey' => $commentTranslationKey, - 'translation' => [ - 'key' => $commentTranslationKey, - 'lang' => $anrLanguageCode, - 'value' => $scaleComment['comment' . $anr->getLanguage()] ?? '', - ], - ]; - - $scalesDataResult[$scaleType]['commentsIndexToValueMap'][$scaleIndex] - = $scaleComment['val']; - } - } - } - } - - $this->cachedData['externalOperationalRiskScalesData'] = $scalesDataResult; - } - - return $this->cachedData['externalOperationalRiskScalesData']; - } - - private function updateScalesAndComments(Entity\Anr $anr, array $data): void - { - $scalesByType = []; - $scales = $this->scaleTable->findByAnr($anr); - foreach ([ - CoreEntity\ScaleSuperClass::TYPE_IMPACT, - CoreEntity\ScaleSuperClass::TYPE_THREAT, - CoreEntity\ScaleSuperClass::TYPE_VULNERABILITY - ] as $type) { - foreach ($scales as $scale) { - if ($scale->getType() === $type) { - $scale->setMin((int)$data['scales'][$type]['min']); - $scale->setMax((int)$data['scales'][$type]['max']); - - $scalesByType[$type] = $scale; - - $this->scaleTable->save($scale, false); - } - } - } - - if (!empty($data['scalesComments'])) { - $scaleComments = $this->scaleCommentTable->findByAnr($anr); - foreach ($scaleComments as $scaleComment) { - if ($scaleComment->getScaleImpactType() === null - || $scaleComment->getScaleImpactType()->isSys() - ) { - $this->scaleCommentTable->remove($scaleComment, false); - } - } - $this->scaleCommentTable->flush(); - - // TODO: there is no position field anymore. - $scaleImpactTypes = $this->scaleImpactTypeTable->findByAnrOrderedAndIndexedByPosition($anr); - foreach ($data['scalesComments'] as $scalesCommentData) { - /* - * Comments, which are not matched with a scale impact type, should not be created. - * This is possible only for exported files before v2.11.0. - */ - if (isset($scalesCommentData['scaleImpactType']) - && !isset($scaleImpactTypes[$scalesCommentData['scaleImpactType']['position']]) - && !isset($scalesCommentData['scaleImpactType']['labels']) - ) { - continue; - } - - $scale = $scalesByType[$scalesCommentData['scale']['type']]; - $scaleComment = (new Entity\ScaleComment()) - ->setAnr($anr) - ->setScale($scale) - ->setScaleIndex($scalesCommentData['scaleIndex'] ?? $scalesCommentData['val']) - ->setScaleValue($scalesCommentData['scaleValue'] ?? $scalesCommentData['val']) - ->setComments([ - 'comment1' => $scalesCommentData['comment1'], - 'comment2' => $scalesCommentData['comment2'], - 'comment3' => $scalesCommentData['comment3'], - 'comment4' => $scalesCommentData['comment4'], - ]) - ->setCreator($this->connectedUser->getEmail()); - - if (isset($scalesCommentData['scaleImpactType']['position'])) { - $scaleImpactTypePosition = $scalesCommentData['scaleImpactType']['position']; - $scaleImpactType = $scaleImpactTypes[$scaleImpactTypePosition] ?? null; - $isSystem = $scaleImpactType !== null && $scaleImpactType->isSys(); - /* Scale impact types are presented in the export separately since v2.11.0 */ - if (isset($scalesCommentData['scaleImpactType']['labels']) - && !$isSystem - && ($scaleImpactType === null - || $scaleImpactType->getLabel($anr->getLanguage()) - !== $scalesCommentData['scaleImpactType']['labels']['label' . $anr->getLanguage()] - ) - ) { - $scaleImpactType = (new Entity\ScaleImpactType()) - ->setType($scalesCommentData['scaleImpactType']['type']) - ->setLabels($scalesCommentData['scaleImpactType']['labels']) - ->setIsSys($scalesCommentData['scaleImpactType']['isSys']) - ->setIsHidden($scalesCommentData['scaleImpactType']['isHidden']) - ->setAnr($anr) - ->setScale($scale) - ->setCreator($this->connectedUser->getEmail()); - - $this->scaleImpactTypeTable->save($scaleImpactType, false); - - $scaleImpactTypes[$scaleImpactTypePosition] = $scaleImpactType; - } - if ($scaleImpactType === null) { - continue; - } - - /* We may overwrite the comments if position is matched but scale type labels are different */ - $scaleComment->setScaleImpactType($scaleImpactType); - } - - $this->scaleCommentTable->save($scaleComment, false); - } - $this->scaleCommentTable->flush(); - } - - /* Reset the cache */ - $this->cachedData['scales'] = []; - } - - private function updateOperationalRisksScalesAndRelatedInstances(Entity\Anr $anr, array $data): void - { - $operationalRiskScales = $this->operationalRiskScaleTable->findByAnr($anr); - $anrLanguageCode = $this->getAnrLanguageCode($anr); - $scalesTranslations = $this->translationTable->findByAnrTypesAndLanguageIndexedByKey( - $anr, - [Translation::OPERATIONAL_RISK_SCALE_TYPE, Translation::OPERATIONAL_RISK_SCALE_COMMENT], - $anrLanguageCode - ); - $externalOperationalScalesData = $this->getExternalOperationalRiskScalesData($anr, $data); - - foreach ($operationalRiskScales as $operationalRiskScale) { - $scaleData = $externalOperationalScalesData[$operationalRiskScale->getType()]; - $currentScaleLevelDifferenceFromExternal = $operationalRiskScale->getMax() - $scaleData['max']; - $operationalRiskScale - ->setAnr($anr) - ->setMin($scaleData['min']) - ->setMax($scaleData['max']) - ->setUpdater($this->connectedUser->getEmail()); - - /* This is currently only applicable for impact scales type. */ - $createdScaleTypes = []; - $matchedScaleTypes = []; - foreach ($scaleData['operationalRiskScaleTypes'] as $scaleTypeData) { - $isScaleTypeMatched = true; - $operationalRiskScaleType = $this->matchScaleTypeDataWithScaleTypesList( - $scaleTypeData, - $operationalRiskScale->getOperationalRiskScaleTypes(), - $scalesTranslations - ); - if ($operationalRiskScaleType === null) { - $isScaleTypeMatched = false; - $labelTranslationKey = (string)Uuid::uuid4(); - $operationalRiskScaleType = (new Entity\OperationalRiskScaleType()) - ->setAnr($anr) - ->setOperationalRiskScale($operationalRiskScale) - ->setLabelTranslationKey($labelTranslationKey) - ->setCreator($this->connectedUser->getEmail()); - - $translation = (new Translation()) - ->setAnr($anr) - ->setType(Translation::OPERATIONAL_RISK_SCALE_TYPE) - ->setLang($anrLanguageCode) - ->setKey($labelTranslationKey) - ->setValue($scaleTypeData['translation']['value']) - ->setCreator($this->connectedUser->getEmail()); - $this->translationTable->save($translation, false); - - $createdScaleTypes[$labelTranslationKey] = $operationalRiskScaleType; - } elseif ($currentScaleLevelDifferenceFromExternal !== 0) { - $matchedScaleTypes[$operationalRiskScaleType->getId()] = $operationalRiskScaleType; - } - - /* The map is used to match for the importing operational risks, scale values with scale types. */ - $this->cachedData['operationalRiskScaleTypes']['currentScaleTypeLabelTranslationKeyToExternalIds'] - [$operationalRiskScaleType->getLabelTranslationKey()] = $scaleTypeData['id']; - - $operationalRiskScaleType->setIsHidden($scaleTypeData['isHidden']); - $this->operationalRiskScaleTypeTable->save($operationalRiskScaleType, false); - - foreach ($scaleTypeData['operationalRiskScaleComments'] as $scaleTypeCommentData) { - $this->createOrUpdateOperationalRiskScaleComment( - $anr, - $isScaleTypeMatched, - $operationalRiskScale, - $scaleTypeCommentData, - $operationalRiskScaleType->getOperationalRiskScaleComments(), - $scalesTranslations, - $operationalRiskScaleType - ); - } - } - - /* Create relations of all the created scales with existed risks. */ - if (!empty($createdScaleTypes)) { - $operationalInstanceRisks = $this->instanceRiskOpTable->findByAnr($anr); - foreach ($operationalInstanceRisks as $operationalInstanceRisk) { - foreach ($createdScaleTypes as $createdScaleType) { - $operationalInstanceRiskScale = (new Entity\OperationalInstanceRiskScale()) - ->setAnr($anr) - ->setOperationalRiskScaleType($createdScaleType) - ->setOperationalInstanceRisk($operationalInstanceRisk) - ->setCreator($this->connectedUser->getEmail()); - $this->operationalInstanceRiskScaleTable->save($operationalInstanceRiskScale, false); - } - } - } - - $maxIndexForLikelihood = 0; - /* This is currently applicable only for likelihood scales type */ - foreach ($scaleData['operationalRiskScaleComments'] as $scaleCommentData) { - $this->createOrUpdateOperationalRiskScaleComment( - $anr, - true, - $operationalRiskScale, - $scaleCommentData, - $operationalRiskScale->getOperationalRiskScaleComments(), - $scalesTranslations - ); - $maxIndexForLikelihood = (int)$scaleCommentData['scaleIndex'] > $maxIndexForLikelihood - ? (int)$scaleCommentData['scaleIndex'] - : $maxIndexForLikelihood; - } - /* Manage a case when the scale (probability) is not matched and level higher than external. */ - if ($maxIndexForLikelihood !== 0 - && $operationalRiskScale->getType() === CoreEntity\OperationalRiskScaleSuperClass::TYPE_LIKELIHOOD - ) { - foreach ($operationalRiskScale->getOperationalRiskScaleComments() as $comment) { - if ($comment->getScaleIndex() >= $maxIndexForLikelihood) { - $comment->setIsHidden(true); - $this->operationalRiskScaleCommentTable->save($comment, false); - } - } - } - - /* Validate if any existed comments are now out of the new scales bound and if their values are valid. - Also, if their comments are complete per scale's level. */ - if ($currentScaleLevelDifferenceFromExternal !== 0) { - foreach ($operationalRiskScale->getOperationalRiskScaleTypes() as $operationalRiskScaleType) { - /* Ignore the currently created scale types. */ - if (\array_key_exists($operationalRiskScaleType->getLabelTranslationKey(), $createdScaleTypes)) { - continue; - } - - if ($currentScaleLevelDifferenceFromExternal < 0 - && !\array_key_exists($operationalRiskScaleType->getId(), $matchedScaleTypes) - ) { - /* 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 - [CoreEntity\OperationalRiskScaleSuperClass::TYPE_IMPACT]['commentsIndexToValueMap']; - while ($commentIndex <= $operationalRiskScale->getMax()) { - $this->createOrUpdateOperationalRiskScaleComment( - $anr, - false, - $operationalRiskScale, - [ - 'scaleIndex' => $commentIndex, - 'scaleValue' => $commentIndexToValueMap[$commentIndex], - 'isHidden' => false, - 'translation' => [ - 'value' => '', - ], - ], - [], - [], - $operationalRiskScaleType - ); - $commentIndex++; - } - - continue; - } - - if ($currentScaleLevelDifferenceFromExternal > 0) { - $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, - so we need to hide their comments and validate values. */ - foreach ($matchedScaleTypes as $matchedScaleType) { - foreach ($matchedScaleType->getOperationalRiskScaleComments() as $comment) { - $isHidden = $operationalRiskScale->getMin() > $comment->getScaleIndex() - || $operationalRiskScale->getMax() < $comment->getScaleIndex(); - $comment->setIsHidden($isHidden); - if ($isHidden && $maxValue >= $comment->getScaleValue()) { - $comment->setScaleValue(++$maxValue); - } - - $this->operationalRiskScaleCommentTable->save($comment, false); - } - } - } else { - /* Manage a case when the scale is not matched and level higher than external */ - foreach ($operationalRiskScaleType->getOperationalRiskScaleComments() as $comment) { - $isHidden = $operationalRiskScale->getMin() > $comment->getScaleIndex() - || $operationalRiskScale->getMax() < $comment->getScaleIndex(); - $comment->setIsHidden($isHidden); - if ($isHidden && $maxValue >= $comment->getScaleValue()) { - $comment->setScaleValue(++$maxValue); - } - - $this->operationalRiskScaleCommentTable->save($comment, false); - } - } - } - } - } - - $this->operationalRiskScaleTable->save($operationalRiskScale); - } - - /* Reset the cache */ - $this->cachedData['currentOperationalRiskScalesData'] = []; - } - - private function createOrUpdateOperationalRiskScaleComment( - Entity\Anr $anr, - bool $isMatchRequired, - Entity\OperationalRiskScale $operationalRiskScale, - array $scaleCommentData, - iterable $scaleCommentsToMatchWith, - array $scalesTranslations, - ?Entity\OperationalRiskScaleType $operationalRiskScaleType = null - ): void { - $operationalRiskScaleComment = null; - if ($isMatchRequired) { - $operationalRiskScaleComment = $this->matchScaleCommentDataWithScaleCommentsList( - $operationalRiskScale, - $scaleCommentData, - $scaleCommentsToMatchWith, - $scalesTranslations - ); - } - if ($operationalRiskScaleComment === null) { - $anrLanguageCode = $this->getAnrLanguageCode($anr); - $commentTranslationKey = (string)Uuid::uuid4(); - $operationalRiskScaleComment = (new Entity\OperationalRiskScaleComment()) - ->setAnr($anr) - ->setOperationalRiskScale($operationalRiskScale) - ->setCommentTranslationKey($commentTranslationKey) - ->setCreator($this->connectedUser->getEmail()); - - $translation = (new Translation()) - ->setAnr($anr) - ->setType(Translation::OPERATIONAL_RISK_SCALE_COMMENT) - ->setLang($anrLanguageCode) - ->setKey($commentTranslationKey) - ->setValue($scaleCommentData['translation']['value']) - ->setCreator($this->connectedUser->getEmail()); - $this->translationTable->save($translation, false); - } - - if ($operationalRiskScaleType !== null) { - $operationalRiskScaleComment->setOperationalRiskScaleType($operationalRiskScaleType); - } - - $operationalRiskScaleComment - ->setScaleIndex($scaleCommentData['scaleIndex']) - ->setScaleValue($scaleCommentData['scaleValue']) - ->setIsHidden($scaleCommentData['isHidden']); - $this->operationalRiskScaleCommentTable->save($operationalRiskScaleComment, false); - } - - /** - * @param array $scaleTypeData - * @param Entity\OperationalRiskScaleType[] $operationalRiskScaleTypes - * @param Translation[] $scalesTranslations - * - * @return Entity\OperationalRiskScaleType|null + * Validates if the data can be imported into the anr. */ - private function matchScaleTypeDataWithScaleTypesList( - array $scaleTypeData, - iterable $operationalRiskScaleTypes, - array $scalesTranslations - ): ?Entity\OperationalRiskScaleType { - foreach ($operationalRiskScaleTypes as $operationalRiskScaleType) { - if (isset($scalesTranslations[$operationalRiskScaleType->getLabelTranslationKey()])) { - $translation = $scalesTranslations[$operationalRiskScaleType->getLabelTranslationKey()]; - if ($translation->getValue() === $scaleTypeData['translation']['value']) { - return $operationalRiskScaleType; - } - } - } - - return null; - } - - private function matchScaleCommentDataWithScaleCommentsList( - Entity\OperationalRiskScale $operationalRiskScale, - array $scaleTypeCommentData, - iterable $operationalRiskScaleComments, - array $scalesTranslations - ): ?Entity\OperationalRiskScaleComment { - foreach ($operationalRiskScaleComments as $operationalRiskScaleComment) { - if ($operationalRiskScale->getId() !== $operationalRiskScaleComment->getOperationalRiskScale()->getId()) { - continue; - } - if ($operationalRiskScaleComment->getScaleIndex() === $scaleTypeCommentData['scaleIndex']) { - $translation = $scalesTranslations[$operationalRiskScaleComment->getCommentTranslationKey()]; - if ($translation->getValue() !== $scaleTypeCommentData['translation']['value']) { - /* We need to update the translation value. */ - $translation->setValue($scaleTypeCommentData['translation']['value']); - $this->translationTable->save($translation, false); - } - - return $operationalRiskScaleComment; - } - } - - return null; - } - - private function getAnrLanguageCode(Anr $anr): string - { - return strtolower($this->configService->getLanguageCodes()[$anr->getLanguage()]); - } - - private function createInstanceMetadata(Entity\Instance $instance, $data): void - { - $anr = $instance->getAnr(); - $anrLanguageCode = $this->getAnrLanguageCode($anr); - //fetch translations - $instanceMetadataTranslations = $this->translationTable->findByAnrTypesAndLanguageIndexedByKey( - $anr, - [Translation::INSTANCE_METADATA], - $anrLanguageCode - ); - if (isset($data['instancesMetadatas'])) { - if (!isset($this->cachedData['anrMetadataOnInstances'])) { - $this->cachedData['anrMetadataOnInstances'] = - $this->getCurrentAnrMetadataOnInstances($anr); - } - // initialize the metadatas labels - $labels = array_column($this->cachedData['anrMetadataOnInstances'], 'label'); - foreach ($data['instancesMetadatas'] as $instanceMetadata) { - if (!in_array($instanceMetadata['label'], $labels)) { - $this->createAnrMetadataOnInstances($anr, [$instanceMetadata]); - //update after insertion - $labels = array_column($this->cachedData['anrMetadataOnInstances'], 'label'); - } - // if the metadata exist we can create/update the instanceMetadata - if (in_array($instanceMetadata['label'], $labels)) { - $indexMetadata = array_search($instanceMetadata['label'], $labels); - $metadata = $this->cachedData['anrMetadataOnInstances'][$indexMetadata]['object']; - $instanceMetadataObject = $this->instanceMetadataTable->findByInstanceAndMetadataField( - $instance, - $metadata - ); - if ($instanceMetadataObject === null) { - $commentTranslationKey = (string)Uuid::uuid4(); - $instanceMetadataObject = (new Entity\InstanceMetadata()) - ->setInstance($instance) - ->setAnrInstanceMetadataField($metadata) - ->setCommentTranslationKey($commentTranslationKey) - ->setCreator($this->connectedUser->getEmail()); - $this->instanceMetadataTable->save($instanceMetadataObject, false); - - $translation = (new Translation()) - ->setAnr($anr) - ->setType(Translation::INSTANCE_METADATA) - ->setKey($commentTranslationKey) - ->setValue($instanceMetadata['comment']) - ->setLang($anrLanguageCode) - ->setCreator($this->connectedUser->getEmail()); - $this->translationTable->save($translation, false); - $instanceMetadataTranslations[$commentTranslationKey] = $translation; - $this->updateInstanceMetadataToBrothers($instance, $instanceMetadataObject); - } else { - $commentTranslationKey = $instanceMetadataObject->getCommentTranslationKey(); - $commentTranslation = $instanceMetadataTranslations[$commentTranslationKey]; - $commentTranslation->setValue( - $commentTranslation->getValue().' '.$instanceMetadata['comment'] - ); - $this->translationTable->save($commentTranslation, false); - } - } - } - } - $this->instanceMetadataTable->flush(); - $this->updateInstanceMetadataFromBrothers($instance); - $this->instanceMetadataTable->flush(); - } - - private function createAnrMetadataOnInstances(Entity\Anr $anr, array $data): void + private function validateIfImportIsPossible(Entity\Anr $anr, ?Entity\Instance $parent, array $data): void { - $labels = array_column($this->cachedData['anrMetadataOnInstances'], 'label'); - foreach ($data as $anrInstanceMetadataData) { - if (!\in_array($anrInstanceMetadataData['label'], $labels, true)) { - $metadata = (new Entity\AnrInstanceMetadataField()) - ->setAnr($anr) - ->setLabel($anrInstanceMetadataData['label']) - ->setCreator($this->connectedUser->getEmail()) - ->setIsDeletable(true); - $this->anrInstanceMetadataFieldTable->save($metadata, false); - - // TODO: simplify the cache by $this->cachedData['anrMetadataOnInstances'][$anrInstanceMetadataData['id']]] = $metadata; - $this->cachedData['anrMetadataOnInstances'][] = [ - 'id' => $metadata->getId(), - 'label' => $anrInstanceMetadataData['label'], - 'object' => $metadata, - ]; - } + if ($parent !== null + && ($parent->getLevel() === CoreEntity\InstanceSuperClass::LEVEL_INTER || $parent->getAnr() !== $anr) + ) { + throw new Exception('Parent instance should be in the node tree and the analysis IDs are matched', 412); } - $this->anrInstanceMetadataFieldTable->flush(); - } - - private function getCurrentAnrMetadataOnInstances(Entity\Anr $anr): array - { - $this->cachedData['currentAnrMetadataOnInstances'] = []; - $anrMetadatasOnInstancesTranslations = $this->translationTable->findByAnrTypesAndLanguageIndexedByKey( - $anr, - [Translation::ANR_METADATAS_ON_INSTANCES], - $this->getAnrLanguageCode($anr) - ); - $anrInstanceMetadataFields = $this->anrInstanceMetadataFieldTable->findByAnr($anr); - foreach ($anrInstanceMetadataFields as $metadataField) { - $translationLabel = $anrMetadatasOnInstancesTranslations[$metadataField->getLabelTranslationKey()] ?? null; - $this->cachedData['currentAnrMetadataOnInstances'][] = [ - 'id' => $metadataField->getId(), - 'label' => $translationLabel !== null ? $translationLabel->getValue() : '', - 'object' => $metadataField, - 'translation' => $translationLabel, - ]; + if ((!empty($data['with_eval']) || !empty($data['withEval'])) && empty($data['scales'])) { + throw new Exception('The importing file should include evaluation scales.', 412); } - return $this->cachedData['currentAnrMetadataOnInstances'] ?? []; - } - /** - * Updates the instance impacts from brothers for global assets. - */ - private function updateInstanceMetadataFromBrothers(Entity\Instance $instance): void - { - if ($instance->getObject()->isScopeGlobal()) { - $instanceBrothers = $this->getInstanceBrothers($instance); - if (!empty($instanceBrothers)) { - // Update instanceMetadata of $instance. We use only one brother as the instanceMetadatas are the same. - $instanceBrother = current($instanceBrothers); - $instancesMetadatasFromBrother = $instanceBrother->getInstanceMetadata(); - foreach ($instancesMetadatasFromBrother as $instanceMetadataFromBrother) { - $metadata = $instanceMetadataFromBrother->getAnrInstanceMetadataFields(); - $instanceMetadata = $this->instanceMetadataTable - ->findByInstanceAndMetadataField($instance, $metadata); - if ($instanceMetadata === null) { - $instanceMetadata = (new Entity\InstanceMetadata()) - ->setInstance($instance) - ->setAnrInstanceMetadataField($metadata) - ->setCommentTranslationKey($instanceMetadataFromBrother->getCommentTranslationKey()) - ->setCreator($this->connectedUser->getEmail()); - $this->instanceMetadataTable->save($instanceMetadata, false); - } - } - } + 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); } } /** - * Updates the instance impacts from instance to Brothers for global assets. + * @throws Exception */ - private function updateInstanceMetadataToBrothers( - Entity\Instance $instance, - Entity\InstanceMetadata $instanceMetadata - ): void { - if ($instance->getObject()->isScopeGlobal()) { - $instanceBrothers = $this->getInstanceBrothers($instance); - if (!empty($instanceBrothers)) { - foreach ($instanceBrothers as $instanceBrother) { - $metadata = $instanceMetadata->getAnrInstanceMetadataFields(); - $instanceMetadataBrother = $this->instanceMetadataTable - ->findByInstanceAndMetadataField($instanceBrother, $metadata); - if ($instanceMetadataBrother === null) { - $instanceMetadataBrother = (new Entity\InstanceMetadata()) - ->setInstance($instanceBrother) - ->setAnrInstanceMetadataField($metadata) - ->setCommentTranslationKey($instanceMetadata->getCommentTranslationKey()) - ->setCreator($this->connectedUser->getEmail()); - $this->instanceMetadataTable->save($instanceMetadataBrother, false); - } - } - } - } - } - - private function getCurrentSoaScaleCommentData(Entity\Anr $anr): array + private function setAndValidateMonarcVersion($data): void { - if (empty($this->cachedData['currentSoaScaleCommentData'])) { - $scales = $this->soaScaleCommentTable->findByAnrOrderByIndex($anr); - foreach ($scales as $scale) { - if (!$scale->isHidden()) { - $this->cachedData['currentSoaScaleCommentData'][$scale->getScaleIndex()] = [ - 'scaleIndex' => $scale->getScaleIndex(), - 'isHidden' => $scale->isHidden(), - 'colour' => $scale->getColour(), - 'object' => $scale, - ]; - } - } + if (isset($data['monarc_version'])) { + $this->monarcVersion = strpos($data['monarc_version'], 'master') === false ? $data['monarc_version'] : '99'; } - return $this->cachedData['currentSoaScaleCommentData'] ?? []; - } - - private function mergeSoaScaleComment(array $newScales, Entity\Anr $anr) - { - $soaScaleCommentTranslations = $this->translationTable->findByAnrTypesAndLanguageIndexedByKey( - $anr, - [CoreEntity\TranslationSuperClass::SOA_SCALE_COMMENT], - $this->getAnrLanguageCode($anr) - ); - $scales = $this->soaScaleCommentTable->findByAnrIndexedByScaleIndex($anr); - // we have scales to create - if (\count($newScales) > \count($scales)) { - $anrLanguageCode = $this->getAnrLanguageCode($anr); - for ($i = \count($scales); $i < \count($newScales); $i++) { - // todo: no translations anymore ! -// $translationKey = (string)Uuid::uuid4(); -// $translation = (new Translation()) -// ->setAnr($anr) -// ->setType(TranslationSuperClass::SOA_SCALE_COMMENT) -// ->setKey($translationKey) -// ->setValue('') -// ->setLang($anrLanguageCode) -// ->setCreator($this->connectedUser->getEmail()); -// $this->translationTable->save($translation, false); -// $soaScaleCommentTranslations[$translationKey] = $translation; - - $scales[$i] = (new Entity\SoaScaleComment()) - ->setScaleIndex($i) - ->setAnr($anr) - ->setCommentTranslationKey($translationKey) - ->setCreator($this->connectedUser->getEmail()); - $this->soaScaleCommentTable->save($scales[$i], false); - } - } - //we have scales to hide - if (\count($newScales) < \count($scales)) { - for ($i = \count($newScales); $i < \count($scales); $i++) { - $scales[$i]->setIsHidden(true); - $this->soaScaleCommentTable->save($scales[$i], false); - } - } - //we process the scales - foreach ($newScales as $id => $newScale) { - $scales[$newScale['scaleIndex']] - ->setColour($newScale['colour']) - ->setIsHidden($newScale['isHidden']); - $this->soaScaleCommentTable->save($scales[$newScale['scaleIndex']], false); - - $translationKey = $scales[$newScale['scaleIndex']]->getCommentTranslationKey(); - $translation = $soaScaleCommentTranslations[$translationKey]; - $translation->setValue($newScale['comment']); - - $this->translationTable->save($translation, false); - - $this->importCacheHelper->addItemToArrayCache( - 'newSoaScaleCommentIndexedByScale', - $scales[$newScale['scaleIndex']], - $newScale['scaleIndex'] - ); - $this->importCacheHelper - ->addItemToArrayCache('soaScaleCommentExternalIdMapToNewObject', $scales[$newScale['scaleIndex']], $id); + if ($this->isMonarcVersionLowerThen('2.8.2')) { + throw new Exception('Import of files exported from MONARC v2.8.1 or lower are not supported.' + . ' Please contact us for more details.'); } - $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 + private function isMonarcVersionLowerThen(string $version): bool { - 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); - } - } + return version_compare($this->monarcVersion, $version) < 0; } } diff --git a/src/Import/Service/ObjectImportService.php b/src/Import/Service/ObjectImportService.php index f81ed15f..ecf6e547 100644 --- a/src/Import/Service/ObjectImportService.php +++ b/src/Import/Service/ObjectImportService.php @@ -1,7 +1,7 @@ monarcObjectTable = $monarcObjectTable; - $this->objectObjectTable = $objectObjectTable; - $this->assetImportService = $assetImportService; - $this->rolfTagTable = $rolfTagTable; - $this->rolfRiskTable = $rolfRiskTable; - $this->measureTable = $measureTable; - $this->referentialTable = $referentialTable; - $this->objectCategoryTable = $objectCategoryTable; $this->connectedUser = $connectedUserService->getConnectedUser(); - $this->importCacheHelper = $importCacheHelper; - $this->soaCategoryService = $soaCategoryService; } public function importFromArray( array $data, Entity\Anr $anr, - string $modeImport = self::IMPORT_MODE_MERGE + string $importMode = self::IMPORT_MODE_MERGE ): ?Entity\MonarcObject { if (!isset($data['type'], $data['object']) || $data['type'] !== 'object') { return null; @@ -108,12 +79,9 @@ public function importFromArray( $objectScope = (int)$objectData['scope']; $nameFiledKey = 'name' . $anr->getLanguage(); $monarcObject = null; - if ($objectScope === ObjectSuperClass::SCOPE_LOCAL - || ( - $objectScope === ObjectSuperClass::SCOPE_GLOBAL - && $modeImport === self::IMPORT_MODE_MERGE - ) - ) { + if ($objectScope === ObjectSuperClass::SCOPE_LOCAL || ( + $objectScope === ObjectSuperClass::SCOPE_GLOBAL && $importMode === self::IMPORT_MODE_MERGE + )) { $monarcObject = $this->monarcObjectTable->findOneByAnrAssetNameScopeAndCategory( $anr, $nameFiledKey, @@ -123,7 +91,8 @@ public function importFromArray( $objectCategory ); if ($monarcObject !== null) { - $this->objectObjectTable->deleteAllByParent($monarcObject); + // TODO: should be dropped and replaced by the use of the processor. + $this->objectObjectTable->deleteLinksByParentObject($monarcObject); } } @@ -137,34 +106,23 @@ public function importFromArray( ->setMode($objectData['mode'] ?? 1) ->setScope($objectData['scope']) ->setLabels([$labelKey => $objectData[$labelKey]]) - ->setPosition((int)$objectData['position']) ->setCreator($this->connectedUser->getEmail()); try { - $this->monarcObjectTable->find($anr, $objectData['uuid']); - } catch (EntityNotFoundException $e) { + $this->monarcObjectTable->findByUuidAndAnr($objectData['uuid'], $anr); + } catch (EntityNotFoundException) { $monarcObject->setUuid($objectData['uuid']); } $this->setMonarcObjectName($monarcObject, $objectData, $nameFiledKey); } - $monarcObject->addAnr($anr); - $this->monarcObjectTable->save($monarcObject); $this->importCacheHelper->addItemToArrayCache('objects', $monarcObject, $monarcObject->getUuid()); if (!empty($data['children'])) { - usort($data['children'], static function ($a, $b) { - if (isset($a['object']['position'], $b['object']['position'])) { - return $a['object']['position'] <=> $b['object']['position']; - } - - return 0; - }); - foreach ($data['children'] as $childObjectData) { - $childMonarcObject = $this->importFromArray($childObjectData, $anr, $modeImport); + $childMonarcObject = $this->importFromArray($childObjectData, $anr, $importMode); if ($childMonarcObject !== null) { $maxPosition = $this->objectObjectTable->findMaxPosition([ 'anr' => $anr, @@ -211,7 +169,7 @@ private function importObjectCategories( $maxPosition = $this->objectCategoryTable->findMaxPositionByAnrAndParent($anr, $parentCategory); $rootCategory = null; if ($parentCategory !== null) { - $rootCategory = $parentCategory->getRoot() ?? $parentCategory; + $rootCategory = $parentCategory->getRootCategory(); } $objectCategory = (new Entity\ObjectCategory()) ->setAnr($anr) @@ -301,7 +259,7 @@ private function setMonarcObjectName( $objectData[$nameFiledKey] ); if ($existedObject !== null) { - if (strpos($objectData[$nameFiledKey], ' - Imp. #') !== false) { + if (str_contains($objectData[$nameFiledKey], ' - Imp. #')) { $objectData[$nameFiledKey] = preg_replace('/#\d+/', '#' . $index, $objectData[$nameFiledKey]); } else { $objectData[$nameFiledKey] .= ' - Imp. #' . $index; @@ -316,7 +274,7 @@ private function setMonarcObjectName( private function getMonarcVersion(array $data): string { if (isset($data['monarc_version'])) { - return strpos($data['monarc_version'], 'master') === false ? $data['monarc_version'] : '99'; + return str_contains($data['monarc_version'], 'master') ? '999' : $data['monarc_version']; } return '1'; @@ -335,9 +293,11 @@ private function validateMonarcVersion(array $data): void } } + // TODO: use the ReferentialImportProcessor. private function processMeasuresAndReferentialData(Entity\Anr $anr, Entity\RolfRisk $rolfRisk, array $measuresData): void { $labelKey = 'label' . $anr->getLanguage(); + $this->referentialImportProcessor->prepareSoaCategoriesCache($anr); foreach ($measuresData as $measureData) { /* Backward compatibility. Prior v2.10.3 measures data were not exported. */ $measureUuid = $measureData['uuid'] ?? $measureData; @@ -361,12 +321,16 @@ private function processMeasuresAndReferentialData(Entity\Anr $anr, Entity\RolfR $this->importCacheHelper->addItemToArrayCache('referentials', $referential, $referentialUuid); } - $soaCategory = $this->soaCategoryService->getOrCreateSoaCategory( - $this->importCacheHelper, - $anr, - $referential, - $measureData['category'][$labelKey] ?? '' - ); + $soaCategory = null; + if (!empty($measureData['category'][$labelKey])) { + $soaCategory = $this->referentialImportProcessor->processSoaCategoryData( + $anr, + $referential, + [ + 'category' => ['label' => $measureData['category'][$labelKey]], + ] + ); + } $measure = (new Entity\Measure()) ->setAnr($anr) diff --git a/src/Import/Traits/EvaluationConverterTrait.php b/src/Import/Traits/EvaluationConverterTrait.php new file mode 100644 index 00000000..d3cd650b --- /dev/null +++ b/src/Import/Traits/EvaluationConverterTrait.php @@ -0,0 +1,41 @@ + [ + 'fieldName' => 'measure.referential.uuid', + 'relationConditions' => [ + 'measure.anr = :anr', + 'referential.anr = :anr', + ], + ], + 'category' => [ + 'fieldName' => 'measure.category', + ], + ]; + + protected static array $ignoredFilterFieldValues = ['category' => 0]; + + protected static array $orderParamsToFieldsMap = [ + 'm.code' => 'measure.code|LENGTH,measure.code', + ]; +} diff --git a/src/Model/Table/QuestionChoiceTable.php b/src/Model/Table/QuestionChoiceTable.php index b5ce9563..f57ba44a 100755 --- a/src/Model/Table/QuestionChoiceTable.php +++ b/src/Model/Table/QuestionChoiceTable.php @@ -22,4 +22,13 @@ public function __construct(DbCli $dbService, ConnectedUserService $connectedUse { parent::__construct($dbService, QuestionChoice::class, $connectedUserService); } + + public function saveEntity(QuestionChoice $question, bool $flushAll = true): void + { + $em = $this->getDb()->getEntityManager(); + $em->persist($question); + if ($flushAll) { + $em->flush(); + } + } } diff --git a/src/Model/Table/SoaTable.php b/src/Model/Table/SoaTable.php deleted file mode 100755 index ceee37a6..00000000 --- a/src/Model/Table/SoaTable.php +++ /dev/null @@ -1,100 +0,0 @@ -getRepository() - ->createQueryBuilder('s') - ->where('s.anr = :anr') - ->setParameter('anr', $anr) - ->getQuery() - ->getResult(); - } - - public function saveEntity(Soa $soa, bool $flushAll = true): void - { - $em = $this->getDb()->getEntityManager(); - $em->persist($soa); - if ($flushAll) { - $em->flush(); - } - } - - /** - * @return Soa[] - */ - public function findByAnrAndSoaCategory(Anr $anr, SoaCategory $soaCategory, array $order = []) - { - $queryBuilder = $this->getRepository() - ->createQueryBuilder('s') - ->innerJoin('s.measure', 'm') - ->where('s.anr = :anr') - ->andWhere('m.category = :category') - ->setParameter('anr', $anr) - ->setParameter('category', $soaCategory); - - if (!empty($order)) { - foreach ($order as $filed => $direction) { - $queryBuilder->addOrderBy($filed, $direction); - } - } - - return $queryBuilder->getQuery()->getResult(); - } - - - public function findByAnrAndMeasureUuid(Anr $anr, string $measureUuid): ?Soa - { - return $this->getRepository() - ->createQueryBuilder('s') - ->innerJoin('s.measure', 'm') - ->where('s.anr = :anr') - ->andWhere('m.uuid = :measure_uuid') - ->andWhere('m.anr = :anr') - ->setParameter('anr', $anr) - ->setParameter('measure_uuid', $measureUuid) - ->setMaxResults(1) - ->getQuery() - ->getOneOrNullResult(); - } - - public function findById(int $id): ?Soa - { - return $this->getRepository() - ->createQueryBuilder('s') - ->where('s.id = :id') - ->setParameter('id', $id) - ->getQuery() - ->getOneOrNullResult(); - } -} diff --git a/src/Service/AnrAmvService.php b/src/Service/AnrAmvService.php index f7907ae6..6bf73df8 100755 --- a/src/Service/AnrAmvService.php +++ b/src/Service/AnrAmvService.php @@ -46,10 +46,8 @@ public function __construct( public function getList(FormattedInputParams $params): array { $result = []; - - /** @var Entity\Amv[] $amvs */ - $amvs = $this->amvTable->findByParams($params); - foreach ($amvs as $amv) { + /** @var Entity\Amv $amv */ + foreach ($this->amvTable->findByParams($params) as $amv) { $result[] = $this->prepareAmvDataResult($amv); } diff --git a/src/Service/AnrAssetService.php b/src/Service/AnrAssetService.php index 04539744..ed950ef9 100755 --- a/src/Service/AnrAssetService.php +++ b/src/Service/AnrAssetService.php @@ -27,10 +27,8 @@ public function __construct( public function getList(FormattedInputParams $params): array { $result = []; - - /** @var Entity\Asset[] $assets */ - $assets = $this->assetTable->findByParams($params); - foreach ($assets as $asset) { + /** @var Entity\Asset $asset */ + foreach ($this->assetTable->findByParams($params) as $asset) { $result[] = $this->prepareAssetDataResult($asset); } diff --git a/src/Service/AnrInstanceConsequenceService.php b/src/Service/AnrInstanceConsequenceService.php index 6294833c..3f62ea2b 100644 --- a/src/Service/AnrInstanceConsequenceService.php +++ b/src/Service/AnrInstanceConsequenceService.php @@ -83,9 +83,11 @@ public function createInstanceConsequences( if ($siblingInstance !== null) { $instancesConsequences = $this->instanceConsequenceTable->findByAnrAndInstance($anr, $siblingInstance); foreach ($instancesConsequences as $instanceConsequence) { + /** @var Entity\ScaleImpactType $scalesImpactType */ + $scalesImpactType = $instanceConsequence->getScaleImpactType(); $this->createInstanceConsequence( $instance, - $instanceConsequence->getScaleImpactType(), + $scalesImpactType, $instanceConsequence->isHidden(), [ 'confidentiality' => $instanceConsequence->getConfidentiality(), @@ -95,6 +97,7 @@ public function createInstanceConsequences( ); } } else { + /** @var Entity\ScaleImpactType $scalesImpactType */ foreach ($this->scalesCacheHelper->getCachedScaleImpactTypes($anr) as $scalesImpactType) { if (!\in_array( $scalesImpactType->getType(), @@ -118,7 +121,6 @@ public function createInstanceConsequence( array $evaluationCriteria = [], bool $saveInTheDb = false ): Entity\InstanceConsequence { - /** @var Entity\InstanceConsequence $instanceConsequence */ $instanceConsequence = (new Entity\InstanceConsequence()) ->setAnr($instance->getAnr()) ->setInstance($instance) diff --git a/src/Service/AnrInstanceMetadataFieldService.php b/src/Service/AnrInstanceMetadataFieldService.php index bbfc73f8..f5f66619 100644 --- a/src/Service/AnrInstanceMetadataFieldService.php +++ b/src/Service/AnrInstanceMetadataFieldService.php @@ -26,41 +26,30 @@ public function __construct( $this->connectedUser = $connectedUserService->getConnectedUser(); } - public function getList(Anr $anr): array - { - $result = []; - /** @var AnrInstanceMetadataField $metadataField */ - foreach ($this->anrInstanceMetadataFieldTable->findByAnr($anr) as $index => $metadataField) { - $result[] = [ - 'id' => $metadataField->getId(), - 'index' => $index + 1, - $anr->getLanguageCode() => $metadataField->getLabel(), - ]; - } - - return $result; - } - - public function getAnrInstanceMetadataFieldData(Anr $anr, int $id): array + public function create(Anr $anr, array $data, bool $saveInDb = true): AnrInstanceMetadataField { - /** @var AnrInstanceMetadataField $metadataField */ - $metadataField = $this->anrInstanceMetadataFieldTable->findByIdAndAnr($id, $anr); + $metadataFieldData = current($data); - return [ - 'id' => $metadataField->getId(), - $anr->getLanguageCode() => $metadataField->getLabel(), - ]; + return $this->createAnrInstanceMetadataField( + $anr, + $metadataFieldData[$anr->getLanguageCode()], + isset($metadataFieldData['isDeletable']) ? (bool)$metadataFieldData['isDeletable'] : null, + $saveInDb + ); } - public function create(Anr $anr, array $data, bool $saveInDb = true): AnrInstanceMetadataField - { - $metadataFieldData = current($data); + public function createAnrInstanceMetadataField( + Anr $anr, + string $label, + ?bool $isDeletable, + bool $saveInDb + ): AnrInstanceMetadataField { $metadataField = (new AnrInstanceMetadataField()) - ->setLabel($metadataFieldData[$anr->getLanguageCode()]) + ->setLabel($label) ->setAnr($anr) ->setCreator($this->connectedUser->getEmail()); - if (isset($metadataFieldData['isDeletable'])) { - $metadataField->setIsDeletable((bool)$metadataFieldData['isDeletable']); + if ($isDeletable !== null) { + $metadataField->setIsDeletable($isDeletable); } $this->anrInstanceMetadataFieldTable->save($metadataField, $saveInDb); diff --git a/src/Service/AnrInstanceService.php b/src/Service/AnrInstanceService.php index cb61e0ee..64d171fd 100755 --- a/src/Service/AnrInstanceService.php +++ b/src/Service/AnrInstanceService.php @@ -73,6 +73,39 @@ public function getInstanceData(Entity\Anr $anr, int $id): array public function instantiateObjectToAnr(Entity\Anr $anr, array $data, bool $isRootLevel = false): Entity\Instance { + $instance = $this->createInstance($anr, $data, $isRootLevel); + + $this->createInstanceMetadataFromGlobalSibling($instance); + + /** @var Entity\MonarcObject $object */ + $object = $instance->getObject(); + $this->anrInstanceConsequenceService->createInstanceConsequences($instance, $anr, $object, false); + $instance->updateImpactBasedOnConsequences()->refreshInheritedImpact(); + + $this->instanceTable->save($instance, false); + + $this->anrInstanceRiskService->createInstanceRisks($instance, $object); + + $this->anrInstanceRiskOpService->createInstanceRisksOp($instance, $object); + + /* Check if the root element is not the same as current child element to avoid a circular dependency. */ + if ($instance->isRoot() + || !$instance->hasParent() + || $instance->getParent()->isRoot() + || $instance->getParent()->getRoot()->getObject()->getUuid() !== $instance->getObject()->getUuid() + ) { + $this->createChildren($instance); + } + + return $instance; + } + + public function createInstance( + Entity\Anr $anr, + array $data, + bool $isRootLevel, + bool $saveInDb = true + ): Entity\Instance { /** @var Entity\MonarcObject $object */ $object = $data['object'] instanceof Entity\MonarcObject ? $data['object'] @@ -85,6 +118,26 @@ public function instantiateObjectToAnr(Entity\Anr $anr, array $data, bool $isRoo ->setNames($object->getNames()) ->setLabels($object->getLabels()) ->setCreator($this->connectedUser->getEmail()); + if (isset($instanceData['c']) || isset($instanceData['confidentiality'])) { + $instance->setConfidentiality((int)($instanceData['c'] ?? $instanceData['confidentiality'])); + } + if (isset($instanceData['i']) || isset($instanceData['integrity'])) { + $instance->setIntegrity((int)($instanceData['i'] ?? $instanceData['integrity'])); + } + if (isset($instanceData['d']) || isset($instanceData['availability'])) { + $instance->setAvailability((int)($instanceData['d'] ?? $instanceData['availability'])); + } + if (isset($instanceData['ch']) || isset($instanceData['isConfidentialityInherited'])) { + $instance->setInheritedConfidentiality( + (int)($instanceData['ch'] ?? $instanceData['isConfidentialityInherited']) + ); + } + if (isset($instanceData['ih']) || isset($instanceData['isIntegrityInherited'])) { + $instance->setInheritedIntegrity((int)($instanceData['ih'] ?? $instanceData['isIntegrityInherited'])); + } + if (isset($instanceData['dh']) || isset($instanceData['isAvailabilityInherited'])) { + $instance->setInheritedAvailability((int)($instanceData['dh'] ?? $instanceData['isAvailabilityInherited'])); + } if (!empty($data['parent'])) { /** @var Entity\Instance $parentInstance */ @@ -95,7 +148,11 @@ public function instantiateObjectToAnr(Entity\Anr $anr, array $data, bool $isRoo $instance->setParent($parentInstance)->setRoot($parentInstance->getRootInstance()); } - $this->updateInstanceLevels($isRootLevel, $instance); + if (!$isRootLevel && isset($data['level'])) { + $instance->setLevel($data['level']); + } else { + $this->updateInstanceLevels($isRootLevel, $instance); + } $this->updatePositions( $instance, @@ -103,27 +160,7 @@ public function instantiateObjectToAnr(Entity\Anr $anr, array $data, bool $isRoo $this->getPreparedPositionData($this->instanceTable, $instance, $data) ); - $this->instanceTable->save($instance); - - $this->createInstanceMetadataFromGlobalSibling($instance); - - $this->anrInstanceConsequenceService->createInstanceConsequences($instance, $anr, $object, false); - $instance->updateImpactBasedOnConsequences()->refreshInheritedImpact(); - - $this->instanceTable->save($instance, false); - - $this->anrInstanceRiskService->createInstanceRisks($instance, $object); - - $this->anrInstanceRiskOpService->createInstanceRisksOp($instance, $object); - - /* Check if the root element is not the same as current child element to avoid a circular dependency. */ - if ($instance->isRoot() - || !$instance->hasParent() - || $instance->getParent()->isRoot() - || $instance->getParent()->getRoot()->getObject()->getUuid() !== $instance->getObject()->getUuid() - ) { - $this->createChildren($instance); - } + $this->instanceTable->save($instance, $saveInDb); return $instance; } @@ -340,8 +377,6 @@ private function getPreparedInstanceData(Entity\Instance $instance): array 'root' => $instance->isRoot() ? null : $instance->getRootInstance(), 'parent' => $instance->hasParent() ? $instance->getParent() : null, 'level' => $instance->getLevel(), - 'assetType' => $instance->getAssetType(), - 'exportable' => $instance->getExportable(), 'c' => $instance->getConfidentiality(), 'i' => $instance->getIntegrity(), 'd' => $instance->getAvailability(), diff --git a/src/Service/AnrMeasureService.php b/src/Service/AnrMeasureService.php index bef5a994..2dcfe988 100755 --- a/src/Service/AnrMeasureService.php +++ b/src/Service/AnrMeasureService.php @@ -30,10 +30,9 @@ public function getList(FormattedInputParams $params): array { $result = []; - /** @var Entity\Measure[] $measures */ - $measures = $this->measureTable->findByParams($params); $includeLinks = $params->hasFilterFor('includeLinks') && $params->getFilterFor('includeLinks')['value']; - foreach ($measures as $measure) { + /** @var Entity\Measure $measure */ + foreach ($this->measureTable->findByParams($params) as $measure) { $result[] = $this->prepareMeasureDataResult($measure, $includeLinks); } diff --git a/src/Service/AnrObjectCategoryService.php b/src/Service/AnrObjectCategoryService.php index d02c42f4..cd665ec3 100644 --- a/src/Service/AnrObjectCategoryService.php +++ b/src/Service/AnrObjectCategoryService.php @@ -78,9 +78,8 @@ public function getList(FormattedInputParams $formattedInputParams) } $categoriesData = []; - /** @var Entity\ObjectCategory[] $objectCategories */ - $objectCategories = $this->objectCategoryTable->findByParams($formattedInputParams); - foreach ($objectCategories as $objectCategory) { + /** @var Entity\ObjectCategory $objectCategory */ + foreach ($this->objectCategoryTable->findByParams($formattedInputParams) as $objectCategory) { $categoriesData[] = $this->getPreparedObjectCategoryData($objectCategory, $includeChildren); } diff --git a/src/Service/AnrObjectService.php b/src/Service/AnrObjectService.php index b8af19dc..c62357ed 100755 --- a/src/Service/AnrObjectService.php +++ b/src/Service/AnrObjectService.php @@ -37,11 +37,9 @@ public function __construct( public function getList(FormattedInputParams $formattedInputParams): array { - /** @var Entity\MonarcObject[] $objects */ - $objects = $this->monarcObjectTable->findByParams($formattedInputParams); - $result = []; - foreach ($objects as $object) { + /** @var Entity\MonarcObject $object */ + foreach ($this->monarcObjectTable->findByParams($formattedInputParams) as $object) { $result[] = $this->getPreparedObjectData($object); } diff --git a/src/Service/AnrRecommendationRiskService.php b/src/Service/AnrRecommendationRiskService.php index fdff4b0e..8b2f6dba 100755 --- a/src/Service/AnrRecommendationRiskService.php +++ b/src/Service/AnrRecommendationRiskService.php @@ -40,13 +40,11 @@ public function __construct( public function getList(FormattedInputParams $formattedInputParams): array { - /** @var Entity\RecommendationRisk[] $recommendationRisks */ - $recommendationRisks = $this->recommendationRiskTable->findByParams($formattedInputParams); - $hasRecommendationFilter = $formattedInputParams->hasFilterFor('recommendation'); $recommendationRisksData = []; $globalObjectsUuids = []; - foreach ($recommendationRisks as $recommendationRisk) { + /** @var Entity\RecommendationRisk $recommendationRisk */ + foreach ($this->recommendationRiskTable->findByParams($formattedInputParams) as $recommendationRisk) { if ($hasRecommendationFilter && $recommendationRisk->getGlobalObject() !== null && isset($globalObjectsUuids[$recommendationRisk->getGlobalObject()->getUuid()]) @@ -56,7 +54,7 @@ public function getList(FormattedInputParams $formattedInputParams): array $recommendationRisksData[] = $this->getPreparedRecommendationRiskData( $recommendationRisk, - $formattedInputParams->hasFilterFor('recommendation') + $hasRecommendationFilter ); if ($hasRecommendationFilter && $recommendationRisk->getGlobalObject() !== null) { diff --git a/src/Service/AnrRecommendationService.php b/src/Service/AnrRecommendationService.php index 548f2442..f5d76f2c 100755 --- a/src/Service/AnrRecommendationService.php +++ b/src/Service/AnrRecommendationService.php @@ -34,11 +34,9 @@ public function __construct( public function getList(FormattedInputParams $formattedInputParams): array { - /** @var Entity\Recommendation[] $recommendations */ - $recommendations = $this->recommendationTable->findByParams($formattedInputParams); - $recommendationsData = []; - foreach ($recommendations as $recommendation) { + /** @var Entity\Recommendation $recommendation */ + foreach ($this->recommendationTable->findByParams($formattedInputParams) as $recommendation) { $recommendationsData[] = $this->getPreparedRecommendationData($recommendation); } diff --git a/src/Service/AnrRecordInternationalTransferServiceFactory.php b/src/Service/AnrRecordInternationalTransferServiceFactory.php index ffa96542..e74318ac 100644 --- a/src/Service/AnrRecordInternationalTransferServiceFactory.php +++ b/src/Service/AnrRecordInternationalTransferServiceFactory.php @@ -10,6 +10,8 @@ use Monarc\Core\Service\AbstractServiceFactory; use Monarc\FrontOffice\Table\UserAnrTable; use Monarc\FrontOffice\Table\AnrTable; +use Monarc\FrontOffice\Entity\RecordInternationalTransfer; +use Monarc\FrontOffice\Model\Table\RecordInternationalTransferTable; /** * Record International Transfer Service Factory @@ -20,8 +22,8 @@ class AnrRecordInternationalTransferServiceFactory extends AbstractServiceFactory { protected $ressources = [ - 'table' => 'Monarc\FrontOffice\Model\Table\RecordInternationalTransferTable', - 'entity' => 'Monarc\FrontOffice\Entity\RecordInternationalTransfer', + 'table' => RecordInternationalTransferTable::class, + 'entity' => RecordInternationalTransfer::class, 'userAnrTable' => UserAnrTable::class, 'anrTable' => AnrTable::class, ]; diff --git a/src/Service/AnrRecordService.php b/src/Service/AnrRecordService.php index 93d99faf..0d48a99c 100644 --- a/src/Service/AnrRecordService.php +++ b/src/Service/AnrRecordService.php @@ -10,7 +10,6 @@ use Monarc\Core\Exception\Exception; use Monarc\Core\Helper\EncryptDecryptHelperTrait; use Monarc\Core\Service\AbstractService; -use Monarc\FrontOffice\Entity\Anr; use Monarc\FrontOffice\Entity\Record; use Monarc\FrontOffice\Entity\RecordInternationalTransfer; use Monarc\FrontOffice\Entity\RecordPersonalData; @@ -25,9 +24,18 @@ class AnrRecordService extends AbstractService { use EncryptDecryptHelperTrait; - protected $dependencies = [ 'anr', 'controller', 'representative', 'dpo', 'jointControllers', - 'personalData', 'internationalTransfers', 'processors', 'recipients']; - protected $filterColumns = [ 'label' ]; + protected $dependencies = [ + 'anr', + 'controller', + 'representative', + 'dpo', + 'jointControllers', + 'personalData', + 'internationalTransfers', + 'processors', + 'recipients', + ]; + protected $filterColumns = ['label']; protected $recordActorService; protected $recordProcessorService; protected $recordRecipientService; @@ -49,49 +57,49 @@ public function deleteRecord($id) $actorsToCheck = []; $processorsToCheck = []; $recipientsToCheck = []; - if($entity->controller) { + if ($entity->controller) { $actorsToCheck[] = $entity->controller->id; } - if($entity->dpo) { + if ($entity->dpo) { $actorsToCheck[] = $entity->dpo->id; } - if($entity->representative) { + if ($entity->representative) { $actorsToCheck[] = $entity->representative->id; } - foreach($entity->jointControllers as $jc) { + foreach ($entity->jointControllers as $jc) { $actorsToCheck[] = $jc->id; } - foreach($entity->processors as $p) { + foreach ($entity->processors as $p) { $processorsToCheck[] = $p->id; } - foreach($entity->recipients as $r) { + foreach ($entity->recipients as $r) { $recipientsToCheck[] = $r->id; } - foreach($entity->personalData as $pd) { - $this->recordPersonalDataService->deletePersonalData(['anr'=> $anrId, 'id' => $pd->id]); + foreach ($entity->personalData as $pd) { + $this->recordPersonalDataService->deletePersonalData(['anr' => $anrId, 'id' => $pd->id]); } - foreach($entity->internationalTransfers as $it) { - $this->recordInternationalTransferService->delete(['anr'=> $anrId, 'id' => $it->id]); + foreach ($entity->internationalTransfers as $it) { + $this->recordInternationalTransferService->delete(['anr' => $anrId, 'id' => $it->id]); } $result = $this->get('table')->delete($id); $actorsToCheck = array_unique($actorsToCheck); - foreach($actorsToCheck as $a) { - if($this->recordActorService->orphanActor($a, $anrId)) { - $this->recordActorService->delete(['anr'=> $anrId, 'id' => $a]); + foreach ($actorsToCheck as $a) { + if ($this->recordActorService->orphanActor($a, $anrId)) { + $this->recordActorService->delete(['anr' => $anrId, 'id' => $a]); } } $processorsToCheck = array_unique($processorsToCheck); - foreach($processorsToCheck as $p) { - if($this->recordProcessorService->orphanProcessor($p, $anrId)) { + foreach ($processorsToCheck as $p) { + if ($this->recordProcessorService->orphanProcessor($p, $anrId)) { $this->recordProcessorService->deleteProcessor($p); } else { $this->recordProcessorService->deleteActivityAndSecMeasure($p, $id); } } $recipientsToCheck = array_unique($recipientsToCheck); - foreach($recipientsToCheck as $r) { - if($this->recordRecipientService->orphanRecipient($r, $anrId)) { - $this->recordRecipientService->delete(['anr'=> $anrId, 'id' => $r]); + foreach ($recipientsToCheck as $r) { + if ($this->recordRecipientService->orphanRecipient($r, $anrId)) { + $this->recordRecipientService->delete(['anr' => $anrId, 'id' => $r]); } } @@ -100,7 +108,9 @@ public function deleteRecord($id) /** * Updates a record of processing activity + * * @param array $data The record details fields + * * @return object The resulting created record object (entity) */ public function updateRecord($id, $data) @@ -108,106 +118,121 @@ public function updateRecord($id, $data) $entity = $this->get('table')->getEntity($id); // keep entities on old object to delete orphans - $oldActors = array(); - if($entity->controller && $entity->controller->id) { + $oldActors = []; + if ($entity->controller && $entity->controller->id) { $oldActors[] = $entity->controller->id; } - if($entity->representative && $entity->representative->id) { + if ($entity->representative && $entity->representative->id) { $oldActors[] = $entity->representative->id; } - if($entity->dpo && $entity->dpo->id) { + if ($entity->dpo && $entity->dpo->id) { $oldActors[] = $entity->dpo->id; } - foreach($entity->jointControllers as $js) { + foreach ($entity->jointControllers as $js) { $oldActors[] = $js->id; } - $oldRecipients = array(); - foreach( $entity->recipients as $r) { + $oldRecipients = []; + foreach ($entity->recipients as $r) { $oldRecipients[] = $r->id; } - $oldProcessors = array(); - foreach( $entity->processors as $p) { + $oldProcessors = []; + foreach ($entity->processors as $p) { $oldProcessors[] = $p->id; } - if(isset($data['controller']['id'])) { + if (isset($data['controller']['id'])) { $data['controller'] = $data['controller']['id']; } else { $data['controller'] = null; } - if(isset($data['dpo']['id'])) { + if (isset($data['dpo']['id'])) { $data['dpo'] = $data['dpo']['id']; } else { $data['dpo'] = null; } - if(isset($data['representative']['id'])) { + if (isset($data['representative']['id'])) { $data['representative'] = $data['representative']['id']; } else { $data['representative'] = null; } - $jointControllers = array(); + $data['jointControllers'] = []; foreach ($data['jointControllers'] as $jc) { - $jointControllers[] = $jc['id']; + if (isset($jc['id'])) { + $data['jointControllers'][] = $jc['id']; + } } - $data['jointControllers'] = $jointControllers; - $personalData = array(); + $data['personalData'] = []; foreach ($data['personalData'] as $pd) { - $personalData[] = $pd['id']; + if (isset($pd['id'])) { + $data['personalData'][] = $pd['id']; + } } - $data['personalData'] = $personalData; - $recipients = array(); + $data['recipients'] = []; foreach ($data['recipients'] as $recipient) { - $recipients[] = $recipient['id']; + $data['recipients'][] = $recipient['id']; } - $data['recipients'] = $recipients; - $internationalTransfers = array(); + $data['internationalTransfers'] = []; foreach ($data['internationalTransfers'] as $it) { - $internationalTransfers[] = $it['id']; + if (isset($it['id'])) { + $data['internationalTransfers'][] = $it['id']; + } } - $data['internationalTransfers'] = $internationalTransfers; - $processors = array(); + $data['processors'] = []; foreach ($data['processors'] as $processor) { - $processors[] = $processor['id']; + if (isset($processor['id'])) { + $data['processors'][] = $processor['id']; + } } - $data['processors'] = $processors; - foreach($entity->personalData as $pd) { - if(!in_array($pd->id, $personalData)) { - $this->recordPersonalDataService->deletePersonalData(['anr'=> $data['anr'], 'id' => $pd->id]); + foreach ($entity->personalData as $pd) { + if (!in_array($pd->id, $data['personalData'])) { + $this->recordPersonalDataService->deletePersonalData(['anr' => $data['anr'], 'id' => $pd->id]); } } - foreach($entity->internationalTransfers as $it) { - if(!in_array($it->id, $internationalTransfers)) { - $this->recordInternationalTransferService->delete(['anr'=> $data['anr'], 'id' => $it->id]); + foreach ($entity->internationalTransfers as $it) { + if (!in_array($it->id, $data['internationalTransfers'])) { + $this->recordInternationalTransferService->delete(['anr' => $data['anr'], 'id' => $it->id]); } } $result = $this->update($id, $data); - foreach($oldActors as $a) { - if(!in_array($a, $jointControllers) && $a != $data['controller'] && $a != $data['dpo'] && $a != $data['representative'] - && $this->recordActorService->orphanActor($a, $data['anr'])) { - $this->recordActorService->delete(['anr'=> $data['anr'], 'id' => $a]); + foreach ($oldActors as $a) { + if (!in_array($a, $data['jointControllers']) + && $a != $data['controller'] + && $a != $data['dpo'] + && $a != $data['representative'] + && $this->recordActorService->orphanActor($a, $data['anr']) + ) { + $this->recordActorService->delete(['anr' => $data['anr'], 'id' => $a]); } } - foreach($oldRecipients as $rc) { - if(!in_array($rc, $recipients) && $this->recordRecipientService->orphanRecipient($rc, $data['anr'])) { - $this->recordRecipientService->delete(['anr'=> $data['anr'], 'id' => $rc]); + foreach ($oldRecipients as $rc) { + if (!in_array($rc, $data['recipients']) && $this->recordRecipientService->orphanRecipient( + $rc, + $data['anr'] + )) { + $this->recordRecipientService->delete(['anr' => $data['anr'], 'id' => $rc]); } } - foreach($oldProcessors as $processor) { - if(!in_array($processor, $processors) && $this->recordProcessorService->orphanProcessor($processor, $data['anr'])) { + foreach ($oldProcessors as $processor) { + if (!in_array($processor, $data['processors']) + && $this->recordProcessorService->orphanProcessor($processor, $data['anr']) + ) { $this->recordProcessorService->deleteProcessor($processor); - } else if (!in_array($processor, $processors)) { + } elseif (!in_array($processor, $data['processors'])) { $this->recordProcessorService->deleteActivityAndSecMeasure($processor, $id); } } + return $result; } /** * Duplicates an existing record in the anr + * * @param int $recordId The id of record to clone, either its ID or the object + * * @return int The newly created record id * @throws Exception */ @@ -219,7 +244,7 @@ public function duplicateRecord($recordId, $newLabel) $newRecord->resetUpdatedAtValue(); $newRecord->setLabel($newLabel); $id = $this->get('table')->save($newRecord); - if($entity->getProcessors()) { + if ($entity->getProcessors()) { foreach ($entity->getProcessors() as $p) { $data = []; $activities = $p->getActivities(); @@ -229,7 +254,7 @@ public function duplicateRecord($recordId, $newLabel) $processor["id"] = $this->recordProcessorService->importActivityAndSecMeasures($data, $p->getId()); } } - if($entity->getPersonalData()) { + if ($entity->getPersonalData()) { foreach ($entity->getPersonalData() as $pd) { $newPersonalData = new RecordPersonalData($pd); $newPersonalData->setId(null); @@ -237,7 +262,7 @@ public function duplicateRecord($recordId, $newLabel) $this->get('personalDataTable')->save($newPersonalData); } } - if($entity->getInternationalTransfers()) { + if ($entity->getInternationalTransfers()) { foreach ($entity->getInternationalTransfers() as $it) { $newInternationalTransfer = new RecordInternationalTransfer($it); $newInternationalTransfer->setId(null); @@ -245,46 +270,53 @@ public function duplicateRecord($recordId, $newLabel) $this->get('internationalTransferTable')->save($newInternationalTransfer); } } + return $id; } /** * Exports a Record of processing activities, optionaly encrypted, for later re-import + * * @param array $data An array with the Record 'id' and 'password' for encryption + * * @return string JSON file, optionally encrypted */ public function export(&$data) { $filename = ""; - $exportedRecords = array(); $elem = $this->generateExportArray($data['id'], $filename); $exportedRecord = json_encode([$elem]); $data['filename'] = $filename; - if (! empty($data['password'])) { + if (!empty($data['password'])) { $exportedRecord = $this->encrypt($exportedRecord, $data['password']); } + return $exportedRecord; } - public function exportAll($data) { + public function exportAll($data) + { $recordEntities = $this->get('table')->getEntityByFields(['anr' => $data["anr"]]); - $exportedRecords = array(); - foreach($recordEntities as $entity) { + $exportedRecords = []; + foreach ($recordEntities as $entity) { $exportedRecords[] = $this->generateExportArray($entity->get("id")); } $exportedRecords = json_encode($exportedRecords); - if (! empty($data['password'])) { + if (!empty($data['password'])) { $exportedRecords = $this->encrypt($exportedRecords, $data['password']); } + return $exportedRecords; } /** * Generates the array to be exported into a file when calling {#exportRecord} * @see #exportRecord + * * @param int $id The record's id * @param string $filename The output filename + * * @return array The data array that should be saved * @throws Exception If the record is not found */ @@ -301,19 +333,19 @@ public function generateExportArray($id, &$filename = "") $return = [ 'name' => $entity->label, ]; - if($entity->controller) { + if ($entity->controller) { $return['controller'] = $this->recordActorService->generateExportArray($entity->controller->id); } - if($entity->representative) { + if ($entity->representative) { $return['representative'] = $this->recordActorService->generateExportArray($entity->representative->id); } - if($entity->dpo) { + if ($entity->dpo) { $return['data_protection_officer'] = $this->recordActorService->generateExportArray($entity->dpo->id); } - if($entity->purposes != '') { + if ($entity->purposes !== '') { $return['purposes'] = $entity->purposes; } - if($entity->secMeasures != '') { + if ($entity->secMeasures !== '') { $return['security_measures'] = $entity->secMeasures; } foreach ($entity->jointControllers as $jc) { @@ -326,19 +358,25 @@ public function generateExportArray($id, &$filename = "") $return['recipients'][] = $this->recordRecipientService->generateExportArray($r->id); } foreach ($entity->internationalTransfers as $it) { - $return['international_transfers'][] = $this->recordInternationalTransferService->generateExportArray($it->id); + $return['international_transfers'][] = $this->recordInternationalTransferService->generateExportArray( + $it->id + ); } foreach ($entity->processors as $p) { $return['processors'][] = $this->recordProcessorService->generateExportArray($p->id, $id); } + return $return; } /** * Imports a Record that has been exported into a file. + * * @param int $anrId The target ANR ID * @param array $data The data that has been posted to the API (file) - * @return array An array where the first key is an array of generated records' ID, and the second the eventual errors + * + * @return array An array where the first key is an array of generated records' ID, and the second the eventual + * errors * @throws Exception If the posted data is invalid, or ANR ID is invalid */ public function importFromFile($anrId, $data) @@ -349,8 +387,6 @@ public function importFromFile($anrId, $data) } $ids = $errors = []; - $anr = $this->get('anrTable')->getEntity($anrId); - if ($data['isJson'] == 'true') { $f = $data['file']; if (isset($f['error']) && $f['error'] === UPLOAD_ERR_OK && file_exists($f['tmp_name'])) { @@ -358,18 +394,24 @@ public function importFromFile($anrId, $data) if (empty($data['password'])) { $file = json_decode(trim(file_get_contents($f['tmp_name'])), true); if (!$file) { // support legacy export which were base64 encoded - $file = json_decode(trim($this->decrypt(base64_decode(file_get_contents($f['tmp_name'])), '')), true); + $file = json_decode( + trim($this->decrypt(base64_decode(file_get_contents($f['tmp_name'])), '')), + true + ); } } else { // Decrypt the file and store the JSON data as an array in memory $key = $data['password']; $file = json_decode(trim($this->decrypt(file_get_contents($f['tmp_name']), $key)), true); if (!$file) { // support legacy export which were base64 encoded - $file = json_decode(trim($this->decrypt(base64_decode(file_get_contents($f['tmp_name'])), $key)), true); + $file = json_decode( + trim($this->decrypt(base64_decode(file_get_contents($f['tmp_name'])), $key)), + true + ); } } - foreach($file as $key => $record) { + foreach ($file as $key => $record) { if ($record !== false && ($id = $this->importFromArray($record, $anrId)) !== false) { $ids[] = $id; } else { @@ -381,8 +423,8 @@ public function importFromFile($anrId, $data) $array = $data['csv']; $file = []; $file['type'] = 'record'; - foreach($array as $key => $row) { - if($key != 0 && trim($row["name"])) { + foreach ($array as $key => $row) { + if ($key != 0 && trim($row["name"])) { if ($file !== false && ($id = $this->importFromArray($file, $anrId)) !== false) { $ids[] = $id; } else { @@ -391,150 +433,166 @@ public function importFromFile($anrId, $data) $file = []; $file['type'] = 'record'; } - if(trim($row['name'])) { + if (trim($row['name'])) { $file['name'] = $row['name']; } - if(trim($row['purposes'])) { + if (trim($row['purposes'])) { $file['purposes'] = $row['purposes']; } - if(trim($row['security measures'])) { + if (trim($row['security measures'])) { $file['security_measures'] = $row['security measures']; } - if(trim($row['controller name'])) { + if (trim($row['controller name'])) { $file['controller'] = []; $file['controller']['name'] = $row['controller name']; - if(trim($row['controller contact'])) { + if (trim($row['controller contact'])) { $file['controller']['contact'] = $row['controller contact']; } } - if(trim($row['representative name'])) { + if (trim($row['representative name'])) { $file['representative'] = []; $file['representative']['name'] = $row['representative name']; - if(trim($row['representative contact'])) { + if (trim($row['representative contact'])) { $file['representative']['contact'] = $row['representative contact']; } } - if(trim($row['data protection officer name'])) { + if (trim($row['data protection officer name'])) { $file['data_protection_officer'] = []; $file['data_protection_officer']['name'] = $row['data protection officer name']; - if(trim($row['data protection officer contact'])) { + if (trim($row['data protection officer contact'])) { $file['data_protection_officer']['contact'] = $row['data protection officer contact']; } } - if(trim($row['joint controllers name'])) { - if( !isset($file['joint_controllers'])) { + if (trim($row['joint controllers name'])) { + if (!isset($file['joint_controllers'])) { $file['joint_controllers'] = []; } $jc = []; $jc['name'] = $row['joint controllers name']; - if(trim($row['joint controllers contact'])) { + if (trim($row['joint controllers contact'])) { $jc['contact'] = $row['joint controllers contact']; } $file['joint_controllers'][] = $jc; } - if(trim($row['retention period unit'])) { - if( !isset($file['personal_data'])) { + if (trim($row['retention period unit'])) { + if (!isset($file['personal_data'])) { $file['personal_data'] = []; } $pd = []; - if(trim($row['data subject'])) { + if (trim($row['data subject'])) { $pd['data_subject'] = $row['data subject']; } - if(trim($row['data categories'])) { - foreach(explode(", ", $row['data categories']) as $dc) { + if (trim($row['data categories'])) { + foreach (explode(", ", $row['data categories']) as $dc) { $dataCategory = []; $dataCategory['name'] = $dc; $pd['data_categories'][] = $dataCategory; } } - if(trim($row['description'])) { + if (trim($row['description'])) { $pd['description'] = $row['description']; } - if(trim($row['retention period']) != "") { + if (trim($row['retention period']) != "") { $pd['retention_period'] = $row['retention period']; } - if(trim($row['retention period unit'])) { + if (trim($row['retention period unit'])) { $pd['retention_period_mode'] = $row['retention period unit']; } - if(trim($row['retention period description'])) { + if (trim($row['retention period description'])) { $pd['retention_period_description'] = $row['retention period description']; } $file['personal_data'][] = $pd; } - if(trim($row['data recipient']) || trim($row['data recipient type']) || trim($row['description'])) { - if( !isset($file['recipients'])) { + if (trim($row['data recipient']) || trim($row['data recipient type']) || trim($row['description'])) { + if (!isset($file['recipients'])) { $file['recipients'] = []; } $r = []; - if(trim($row['data recipient'])) { + if (trim($row['data recipient'])) { $r['name'] = $row['data recipient']; } - if(trim($row['data recipient type'])) { + if (trim($row['data recipient type'])) { $r['type'] = $row['data recipient type']; } - if(trim($row['description'])) { + if (trim($row['description'])) { $r['description'] = $row['description']; } $file['recipients'][] = $r; } - if(trim($row['organisation of international transfer']) || trim($row['description']) || trim($row['country']) || trim($row['documents'])) { - if( !isset($file['international_transfers'])) { + if (trim($row['organisation of international transfer']) + || trim($row['description']) + || trim($row['country']) + || trim($row['documents']) + ) { + if (!isset($file['international_transfers'])) { $file['international_transfers'] = []; } $it = []; - if(trim($row['organisation of international transfer'])) + if (trim($row['organisation of international transfer'])) { $it['organisation'] = $row['organisation of international transfer']; - if(trim($row['description'])) + } + if (trim($row['description'])) { $it['description'] = $row['description']; - if(trim($row['country'])) + } + if (trim($row['country'])) { $it['country'] = $row['country']; - if(trim($row['documents'])) + } + if (trim($row['documents'])) { $it['documents'] = $row['documents']; + } $file['international_transfers'][] = $it; } - if(trim($row['data processor name'])) { - if( !isset($file['processors'])) { + if (trim($row['data processor name'])) { + if (!isset($file['processors'])) { $file['processors'] = []; } $p = []; $p['name'] = $row['data processor name']; - if(trim($row['data processor contact'])) + if (trim($row['data processor contact'])) { $p['contact'] = $row['data processor contact']; - if(trim($row['activities'])) + } + if (trim($row['activities'])) { $p['activities'] = $row['activities']; - if(trim($row['data processor security measures'])) + } + if (trim($row['data processor security measures'])) { $p['security_measures'] = $row['data processor security measures']; - if(trim($row['data processor representative name'])) { + } + if (trim($row['data processor representative name'])) { $rep = []; $rep['name'] = $row['data processor representative name']; - if(trim($row['data processor representative contact'])) + if (trim($row['data processor representative contact'])) { $rep['contact'] = $row['data processor representative contact']; + } $p['representative'] = $rep; } - if(trim($row['data processor data protection officer name'])) { + if (trim($row['data processor data protection officer name'])) { $dpo = []; $dpo['name'] = $row['data processor data protection officer name']; - if(trim($row['data processor data protection officer contact'])) + if (trim($row['data processor data protection officer contact'])) { $dpo['contact'] = $row['data processor data protection officer contact']; + } $p['data_protection_officer'] = $dpo; } $file['processors'][] = $p; } } - if ($file !== false && $file['name'] && ($id = $this->importFromArray($file, $anrId)) !== false) { + if (isset($file['name']) && ($id = $this->importFromArray($file, $anrId)) !== false) { $ids[] = $id; } else { - $errors[] = 'The file "' . $file['name'] . '" can\'t be imported'; + $errors[] = 'The file "' . ($file['name'] ?? '') . '" can\'t be imported'; } } + return [$ids, $errors]; } /** * Imports a record from a data array. This data is generally what has been exported into a file. + * * @param array $data The record's data fields - * @param Anr $anr The target ANR id + * @param int $anr The target ANR id + * * @return bool|int The ID of the generated asset, or false if an error occurred. */ public function importFromArray($data, $anr) @@ -542,8 +600,8 @@ public function importFromArray($data, $anr) $newData = []; $newData['anr'] = $anr; $newData['label'] = $data['name']; - $newData['purposes'] = (isset($data['purposes']) ? $data['purposes'] : ''); - $newData['secMeasures'] = (isset($data['security_measures']) ? $data['security_measures'] : ''); + $newData['purposes'] = $data['purposes'] ?? ''; + $newData['secMeasures'] = $data['security_measures'] ?? ''; if (isset($data['controller'])) { $newData['controller'] = $this->recordActorService->importFromArray($data['controller'], $anr); } @@ -587,7 +645,11 @@ public function importFromArray($data, $anr) } if (isset($data['international_transfers'])) { foreach ($data['international_transfers'] as $it) { - $internationalTransfers['id'] = $this->recordInternationalTransferService->importFromArray($it, $anr, $id); + $internationalTransfers['id'] = $this->recordInternationalTransferService->importFromArray( + $it, + $anr, + $id + ); $newData['internationalTransfers'][] = $internationalTransfers; } } diff --git a/src/Service/AnrRolfRiskService.php b/src/Service/AnrRolfRiskService.php index ec2601fc..d526e08f 100755 --- a/src/Service/AnrRolfRiskService.php +++ b/src/Service/AnrRolfRiskService.php @@ -21,7 +21,6 @@ public function __construct( private Table\RolfRiskTable $rolfRiskTable, private Table\RolfTagTable $rolfTagTable, private Table\MeasureTable $measureTable, - private Table\MonarcObjectTable $monarcObjectTable, private Table\ReferentialTable $referentialTable, private Table\InstanceRiskOpTable $instanceRiskOpTable, private AnrInstanceRiskOpService $anrInstanceRiskOpService, diff --git a/src/Service/AnrScaleCommentService.php b/src/Service/AnrScaleCommentService.php index ec32822d..48d77ffe 100755 --- a/src/Service/AnrScaleCommentService.php +++ b/src/Service/AnrScaleCommentService.php @@ -29,9 +29,8 @@ public function __construct( public function getList(FormattedInputParams $formattedInputParams): array { $result = []; - /** @var Entity\ScaleComment[] $scaleComments */ - $scaleComments = $this->scaleCommentTable->findByParams($formattedInputParams); - foreach ($scaleComments as $scaleComment) { + /** @var Entity\ScaleComment $scaleComment */ + foreach ($this->scaleCommentTable->findByParams($formattedInputParams) as $scaleComment) { $result[] = array_merge([ 'id' => $scaleComment->getId(), 'scaleIndex' => $scaleComment->getScaleIndex(), @@ -46,10 +45,12 @@ public function getList(FormattedInputParams $formattedInputParams): array return $result; } - public function create(Entity\Anr $anr, array $data): Entity\ScaleComment + public function create(Entity\Anr $anr, array $data, bool $saveInDb = true): Entity\ScaleComment { /** @var Entity\Scale $scale */ - $scale = $this->scaleTable->findByIdAndAnr($data['scaleId'], $anr); + $scale = isset($data['scale']) && $data['scale'] instanceof Entity\Scale + ? $data['scale'] + : $this->scaleTable->findByIdAndAnr($data['scaleId'], $anr); /** @var Entity\ScaleComment $scaleComment */ $scaleComment = (new Entity\ScaleComment()) @@ -60,13 +61,15 @@ public function create(Entity\Anr $anr, array $data): Entity\ScaleComment ->setScaleValue($data['scaleValue']) ->setCreator($this->connectedUser->getEmail()); - if (isset($data['scaleImpactType'])) { + if (!empty($data['scaleImpactType'])) { /** @var Entity\ScaleImpactType $scaleImpactType */ - $scaleImpactType = $this->scaleImpactTypeTable->findByIdAndAnr($data['scaleImpactType'], $anr); + $scaleImpactType = $data['scaleImpactType'] instanceof Entity\ScaleImpactType + ? $data['scaleImpactType'] + : $this->scaleImpactTypeTable->findByIdAndAnr($data['scaleImpactType'], $anr); $scaleComment->setScaleImpactType($scaleImpactType); } - $this->scaleCommentTable->save($scaleComment); + $this->scaleCommentTable->save($scaleComment, $saveInDb); return $scaleComment; } diff --git a/src/Service/AnrScaleImpactTypeService.php b/src/Service/AnrScaleImpactTypeService.php index e4595345..ff61be6b 100755 --- a/src/Service/AnrScaleImpactTypeService.php +++ b/src/Service/AnrScaleImpactTypeService.php @@ -32,10 +32,9 @@ public function __construct( public function getList(Entity\Anr $anr): array { $result = []; - /** @var Entity\ScaleImpactType[] $scaleImpactTypes */ - $scaleImpactTypes = $this->scaleImpactTypeTable->findByAnr($anr); $scaleImpactTypesShortcuts = ScaleImpactTypeSuperClass::getScaleImpactTypesShortcuts(); - foreach ($scaleImpactTypes as $scaleImpactType) { + /** @var Entity\ScaleImpactType $scaleImpactType */ + foreach ($this->scaleImpactTypeTable->findByAnr($anr) as $scaleImpactType) { $result[] = array_merge([ 'id' => $scaleImpactType->getId(), 'isHidden' => (int)$scaleImpactType->isHidden(), @@ -49,7 +48,6 @@ public function getList(Entity\Anr $anr): array public function create(Entity\Anr $anr, array $data, bool $saveInTheDb = true): Entity\ScaleImpactType { - /** @var Entity\ScaleImpactType $scaleImpactType */ $scaleImpactType = (new Entity\ScaleImpactType()) ->setAnr($anr) ->setScale( @@ -58,11 +56,18 @@ public function create(Entity\Anr $anr, array $data, bool $saveInTheDb = true): ->setLabels($data['labels']) ->setType($data['type'] ?? $this->scaleImpactTypeTable->findMaxTypeValueByAnr($anr) + 1) ->setCreator($this->connectedUser->getEmail()); + if (isset($data['isHidden'])) { + $scaleImpactType->setIsHidden((bool)$data['isHidden']); + } /* Create InstanceConsequence for each instance of the current anr. */ /** @var Entity\Instance $instance */ foreach ($this->instanceTable->findByAnr($scaleImpactType->getAnr()) as $instance) { - $this->instanceConsequenceService->createInstanceConsequence($instance, $scaleImpactType); + $this->instanceConsequenceService->createInstanceConsequence( + $instance, + $scaleImpactType, + $scaleImpactType->isHidden() + ); } $this->scaleImpactTypeTable->save($scaleImpactType, $saveInTheDb); diff --git a/src/Service/AnrScaleService.php b/src/Service/AnrScaleService.php index 1b305288..d9d2f2f7 100755 --- a/src/Service/AnrScaleService.php +++ b/src/Service/AnrScaleService.php @@ -33,11 +33,9 @@ public function __construct( public function getList(Entity\Anr $anr): array { $result = []; - /** @var Entity\Scale[] $scales */ - $scales = $this->scaleTable->findByAnr($anr); $availableTypes = CoreEntity\ScaleSuperClass::getAvailableTypes(); - - foreach ($scales as $scale) { + /** @var Entity\Scale $scale */ + foreach ($this->scaleTable->findByAnr($anr) as $scale) { $result[] = [ 'id' => $scale->getId(), 'type' => $availableTypes[$scale->getType()], diff --git a/src/Service/AnrService.php b/src/Service/AnrService.php index 504d106d..9cad392d 100755 --- a/src/Service/AnrService.php +++ b/src/Service/AnrService.php @@ -54,7 +54,7 @@ public function __construct( private Table\MeasureTable $measureTable, private Table\RolfRiskTable $rolfRiskTable, private Table\RolfTagTable $rolfTagTable, - private DeprecatedTable\SoaTable $soaTable, + private Table\SoaTable $soaTable, private DeprecatedTable\QuestionTable $questionTable, private DeprecatedTable\QuestionChoiceTable $questionChoiceTable, private DeprecatedTable\InterviewTable $interviewTable, @@ -577,7 +577,7 @@ private function updateReferentialsFromSource( } /* Recreate SOA link with measure. */ - $this->soaTable->saveEntity((new Entity\Soa())->setAnr($anr)->setMeasure($measure), false); + $this->soaTable->save((new Entity\Soa())->setAnr($anr)->setMeasure($measure), false); } $this->measureTable->save($measure, false); @@ -1092,10 +1092,10 @@ private function duplicateSoasAndSoaScaleComments( if ($isSourceCommon) { foreach ($createdMeasuresUuidsToObjects as $measure) { - $this->soaTable->saveEntity((new Entity\Soa())->setAnr($newAnr)->setMeasure($measure), false); + $this->soaTable->save((new Entity\Soa())->setAnr($newAnr)->setMeasure($measure), false); } } else { - /** @var Entity\Anr $sourceAnr */ + /** @var Entity\Soa $sourceSoa */ foreach ($this->soaTable->findByAnr($sourceAnr) as $sourceSoa) { $measure = $createdMeasuresUuidsToObjects[$sourceSoa->getMeasure()->getUuid()] ?? null; if ($measure === null) { @@ -1109,7 +1109,7 @@ private function duplicateSoasAndSoaScaleComments( $anrSoaScaleCommentOldIdsToNewObjects[$sourceSoa->getSoaScaleComment()->getId()] ); } - $this->soaTable->saveEntity($newSoa, false); + $this->soaTable->save($newSoa, false); } } } diff --git a/src/Service/AnrThemeService.php b/src/Service/AnrThemeService.php index 2b065a76..75cf4f71 100755 --- a/src/Service/AnrThemeService.php +++ b/src/Service/AnrThemeService.php @@ -26,10 +26,8 @@ public function __construct(private ThemeTable $themeTable, ConnectedUserService public function getList(FormattedInputParams $params): array { $result = []; - - /** @var Theme[] $themes */ - $themes = $this->themeTable->findByParams($params); - foreach ($themes as $theme) { + /** @var Theme $theme */ + foreach ($this->themeTable->findByParams($params) as $theme) { $result[] = $this->prepareThemeDataResult($theme); } diff --git a/src/Service/AnrThreatService.php b/src/Service/AnrThreatService.php index 833c843b..6b2ab9cb 100755 --- a/src/Service/AnrThreatService.php +++ b/src/Service/AnrThreatService.php @@ -33,9 +33,8 @@ public function getList(FormattedInputParams $params): array { $result = []; - /** @var Threat[] $threats */ - $threats = $this->threatTable->findByParams($params); - foreach ($threats as $threat) { + /** @var Threat $threat */ + foreach ($this->threatTable->findByParams($params) as $threat) { $result[] = $this->prepareThreatDataResult($threat); } diff --git a/src/Service/AnrVulnerabilityService.php b/src/Service/AnrVulnerabilityService.php index 210a57cd..0a978156 100755 --- a/src/Service/AnrVulnerabilityService.php +++ b/src/Service/AnrVulnerabilityService.php @@ -29,10 +29,8 @@ public function __construct( public function getList(FormattedInputParams $params): array { $result = []; - - /** @var Vulnerability[] $vulnerabilities */ - $vulnerabilities = $this->vulnerabilityTable->findByParams($params); - foreach ($vulnerabilities as $vulnerability) { + /** @var Vulnerability $vulnerability */ + foreach ($this->vulnerabilityTable->findByParams($params) as $vulnerability) { $result[] = $this->prepareVulnerabilityDataResult($vulnerability); } diff --git a/src/Service/InstanceMetadataService.php b/src/Service/InstanceMetadataService.php index 9292ecb1..0931b7c5 100644 --- a/src/Service/InstanceMetadataService.php +++ b/src/Service/InstanceMetadataService.php @@ -8,10 +8,7 @@ namespace Monarc\FrontOffice\Service; use Monarc\Core\Entity\UserSuperClass; -use Monarc\FrontOffice\Entity\Anr; -use Monarc\FrontOffice\Entity\AnrInstanceMetadataField; -use Monarc\FrontOffice\Entity\Instance; -use Monarc\FrontOffice\Entity\InstanceMetadata; +use Monarc\FrontOffice\Entity; use Monarc\Core\Service\ConnectedUserService; use Monarc\FrontOffice\Table; @@ -28,38 +25,44 @@ public function __construct( $this->connectedUser = $connectedUserService->getConnectedUser(); } - public function getInstancesMetadata(Anr $anr, int $instanceId): array + public function getInstancesMetadata(Entity\Anr $anr, int $instanceId): array { $result = []; - /** @var Instance $instance */ + /** @var Entity\Instance $instance */ $instance = $this->instanceTable->findByIdAndAnr($instanceId, $anr); + $instanceMetadataByMetadataFieldId = []; foreach ($instance->getInstanceMetadata() as $instanceMetadata) { - $metadataField = $instanceMetadata->getAnrInstanceMetadataField(); + $metadataFieldId = $instanceMetadata->getAnrInstanceMetadataField()->getId(); + $instanceMetadataByMetadataFieldId[$metadataFieldId] = [ + 'id' => $instanceMetadata->getId(), + 'metadataId' => $metadataFieldId, + $anr->getLanguageCode() => $instanceMetadata->getComment(), + ]; + } + + foreach ($anr->getAnrInstanceMetadataFields() as $metadataField) { $metadataFieldId = $metadataField->getId(); $result[$metadataFieldId] = [ 'id' => $metadataFieldId, - $anr->getLanguage() => $metadataField->getLabel(), + $anr->getLanguageCode() => $metadataField->getLabel(), 'isDeletable' => $metadataField->isDeletable(), - 'instanceMetadata' => [ - 'id' => $instanceMetadata->getId(), - 'metadataId' => $metadataFieldId, - $anr->getLanguage() => $instanceMetadata->getComment(), - ], + 'instanceMetadata' => $instanceMetadataByMetadataFieldId[$metadataFieldId] ?? [], ]; } return $result; } - public function create(Anr $anr, int $instanceId, array $data): InstanceMetadata + public function create(Entity\Anr $anr, int $instanceId, array $data): Entity\InstanceMetadata { + /** @var Entity\Instance $instance */ $instance = $this->instanceTable->findById($instanceId); $metadataFieldData = current($data['metadata']); $instanceMetadataComment = $metadataFieldData['instanceMetadata'][$anr->getLanguageCode()] ?? ''; - /** @var AnrInstanceMetadataField $metadataField */ + /** @var Entity\AnrInstanceMetadataField $metadataField */ $metadataField = $this->anrInstanceMetadataFieldTable->findByIdAndAnr((int)$metadataFieldData['id'], $anr); - $instanceMetadata = (new InstanceMetadata()) + $instanceMetadata = (new Entity\InstanceMetadata()) ->setInstance($instance) ->setAnrInstanceMetadataField($metadataField) ->setComment($instanceMetadataComment) @@ -68,7 +71,7 @@ public function create(Anr $anr, int $instanceId, array $data): InstanceMetadata /* Create the same context instance metadata records for the global instance's siblings. */ $siblingInstances = $this->instanceTable->findGlobalSiblingsByAnrAndInstance($anr, $instance); foreach ($siblingInstances as $siblingInstance) { - $siblingInstanceMetadata = (new InstanceMetadata()) + $siblingInstanceMetadata = (new Entity\InstanceMetadata()) ->setInstance($siblingInstance) ->setAnrInstanceMetadataField($metadataField) ->setComment($instanceMetadataComment) @@ -82,9 +85,9 @@ public function create(Anr $anr, int $instanceId, array $data): InstanceMetadata return $instanceMetadata; } - public function update(Anr $anr, int $id, array $data): InstanceMetadata + public function update(Entity\Anr $anr, int $id, array $data): Entity\InstanceMetadata { - /** @var InstanceMetadata $instanceMetadata */ + /** @var Entity\InstanceMetadata $instanceMetadata */ $instanceMetadata = $this->instanceMetadataTable->findByIdAndAnr($id, $anr); $commentValue = $data[$anr->getLanguageCode()] ?? ''; diff --git a/src/Service/Model/Entity/SoaServiceModelEntity.php b/src/Service/Model/Entity/SoaServiceModelEntity.php deleted file mode 100755 index 1114d94c..00000000 --- a/src/Service/Model/Entity/SoaServiceModelEntity.php +++ /dev/null @@ -1,19 +0,0 @@ -isHidden()) { $updatedInstanceRiskIds = []; foreach ($operationalRiskScaleType->getOperationalInstanceRiskScales() as $operationalInstanceRiskScale) { + /** @var Entity\InstanceRiskOp $operationalInstanceRisk */ $operationalInstanceRisk = $operationalInstanceRiskScale->getOperationalInstanceRisk(); if (!\in_array($operationalInstanceRisk->getId(), $updatedInstanceRiskIds, true)) { $this->instanceRiskOpService->updateRiskCacheValues($operationalInstanceRisk); diff --git a/src/Service/SoaCategoryService.php b/src/Service/SoaCategoryService.php index add8c671..c3c9ee6c 100755 --- a/src/Service/SoaCategoryService.php +++ b/src/Service/SoaCategoryService.php @@ -8,7 +8,6 @@ namespace Monarc\FrontOffice\Service; use Monarc\Core\InputFormatter\FormattedInputParams; -use Monarc\FrontOffice\Import\Helper\ImportCacheHelper; use Monarc\FrontOffice\Entity; use Monarc\FrontOffice\Table\ReferentialTable; use Monarc\FrontOffice\Table\SoaCategoryTable; @@ -75,48 +74,4 @@ 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, - Entity\Referential $referential, - string $labelValue - ): Entity\SoaCategory { - $languageIndex = $anr->getLanguage(); - $labelKey = 'label' . $languageIndex; - - $this->prepareSoaCategoriesCacheData($importCacheHelper, $anr); - - $cacheKey = $referential->getUuid() . '_' . $labelValue; - $soaCategory = $importCacheHelper->getItemFromArrayCache('soa_categories_by_ref_and_label', $cacheKey); - if ($soaCategory !== null) { - return $soaCategory; - } - - $soaCategory = (new Entity\SoaCategory()) - ->setAnr($anr) - ->setReferential($referential) - ->setLabels([$labelKey => $labelValue]); - - $this->soaCategoryTable->save($soaCategory, false); - - $importCacheHelper->addItemToArrayCache('soa_categories_by_ref_and_label', $soaCategory, $cacheKey); - - 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/Service/SoaService.php b/src/Service/SoaService.php index 261f0bbc..600ae8f1 100755 --- a/src/Service/SoaService.php +++ b/src/Service/SoaService.php @@ -1,72 +1,115 @@ -get('entity')->getFiltersForService(); - - $data = $this->get('table')->fetchAllFiltered( - array_keys($this->get('entity')->getJsonArray()), - $page, - 0, - $this->parseFrontendOrder($order), - $this->parseFrontendFilter($filter, $filtersCol), - $filterAnd, - $filterJoin, - $filterLeft - ); - if ($order == "m.code" || $order == "-m.code") { - $desc = ($order == "-m.code"); - if (!$desc) { - uasort($data, function ($a, $b) { - return strnatcmp($a['measure']->getCode(), $b['measure']->getCode()); - }); - } else { - uasort($data, function ($a, $b) { - return strnatcmp($b['measure']->getCode(), $a['measure']->getCode()); - }); + $result = []; + /** @var Soa $soa */ + foreach ($this->soaTable->findByParams($params) as $soa) { + $measure = $soa->getMeasure(); + $linkedMeasuresUuids = []; + foreach ($measure->getLinkedMeasures() as $linkedMeasure) { + $linkedMeasuresUuids[] = $linkedMeasure->getUuid(); } - } - if ($limit != 0) { - return array_slice($data, ($page - 1) * $limit, $limit, false); + $amvsData = []; + $amvsUuids = []; + foreach ($measure->getAmvs() as $amv) { + $amvsUuids[] = $amv->getUuid(); + } + if (!empty($amvsUuids)) { + $amvsData = $this->anrInstanceRiskService->getInstanceRisks($soa->getAnr(), null, [ + 'amvs' => $amvsUuids, + 'limit' => -1, + 'order' => 'maxRisk', + 'order_direction' => 'desc', + ]); + } + $rolfRisksData = []; + $rolfRisksIds = []; + foreach ($measure->getRolfRisks() as $rolfRisk) { + $rolfRisksIds[] = $rolfRisk->getId(); + } + if (!empty($amvsUuids)) { + $rolfRisksData = $this->anrInstanceRiskOpService->getOperationalRisks($measure->getAnr(), null, [ + 'rolfRisks' => $rolfRisksIds, + 'limit' => -1, + 'order' => 'cacheNetRisk', + 'order_direction' => 'desc', + ]); + } + + $result[] = [ + 'id' => $soa->getId(), + 'remarks' => $soa->getRemarks(), + 'evidences' => $soa->getEvidences(), + 'actions' => $soa->getActions(), + 'compliance' => $soa->getCompliance(), + 'EX' => $soa->getEx(), + 'LR' => $soa->getLr(), + 'CO' => $soa->getCo(), + 'BR' => $soa->getBr(), + 'BP' => $soa->getBp(), + 'RRA' => $soa->getRra(), + 'soaScaleComment' => $soa->getSoaScaleComment() === null ? null : [ + 'id' => $soa->getSoaScaleComment()->getId(), + 'colour' => $soa->getSoaScaleComment()->getColour(), + 'comment' => $soa->getSoaScaleComment()->getComment(), + 'scaleIndex' => $soa->getSoaScaleComment()->getScaleIndex(), + 'isHidden' => $soa->getSoaScaleComment()->isHidden(), + ], + 'measure' => array_merge([ + 'id' => $measure->getId(), + 'uuid' => $measure->getUuid(), + 'referential' => array_merge([ + 'uuid' => $measure->getReferential()->getUuid(), + ], $measure->getReferential()->getLabels()), + 'code' => $measure->getCode(), + 'category' => $measure->getCategory() === null + ? [] + : array_merge(['id' => $measure->getCategory()->getId()], $measure->getCategory()->getLabels()), + 'status' => $measure->getStatus(), + 'linkedMeasures' => $linkedMeasuresUuids, + 'amvs' => $amvsData, + 'rolfRisks' => $rolfRisksData, + ], $measure->getLabels()) + ]; } - return $data; + return $result; } - public function getFilteredCount($filter = null, $filterAnd = null) + public function getCount(FormattedInputParams $params): int { - list($filterJoin, $filterLeft, $filtersCol) = $this->get('entity')->getFiltersForService(); - - return $this->get('table')->countFiltered( - $this->parseFrontendFilter($filter, $filtersCol), - $filterAnd, - $filterJoin, - $filterLeft - ); + return $this->soaTable->countByParams($params); } - public function patchSoa(int $id, array $data) + public function patchSoa(Anr $anr, int $id, array $data, bool $saveInDb = true): Soa { - $table = $this->get('table'); - $soa = $table->findById($id); + /** @var Soa $soa */ + $soa = $this->soaTable->findByIdAndAnr($id, $anr); if (isset($data['remarks'])) { $soa->setRemarks($data['remarks']); } @@ -94,11 +137,34 @@ public function patchSoa(int $id, array $data) if (isset($data['RRA'])) { $soa->setRra($data['RRA']); } - if (isset($data['soaScaleComment'])) { - $soaScaleCommentTable = $this->get('soaScaleCommentTable'); - $soaScaleComment = $soaScaleCommentTable->findById($data['soaScaleComment']); + if (!empty($data['soaScaleComment']) && $soa->getSoaScaleComment() !== null + && $soa->getSoaScaleComment()->getId() !== (int)$data['soaScaleComment'] + ) { + /** @var SoaScaleComment $soaScaleComment */ + $soaScaleComment = $this->soaScaleCommentTable->findByIdAndAnr((int)$data['soaScaleComment'], $anr); $soa->setSoaScaleComment($soaScaleComment); } - $table->saveEntity($soa); + + $this->soaTable->save($soa, $saveInDb); + + return $soa; + } + + /** + * @return int[] + */ + public function patchList(Anr $anr, array $data): array + { + $updatedIds = []; + foreach ($data as $row) { + $id = $row['id']; + if (\is_array($row['soaScaleComment'])) { + $row['soaScaleComment'] = $row['soaScaleComment']['id']; + } + $updatedIds[] = $this->patchSoa($anr, $id, $row, false)->getId(); + } + $this->soaTable->flush(); + + return $updatedIds; } } diff --git a/src/Service/SoaServiceFactory.php b/src/Service/SoaServiceFactory.php deleted file mode 100755 index c52e8666..00000000 --- a/src/Service/SoaServiceFactory.php +++ /dev/null @@ -1,24 +0,0 @@ - Soa::class, - 'table' => DeprecatedTable\SoaTable::class, - 'anrTable' => Table\AnrTable::class, - 'userAnrTable' => Table\UserAnrTable::class, - 'soaScaleCommentTable' => Table\SoaScaleCommentTable::class, - ]; -} diff --git a/src/Stats/Service/StatsAnrService.php b/src/Stats/Service/StatsAnrService.php index 86933841..9a461f88 100644 --- a/src/Stats/Service/StatsAnrService.php +++ b/src/Stats/Service/StatsAnrService.php @@ -19,13 +19,7 @@ use Monarc\FrontOffice\Entity\SoaCategory; use Monarc\FrontOffice\Entity\User; use Monarc\FrontOffice\Entity\UserRole; -use Monarc\FrontOffice\Model\Table\SoaTable; -use Monarc\FrontOffice\Table\ReferentialTable; -use Monarc\FrontOffice\Table\ScaleTable; -use Monarc\FrontOffice\Table\AnrTable; -use Monarc\FrontOffice\Table\InstanceRiskOpTable; -use Monarc\FrontOffice\Table\InstanceRiskTable; -use Monarc\FrontOffice\Table\SnapshotTable; +use Monarc\FrontOffice\Table; use Monarc\FrontOffice\Stats\DataObject\StatsDataObject; use Monarc\FrontOffice\Stats\Exception\StatsAlreadyCollectedException; use Monarc\FrontOffice\Stats\Exception\StatsFetchingException; @@ -66,14 +60,14 @@ class StatsAnrService private $apiKey; public function __construct( - private AnrTable $anrTable, - private ScaleTable $scaleTable, - private InstanceRiskTable $informationalRiskTable, - private InstanceRiskOpTable $operationalRiskTable, - private ReferentialTable $referentialTable, - private SoaTable $soaTable, + private Table\AnrTable $anrTable, + private Table\ScaleTable $scaleTable, + private Table\InstanceRiskTable $informationalRiskTable, + private Table\InstanceRiskOpTable $operationalRiskTable, + private Table\ReferentialTable $referentialTable, + private Table\SoaTable $soaTable, private StatsApiProvider $statsApiProvider, - private SnapshotTable $snapshotTable, + private Table\SnapshotTable $snapshotTable, ConnectedUserService $connectedUserService, array $config ) { diff --git a/src/Table/InstanceMetadataTable.php b/src/Table/InstanceMetadataTable.php index 799de570..cba5ecdf 100644 --- a/src/Table/InstanceMetadataTable.php +++ b/src/Table/InstanceMetadataTable.php @@ -29,6 +29,7 @@ public function findByInstanceAndMetadataField( ->andWhere('im.anrInstanceMetadataField = :anrInstanceMetadataField') ->setParameter('instance', $instance) ->setParameter('anrInstanceMetadataField', $anrInstanceMetadataField) + ->setMaxResults(1) ->getQuery() ->getOneOrNullResult(); } diff --git a/src/Table/MonarcObjectTable.php b/src/Table/MonarcObjectTable.php index 4f294787..9e7abbc3 100755 --- a/src/Table/MonarcObjectTable.php +++ b/src/Table/MonarcObjectTable.php @@ -9,6 +9,7 @@ use Doctrine\ORM\EntityManager; use Monarc\Core\Entity\AnrSuperClass; +use Monarc\Core\Entity\ObjectSuperClass; use Monarc\Core\Table\AbstractTable; use Monarc\Core\Table\Interfaces\PositionUpdatableTableInterface; use Monarc\Core\Table\Traits\PositionIncrementTableTrait; @@ -64,4 +65,17 @@ public function findOneByAnrAndName(Anr $anr, string $nameKey, string $nameValue ->getQuery() ->getOneOrNullResult(); } + + /** + * @return MonarcObject[] + */ + public function findGlobalObjectsByAnr(Anr $anr): array + { + return $this->getRepository()->createQueryBuilder('o') + ->where('o.anr = :anr') + ->andWhere('o.scope = ' . ObjectSuperClass::SCOPE_GLOBAL) + ->setParameter('anr', $anr) + ->getQuery() + ->getResult(); + } } diff --git a/src/Table/ScaleImpactTypeTable.php b/src/Table/ScaleImpactTypeTable.php index 8720dab8..52c4128a 100755 --- a/src/Table/ScaleImpactTypeTable.php +++ b/src/Table/ScaleImpactTypeTable.php @@ -9,7 +9,6 @@ use Doctrine\ORM\EntityManager; use Monarc\Core\Table\ScaleImpactTypeTable as CoreScaleImpactTypeTable; -use Monarc\FrontOffice\Entity\Anr; use Monarc\FrontOffice\Entity\ScaleImpactType; class ScaleImpactTypeTable extends CoreScaleImpactTypeTable @@ -18,33 +17,4 @@ public function __construct(EntityManager $entityManager, string $entityName = S { parent::__construct($entityManager, $entityName); } - - // TODO: there is no position field anymore. - /** - * @return ScaleImpactType[] - */ - public function findByAnrOrderedByPosition(Anr $anr): array - { - return $this->getRepository() - ->createQueryBuilder('sit') - ->where('sit.anr = :anr') - ->setParameter('anr', $anr) - ->addOrderBy('sit.position') - ->getQuery() - ->getResult(); - } - - /** - * @return ScaleImpactType[] - */ - public function findByAnrOrderedAndIndexedByPosition(Anr $anr): array - { - return $this->getRepository() - ->createQueryBuilder('sit', 'sit.position') - ->where('sit.anr = :anr') - ->setParameter('anr', $anr) - ->addOrderBy('sit.position') - ->getQuery() - ->getResult(); - } } diff --git a/src/Table/SoaTable.php b/src/Table/SoaTable.php new file mode 100755 index 00000000..2ea365e0 --- /dev/null +++ b/src/Table/SoaTable.php @@ -0,0 +1,55 @@ +getRepository() + ->createQueryBuilder('s') + ->innerJoin('s.measure', 'm') + ->where('s.anr = :anr') + ->andWhere('m.category = :category') + ->setParameter('anr', $anr) + ->setParameter('category', $soaCategory); + + foreach ($order as $filed => $direction) { + $queryBuilder->addOrderBy($filed, $direction); + } + + return $queryBuilder->getQuery()->getResult(); + } + + public function findByAnrAndMeasureUuid(Entity\Anr $anr, string $measureUuid): ?Entity\Soa + { + return $this->getRepository() + ->createQueryBuilder('s') + ->innerJoin('s.measure', 'm') + ->where('s.anr = :anr') + ->andWhere('m.uuid = :measure_uuid') + ->andWhere('m.anr = :anr') + ->setParameter('anr', $anr) + ->setParameter('measure_uuid', $measureUuid) + ->setMaxResults(1) + ->getQuery() + ->getOneOrNullResult(); + } +}