From 59089c7d00b410a4ab11c478f9d7c3ec2377bdbf Mon Sep 17 00:00:00 2001 From: Mark West <66728126+MWest2020@users.noreply.github.com> Date: Wed, 23 Aug 2023 13:52:40 +0200 Subject: [PATCH 01/68] Update .readthedocs.yml --- .readthedocs.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.readthedocs.yml b/.readthedocs.yml index 57d39b31f..f91650cbb 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,5 +1,10 @@ version: 2 +build: + os: ubuntu-22.04 + tools: + python: "3.11" + # Build all formats formats: all From 49e5e343aacb4c85b182d058f5d31b1977d2d297 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Thu, 12 Oct 2023 14:17:30 +0200 Subject: [PATCH 02/68] Make api prefixes dynamic --- api/src/Controller/ZZController.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api/src/Controller/ZZController.php b/api/src/Controller/ZZController.php index 632d5fad8..98b008344 100644 --- a/api/src/Controller/ZZController.php +++ b/api/src/Controller/ZZController.php @@ -61,9 +61,10 @@ public function objectAction( * @TODO This function needs to be more dynamic: /{item}/api/{path}. * This function dynamically handles the custom endpoints. * - * @Route("/klanten/api/{path}", name="dynamic_route_second", requirements={"path" = ".+"}) + * @Route("/{prefix}/api/{path}", name="dynamic_route_second", requirements={"path" = ".+"}) * * @param string|null $path + * @param string|null $bundle * @param Request $request * @param EndpointService $endpointService * @return Response @@ -71,6 +72,7 @@ public function objectAction( */ public function dynamicCustomAction( ?string $path, + ?string $bundle, Request $request, EndpointService $endpointService ): Response { From 0fe478e195e25e91cee98ffb60b2d76986495592 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Tue, 17 Oct 2023 11:55:24 +0200 Subject: [PATCH 03/68] Write the correct value to log --- api/src/Security/OIDCAuthenticator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/Security/OIDCAuthenticator.php b/api/src/Security/OIDCAuthenticator.php index a55b40e90..fd45f9a64 100644 --- a/api/src/Security/OIDCAuthenticator.php +++ b/api/src/Security/OIDCAuthenticator.php @@ -82,7 +82,7 @@ public function authenticate(Request $request): PassportInterface $accessToken = $this->authenticationService->authenticate($method, $identifier, $code); $result = json_decode(base64_decode(explode('.', $accessToken['access_token'])[1]), true); - $this->logger->notice('Received result from OIDC connector', ['authResult' => $result]); + $this->logger->notice('Received result from OIDC connector', ['authResult' => $accessToken]); // Make sure groups is always an array, even if there are no groups. if (is_array($result['groups']) === false && $result['groups'] !== null) { From d44175ffb429472e79f367d69fbb429c63326bed Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Tue, 17 Oct 2023 14:16:37 +0200 Subject: [PATCH 04/68] Create productpage-deploy --- .github/workflows/productpage-deploy | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/workflows/productpage-deploy diff --git a/.github/workflows/productpage-deploy b/.github/workflows/productpage-deploy new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/.github/workflows/productpage-deploy @@ -0,0 +1 @@ + From bfb316f4f58106e3d6e7ee154277de9a1a51321e Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Tue, 17 Oct 2023 14:18:00 +0200 Subject: [PATCH 05/68] Update productpage-deploy --- .github/workflows/productpage-deploy | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/productpage-deploy b/.github/workflows/productpage-deploy index 8b1378917..f8d8648c6 100644 --- a/.github/workflows/productpage-deploy +++ b/.github/workflows/productpage-deploy @@ -1 +1,13 @@ +name: My Product Page Workflow +on: + workflow_dispatch: + schedule: + - cron: '0 0 * * *' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Deploy Product Github Page + uses: OpenCatalogi/productpage-action@0.0.1-main.6.3c445a5 From f3268eb6e18b30776c1984c7e02fb41f0291c21b Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Tue, 17 Oct 2023 14:18:30 +0200 Subject: [PATCH 06/68] Update productpage-deploy --- .github/workflows/productpage-deploy | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/productpage-deploy b/.github/workflows/productpage-deploy index f8d8648c6..4f4090e31 100644 --- a/.github/workflows/productpage-deploy +++ b/.github/workflows/productpage-deploy @@ -2,8 +2,6 @@ name: My Product Page Workflow on: workflow_dispatch: - schedule: - - cron: '0 0 * * *' jobs: build: From 990eb798cf1ae28805856cc763516d2b7c5fcee9 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Wed, 18 Oct 2023 10:35:46 +0200 Subject: [PATCH 07/68] Add accept type for aggregations to zzcontroller --- api/src/Controller/ZZController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/src/Controller/ZZController.php b/api/src/Controller/ZZController.php index 632d5fad8..d2f86ca29 100644 --- a/api/src/Controller/ZZController.php +++ b/api/src/Controller/ZZController.php @@ -181,6 +181,8 @@ private function getAcceptType(Request $request): string return 'html'; case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': return 'docx'; + case 'application/json+aggregations': + return 'aggregations'; }//end switch throw new BadRequestHttpException('No proper accept could be determined'); From 05db2eda44a3390d605ca5ed0a22c5864fb07330 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Wed, 18 Oct 2023 16:36:03 +0200 Subject: [PATCH 08/68] Create an alternate route for working without refresh token, or groups --- api/src/Security/OIDCAuthenticator.php | 53 ++++++++++++++++++++++++-- docker-compose-dex.yml | 21 ++++------ 2 files changed, 57 insertions(+), 17 deletions(-) diff --git a/api/src/Security/OIDCAuthenticator.php b/api/src/Security/OIDCAuthenticator.php index fd45f9a64..a3f495a7d 100644 --- a/api/src/Security/OIDCAuthenticator.php +++ b/api/src/Security/OIDCAuthenticator.php @@ -2,7 +2,10 @@ namespace App\Security; +use App\Entity\SecurityGroup; +use App\Entity\User; use App\Security\User\AuthenticationUser; +use App\Service\ApplicationService; use App\Service\AuthenticationService; use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; @@ -32,6 +35,13 @@ class OIDCAuthenticator extends AbstractAuthenticator */ private LoggerInterface $logger; + /** + * The new authenticationService + */ + private \CommonGateway\CoreBundle\Service\AuthenticationService $coreAuthenticationService; + + private ApplicationService $applicationService; + /** * Constructor @@ -47,7 +57,9 @@ public function __construct( SessionInterface $session, EntityManagerInterface $entityManager, ParameterBagInterface $parameterBag, - LoggerInterface $callLogger + LoggerInterface $callLogger, + \CommonGateway\CoreBundle\Service\AuthenticationService $coreAuthenticationService, + ApplicationService $applicationService ) { $this->authenticationService = $authenticationService; @@ -55,6 +67,8 @@ public function __construct( $this->entityManager = $entityManager; $this->parameterBag = $parameterBag; $this->logger = $callLogger; + $this->coreAuthenticationService = $coreAuthenticationService; + $this->applicationService = $applicationService; } public function supports(Request $request): ?bool @@ -85,9 +99,9 @@ public function authenticate(Request $request): PassportInterface $this->logger->notice('Received result from OIDC connector', ['authResult' => $accessToken]); // Make sure groups is always an array, even if there are no groups. - if (is_array($result['groups']) === false && $result['groups'] !== null) { + if (isset($result['groups']) !== false && (is_array($result['groups']) === false && $result['groups'] !== null)) { $result['groups'] = [$result['groups']]; - } else if (is_array($result['groups']) === false) { + } else if (isset($result['groups']) === false || is_array($result['groups']) === false) { $result['groups'] = []; } @@ -100,10 +114,41 @@ public function authenticate(Request $request): PassportInterface $this->session->set('activeOrganization', $defaultOrganization); if (isset($accessToken['refresh_token'])) { $this->session->set('refresh_token', $accessToken['refresh_token']); + $userIdentifier = $result['email']; + } else { + $doctrineUser = $this->entityManager->getRepository('App:User')->findOneBy(['email' => $result['email']]); + if($doctrineUser instanceof User === false) { + $doctrineUser = new User(); + } + $doctrineUser->setName($result['name']); + $doctrineUser->setEmail($result['email']); + $doctrineUser->setPassword(''); + $doctrineUser->addApplication($this->applicationService->getApplication()); + $doctrineUser->setOrganization($doctrineUser->getApplications()->first()->getOrganization()); + + foreach ($result['groups'] as $group) { + $securityGroup = $this->entityManager->getRepository('App:SecurityGroup')->findOneBy(['name' => $group]); + if ($securityGroup instanceof SecurityGroup === true) { + $doctrineUser->addSecurityGroup($securityGroup); + } + } + + $this->entityManager->persist($doctrineUser); + $this->entityManager->flush(); + + $userIdentifier = $doctrineUser->getId()->toString(); + + $token = $this->coreAuthenticationService->createJwtToken($doctrineUser->getApplications()[0]->getPrivateKey(), $this->coreAuthenticationService->serializeUser($doctrineUser, $this->session)); + + $doctrineUser->setJwtToken($token); + $this->session->set('jwtToken', $token); + + $this->entityManager->persist($doctrineUser); + $this->entityManager->flush(); } return new Passport( - new UserBadge($result['email'], function ($userIdentifier) use ($result) { + new UserBadge($userIdentifier, function ($userIdentifier) use ($result) { return new AuthenticationUser( $userIdentifier, $result['email'], diff --git a/docker-compose-dex.yml b/docker-compose-dex.yml index 93090cb9c..82b8a58dd 100644 --- a/docker-compose-dex.yml +++ b/docker-compose-dex.yml @@ -8,14 +8,6 @@ x-cache: - ${CONTAINER_REGISTRY_BASE}/${CONTAINER_PROJECT_NAME}-cron services: - # gateway-frontend: - # &gateway-frontend - # image: ghcr.io/conductionnl/commonground-gateway-frontend:latest - # depends_on: - # - php - # ports: - # - "83:80" - cron: image: ${CONTAINER_REGISTRY_BASE}/${CONTAINER_PROJECT_NAME}_cron:${APP_ENV} build: ./dockerized-cron @@ -45,6 +37,7 @@ services: - ./api/var/certs:/var/certs:rw,cached - ./gateway:/srv/api/fixtures:rw,cached environment: + - APP_INIT='true' - CONTAINER_REGISTRY_BASE=${CONTAINER_REGISTRY_BASE} - CONTAINER_PROJECT_NAME=${CONTAINER_PROJECT_NAME} - DATABASE_URL=postgres://api-platform:!ChangeMe!@db/api?serverVersion=10.1 @@ -106,6 +99,7 @@ services: - CRON_RUNNER_ENABLED=${CRON_RUNNER_ENABLED} - CRON_RUNNER_CRONTAB=${CRON_RUNNER_CRONTAB} - CRON_RUNNER_CONCURRENCY_POLICY=${CRON_RUNNER_CONCURRENCY_POLICY} + - LOG_LEVEL=${LOG_LEVEL} ports: - "82:80" cap_drop: @@ -170,13 +164,14 @@ services: - "5432:5432" mongodb: - image: mongo + image: mongo:4.4.14 restart: always environment: MONGO_INITDB_ROOT_USERNAME: api-platform MONGO_INITDB_ROOT_PASSWORD: '!ChangeMe!' ports: - "27017:27017" + dex: image: dexidp/dex:latest ports: @@ -204,12 +199,12 @@ services: ports: - 389:389 - 636:636 - + rabbitmq: - image: rabbitmq:latest + image: rabbitmq:3.12.0 environment: - - RABBITMQ_DEFAULT_USER=${RABBITMQ_USERNAME} - - RABBITMQ_DEFAULT_PASS=${RABBITMQ_PASSWORD} + - RABBITMQ_DEFAULT_USER=${RABBITMQ_USERNAME} + - RABBITMQ_DEFAULT_PASS=${RABBITMQ_PASSWORD} networks: From 23a9d5bf259890f599faac7dd41f59f0335ed7bd Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Wed, 18 Oct 2023 16:39:05 +0200 Subject: [PATCH 09/68] Add some docblocks --- api/src/Security/OIDCAuthenticator.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/api/src/Security/OIDCAuthenticator.php b/api/src/Security/OIDCAuthenticator.php index a3f495a7d..2416d6ca6 100644 --- a/api/src/Security/OIDCAuthenticator.php +++ b/api/src/Security/OIDCAuthenticator.php @@ -36,10 +36,13 @@ class OIDCAuthenticator extends AbstractAuthenticator private LoggerInterface $logger; /** - * The new authenticationService + * @var \CommonGateway\CoreBundle\Service\AuthenticationService The new authenticationService */ private \CommonGateway\CoreBundle\Service\AuthenticationService $coreAuthenticationService; + /** + * @var ApplicationService The application service + */ private ApplicationService $applicationService; @@ -51,6 +54,8 @@ class OIDCAuthenticator extends AbstractAuthenticator * @param EntityManagerInterface $entityManager The entity manager * @param ParameterBagInterface $parameterBag The Parameter Bag * @param LoggerInterface $callLogger The call logger + * @param \CommonGateway\CoreBundle\Service\AuthenticationService $coreAuthenticationService The new auth service + * @param ApplicationService $applicationService $the application service */ public function __construct( AuthenticationService $authenticationService, From c06b55a72ba4891581c3389d662fa25679475f39 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Wed, 18 Oct 2023 16:45:30 +0200 Subject: [PATCH 10/68] Remove unecessary capabilities from nginx container --- api/helm/commonground-gateway-1.5.4.tgz | Bin 0 -> 297195 bytes api/helm/commonground-gateway/Chart.yaml | 2 +- api/helm/commonground-gateway/values.yaml | 6 - api/helm/index.yaml | 132 +++++++++++++--------- 4 files changed, 82 insertions(+), 58 deletions(-) create mode 100644 api/helm/commonground-gateway-1.5.4.tgz diff --git a/api/helm/commonground-gateway-1.5.4.tgz b/api/helm/commonground-gateway-1.5.4.tgz new file mode 100644 index 0000000000000000000000000000000000000000..3471833d6645aceff983b193bda8be13d17e2e94 GIT binary patch literal 297195 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

uXaWz74x^yEt*h}*8C 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 Date: Mon, 23 Oct 2023 14:03:56 +0200 Subject: [PATCH 11/68] remove to do --- api/src/Controller/ZZController.php | 1 - 1 file changed, 1 deletion(-) diff --git a/api/src/Controller/ZZController.php b/api/src/Controller/ZZController.php index cb86c329f..8806b7fed 100644 --- a/api/src/Controller/ZZController.php +++ b/api/src/Controller/ZZController.php @@ -58,7 +58,6 @@ public function objectAction( } /** - * @TODO This function needs to be more dynamic: /{item}/api/{path}. * This function dynamically handles the custom endpoints. * * @Route("/{prefix}/api/{path}", name="dynamic_route_second", requirements={"path" = ".+"}) From bd5b8c040d242ee5649625c43d315fb9812c80f9 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Tue, 24 Oct 2023 09:26:33 +0200 Subject: [PATCH 12/68] Add creating an actionEvent on log --- api/src/Logger/SessionDataProcessor.php | 39 ++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/api/src/Logger/SessionDataProcessor.php b/api/src/Logger/SessionDataProcessor.php index ae9014c48..144b4977b 100644 --- a/api/src/Logger/SessionDataProcessor.php +++ b/api/src/Logger/SessionDataProcessor.php @@ -2,6 +2,9 @@ namespace App\Logger; +use App\Event\ActionEvent; +use Doctrine\ORM\EntityManagerInterface; +use Psr\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Session\SessionInterface; @@ -10,14 +13,34 @@ class SessionDataProcessor private SessionInterface $session; private RequestStack $requestStack; - public function __construct(SessionInterface $session, RequestStack $requestStack) + /** + * @var EventDispatcherInterface The event dispatcher. + */ + private EventDispatcherInterface $eventDispatcher; + + private EntityManagerInterface $entityManager; + + /** + * @param SessionInterface $session + * @param RequestStack $requestStack + * @param EventDispatcherInterface $eventDispatcher + */ + public function __construct( + SessionInterface $session, + RequestStack $requestStack, + EventDispatcherInterface $eventDispatcher, + EntityManagerInterface $entityManager + ) { $this->session = $session; $this->requestStack = $requestStack; + $this->eventDispatcher = $eventDispatcher; + $this->entityManager = $entityManager; } public function __invoke(array $record): array { + $record['context']['session'] = $this->session->getId(); $record['context']['process'] = $this->session->has('process') ? $this->session->get('process') : ''; $record['context']['endpoint'] = $this->session->has('endpoint') ? $this->session->get('endpoint') : ''; @@ -34,6 +57,20 @@ public function __invoke(array $record): array $record['context']['host'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getHost() : ''; $record['context']['ip'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getClientIp() : ''; + + if ($this->entityManager->getConnection()->isConnected() === true + && in_array( + $this->entityManager->getConnection()->getDatabase(), + $this->entityManager->getConnection()->getSchemaManager()->listDatabases() + ) === true + ){ + $event = new ActionEvent('commongateway.action.event', $record, 'core.log.create'); + + $this->eventDispatcher->dispatch($event, 'commongateway.action.event'); + + $record = $event->getData(); + } + return $record; } } From 73e00511181627ae83d29b304cb52fded5125c41 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Tue, 24 Oct 2023 09:46:20 +0200 Subject: [PATCH 13/68] update comment in EmailService --- api/src/Service/EmailService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/Service/EmailService.php b/api/src/Service/EmailService.php index ae5a38d7d..e2f240e79 100644 --- a/api/src/Service/EmailService.php +++ b/api/src/Service/EmailService.php @@ -11,7 +11,7 @@ use Twig\Error\RuntimeError; use Twig\Error\SyntaxError; -// todo: move this to an email plugin with the following packages from composer.json: symfony/mailer, symfony/mailgun-mailer & symfony/http-client +// todo: move this to an email plugin with the following packages from composer.json: symfony/mailer, symfony/mailgun-mailer, symfony/sendinblue-mailer & symfony/http-client /** * @Author Wilco Louwerse , Ruben van der Linde , Sarai Misidjan From 7997e58793f81eb2fd346a8315b092cfc3573eba Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Tue, 24 Oct 2023 10:28:11 +0200 Subject: [PATCH 14/68] Working conditions --- api/src/Logger/SessionDataProcessor.php | 1 + 1 file changed, 1 insertion(+) diff --git a/api/src/Logger/SessionDataProcessor.php b/api/src/Logger/SessionDataProcessor.php index 144b4977b..537753cba 100644 --- a/api/src/Logger/SessionDataProcessor.php +++ b/api/src/Logger/SessionDataProcessor.php @@ -63,6 +63,7 @@ public function __invoke(array $record): array $this->entityManager->getConnection()->getDatabase(), $this->entityManager->getConnection()->getSchemaManager()->listDatabases() ) === true + && $this->entityManager->getConnection()->getSchemaManager()->tablesExist('action') === true ){ $event = new ActionEvent('commongateway.action.event', $record, 'core.log.create'); From 11c7f30e44e8bd929959c7cbe621d187c5528439 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Tue, 24 Oct 2023 11:11:24 +0200 Subject: [PATCH 15/68] Split adding session data and sending a log --- api/src/Logger/SessionDataProcessor.php | 65 ++++++++++++++++++------- 1 file changed, 48 insertions(+), 17 deletions(-) diff --git a/api/src/Logger/SessionDataProcessor.php b/api/src/Logger/SessionDataProcessor.php index 537753cba..0ce8592c2 100644 --- a/api/src/Logger/SessionDataProcessor.php +++ b/api/src/Logger/SessionDataProcessor.php @@ -38,26 +38,43 @@ public function __construct( $this->entityManager = $entityManager; } - public function __invoke(array $record): array + /** + * Update the context with data from the session and the request stack. + * + * @param array $context The context to update. + * + * @return array The updated context. + */ + public function updateContext($context): array { + $context['session'] = $this->session->getId(); + $context['process'] = $this->session->has('process') ? $this->session->get('process') : ''; + $context['endpoint'] = $this->session->has('endpoint') ? $this->session->get('endpoint') : ''; + $context['schema'] = $this->session->has('schema') ? $this->session->get('schema') : ''; + $context['object'] = $this->session->has('object') === true ? $this->session->get('object') : ''; + $context['cronjob'] = $this->session->has('cronjob') ? $this->session->get('cronjob') : ''; + $context['action'] = $this->session->has('cronjob') ? $this->session->get('action') : ''; + $context['mapping'] = $this->session->has('mapping') ? $this->session->get('mapping') : ''; + $context['source'] = $this->session->has('source') ? $this->session->get('source') : ''; + $context['plugin'] = isset($record['data']['plugin']) === true ? $record['data']['plugin'] : ''; + $context['user'] = $this->session->has('user') ? $this->session->get('user') : ''; + $context['organization'] = $this->session->has('organization') ? $this->session->get('organization') : ''; + $context['application'] = $this->session->has('application') ? $this->session->get('application') : ''; + $context['host'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getHost() : ''; + $context['ip'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getClientIp() : ''; - $record['context']['session'] = $this->session->getId(); - $record['context']['process'] = $this->session->has('process') ? $this->session->get('process') : ''; - $record['context']['endpoint'] = $this->session->has('endpoint') ? $this->session->get('endpoint') : ''; - $record['context']['schema'] = $this->session->has('schema') ? $this->session->get('schema') : ''; - $record['context']['object'] = $this->session->has('object') === true ? $this->session->get('object') : ''; - $record['context']['cronjob'] = $this->session->has('cronjob') ? $this->session->get('cronjob') : ''; - $record['context']['action'] = $this->session->has('cronjob') ? $this->session->get('action') : ''; - $record['context']['mapping'] = $this->session->has('mapping') ? $this->session->get('mapping') : ''; - $record['context']['source'] = $this->session->has('source') ? $this->session->get('source') : ''; - $record['context']['plugin'] = isset($record['data']['plugin']) === true ? $record['data']['plugin'] : ''; - $record['context']['user'] = $this->session->has('user') ? $this->session->get('user') : ''; - $record['context']['organization'] = $this->session->has('organization') ? $this->session->get('organization') : ''; - $record['context']['application'] = $this->session->has('application') ? $this->session->get('application') : ''; - $record['context']['host'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getHost() : ''; - $record['context']['ip'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getClientIp() : ''; - + return $context; + } + /** + * Dispatches a log create action. + * + * @param array $record The log record that is created. + * + * @return array The resulting log record after the action. + */ + public function dispatchLogCreateAction(array $record): array + { if ($this->entityManager->getConnection()->isConnected() === true && in_array( $this->entityManager->getConnection()->getDatabase(), @@ -74,4 +91,18 @@ public function __invoke(array $record): array return $record; } + + /** + * Updates the log record with data from the session, request and from actions. + * + * @param array $record The log record. + * + * @return array The updated log record. + */ + public function __invoke(array $record): array + { + $record['context'] = $this->updateContext($record['context']); + + return $this->dispatchLogCreateAction($record); + } } From b301d8b02a6554d63c115d799ac8918d030c071b Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Tue, 24 Oct 2023 11:17:54 +0200 Subject: [PATCH 16/68] Add docblock for entitymanager --- api/src/Logger/SessionDataProcessor.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/src/Logger/SessionDataProcessor.php b/api/src/Logger/SessionDataProcessor.php index 0ce8592c2..500294ecb 100644 --- a/api/src/Logger/SessionDataProcessor.php +++ b/api/src/Logger/SessionDataProcessor.php @@ -18,6 +18,9 @@ class SessionDataProcessor */ private EventDispatcherInterface $eventDispatcher; + /** + * @var EntityManagerInterface The entity manager. + */ private EntityManagerInterface $entityManager; /** From e24d12a0648e3cd470ebf07be5df35dd5aae0976 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Tue, 24 Oct 2023 11:19:03 +0200 Subject: [PATCH 17/68] added new log email (example) template --- .../emails/gateway/new-log-e-mail.html.twig | 491 ++++++++++++++++++ 1 file changed, 491 insertions(+) create mode 100644 api/templates/emails/gateway/new-log-e-mail.html.twig diff --git a/api/templates/emails/gateway/new-log-e-mail.html.twig b/api/templates/emails/gateway/new-log-e-mail.html.twig new file mode 100644 index 000000000..0c02d3133 --- /dev/null +++ b/api/templates/emails/gateway/new-log-e-mail.html.twig @@ -0,0 +1,491 @@ +{# todo: move this to an email plugin (see EmailService.php) #} + + + + + + {{ subject }} + + + + + + + + + + + + + + + From 9dbf4d3427b3c851ad58ea70c2807b50ad32e47f Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Tue, 24 Oct 2023 13:12:57 +0200 Subject: [PATCH 18/68] Fixes for handling logging actionEvents and creating emails --- api/src/Logger/SessionDataProcessor.php | 1 + api/src/Service/EmailService.php | 5 ++++- .../emails/gateway/new-log-e-mail.html.twig | 16 ++++++++++++++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/api/src/Logger/SessionDataProcessor.php b/api/src/Logger/SessionDataProcessor.php index 0ce8592c2..a85960e7d 100644 --- a/api/src/Logger/SessionDataProcessor.php +++ b/api/src/Logger/SessionDataProcessor.php @@ -81,6 +81,7 @@ public function dispatchLogCreateAction(array $record): array $this->entityManager->getConnection()->getSchemaManager()->listDatabases() ) === true && $this->entityManager->getConnection()->getSchemaManager()->tablesExist('action') === true + && in_array($record['level_name'], ['DEBUG', 'INFO']) === false ){ $event = new ActionEvent('commongateway.action.event', $record, 'core.log.create'); diff --git a/api/src/Service/EmailService.php b/api/src/Service/EmailService.php index e2f240e79..7625caabc 100644 --- a/api/src/Service/EmailService.php +++ b/api/src/Service/EmailService.php @@ -71,8 +71,11 @@ private function sendEmail(): bool $variables = []; foreach ($this->configuration['variables'] as $key => $variable) { - if (array_key_exists($variable, $this->data['response'])) { + // Response is the default used for creating emails after an /api endpoint has been called and returned a response. + if (isset($this->data['response']) === true && array_key_exists($variable, $this->data['response'])) { $variables[$key] = $this->data['response'][$variable]; + } elseif (array_key_exists($variable, $this->data)) { + $variables[$key] = $this->data[$variable]; } } diff --git a/api/templates/emails/gateway/new-log-e-mail.html.twig b/api/templates/emails/gateway/new-log-e-mail.html.twig index 0c02d3133..672102e69 100644 --- a/api/templates/emails/gateway/new-log-e-mail.html.twig +++ b/api/templates/emails/gateway/new-log-e-mail.html.twig @@ -379,6 +379,7 @@ } .logo { + width: 28%; display: block; } @@ -467,16 +468,27 @@ +

Hallo,

- De volgende {{log_level}} log met code {{code}} is aangemaakt: + (At: {{ datetime | nl2br }}) +
+ The following {{level_name}} log for channel '{{channel}}' has been created:


{{ description | nl2br }}


-

Met vriendelijke groet,

+

Yours sincerely,

Conduction

+
From 978efcdfada862c0f34e5e98822ff463c06c2c10 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Tue, 24 Oct 2023 13:14:12 +0200 Subject: [PATCH 19/68] Added sendinblue-mailer to composer --- api/composer.json | 1 + api/composer.lock | 67 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/api/composer.json b/api/composer.json index 29e506841..aa2726af6 100644 --- a/api/composer.json +++ b/api/composer.json @@ -54,6 +54,7 @@ "symfony/proxy-manager-bridge": "5.3.*", "symfony/runtime": "5.3.*", "symfony/security-bundle": "5.3.*", + "symfony/sendinblue-mailer": "5.3.*", "symfony/serializer": "5.3.*", "symfony/twig-bundle": "5.3.*", "symfony/validator": "5.3.*", diff --git a/api/composer.lock b/api/composer.lock index ed73b85fd..299b34e96 100644 --- a/api/composer.lock +++ b/api/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "26900a024cf0ecaf8966b2e29a938737", + "content-hash": "7c977e35c7047aed418241d4b550d8af", "packages": [ { "name": "adbario/php-dot-notation", @@ -12141,6 +12141,71 @@ ], "time": "2021-11-05T16:25:46+00:00" }, + { + "name": "symfony/sendinblue-mailer", + "version": "v5.3.14", + "source": { + "type": "git", + "url": "https://github.com/symfony/sendinblue-mailer.git", + "reference": "269383a0dac2ab4d5a1a0f0c042ba114d132cb24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/sendinblue-mailer/zipball/269383a0dac2ab4d5a1a0f0c042ba114d132cb24", + "reference": "269383a0dac2ab4d5a1a0f0c042ba114d132cb24", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/mailer": "^5.1" + }, + "require-dev": { + "symfony/http-client": "^4.4|^5.0" + }, + "type": "symfony-bridge", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\Bridge\\Sendinblue\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Sendinblue Mailer Bridge", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/sendinblue-mailer/tree/v5.3.14" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-01-02T09:51:59+00:00" + }, { "name": "symfony/serializer", "version": "v5.3.12", From defc20f15fc3c239cdc3623a3d86b7d4d2935e2a Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Tue, 24 Oct 2023 13:58:18 +0200 Subject: [PATCH 20/68] Exclude Notice and Warning from Log Actions --- api/src/Logger/SessionDataProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/Logger/SessionDataProcessor.php b/api/src/Logger/SessionDataProcessor.php index a85960e7d..bd1d66a91 100644 --- a/api/src/Logger/SessionDataProcessor.php +++ b/api/src/Logger/SessionDataProcessor.php @@ -81,7 +81,7 @@ public function dispatchLogCreateAction(array $record): array $this->entityManager->getConnection()->getSchemaManager()->listDatabases() ) === true && $this->entityManager->getConnection()->getSchemaManager()->tablesExist('action') === true - && in_array($record['level_name'], ['DEBUG', 'INFO']) === false + && in_array($record['level_name'], ['DEBUG', 'INFO', 'NOTICE', 'WARNING']) === false ){ $event = new ActionEvent('commongateway.action.event', $record, 'core.log.create'); From 1ee5cca9adcf7baec7a8b26278672d51de990f20 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Tue, 24 Oct 2023 14:30:09 +0200 Subject: [PATCH 21/68] Also accept group as field for groups --- api/src/Security/OIDCAuthenticator.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/src/Security/OIDCAuthenticator.php b/api/src/Security/OIDCAuthenticator.php index 2416d6ca6..4f6e1b932 100644 --- a/api/src/Security/OIDCAuthenticator.php +++ b/api/src/Security/OIDCAuthenticator.php @@ -108,6 +108,9 @@ public function authenticate(Request $request): PassportInterface $result['groups'] = [$result['groups']]; } else if (isset($result['groups']) === false || is_array($result['groups']) === false) { $result['groups'] = []; + if (isset($result['group']) === true && is_array($result['group']) === true) { + $result['groups'] = $result['group']; + } } // Set default organization in session for multitenancy (see how this is done in other Authenticators, this can be different for each one!) From bfc72e15df53dccfcb25f0ed6fe17b34ee1383be Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Tue, 24 Oct 2023 14:48:55 +0200 Subject: [PATCH 22/68] Remove refresh token and default to fallback route as ADFS does not conform --- api/src/Security/OIDCAuthenticator.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/api/src/Security/OIDCAuthenticator.php b/api/src/Security/OIDCAuthenticator.php index 4f6e1b932..a3b6f1db5 100644 --- a/api/src/Security/OIDCAuthenticator.php +++ b/api/src/Security/OIDCAuthenticator.php @@ -120,10 +120,10 @@ public function authenticate(Request $request): PassportInterface $this->session->set('organizations', $organizations); $this->session->set('parentOrganizations', $parentOrganizations); $this->session->set('activeOrganization', $defaultOrganization); - if (isset($accessToken['refresh_token'])) { - $this->session->set('refresh_token', $accessToken['refresh_token']); - $userIdentifier = $result['email']; - } else { +// if (isset($accessToken['refresh_token'])) { +// $this->session->set('refresh_token', $accessToken['refresh_token']); +// $userIdentifier = $result['email']; +// } else { $doctrineUser = $this->entityManager->getRepository('App:User')->findOneBy(['email' => $result['email']]); if($doctrineUser instanceof User === false) { $doctrineUser = new User(); @@ -153,7 +153,7 @@ public function authenticate(Request $request): PassportInterface $this->entityManager->persist($doctrineUser); $this->entityManager->flush(); - } +// } return new Passport( new UserBadge($userIdentifier, function ($userIdentifier) use ($result) { From 68b4d82e657bfa144c1809b9db9d1f98d08763f2 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Tue, 24 Oct 2023 15:19:38 +0200 Subject: [PATCH 23/68] catch another ADFS error --- api/src/Security/OIDCAuthenticator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/Security/OIDCAuthenticator.php b/api/src/Security/OIDCAuthenticator.php index a3b6f1db5..8fbf5111d 100644 --- a/api/src/Security/OIDCAuthenticator.php +++ b/api/src/Security/OIDCAuthenticator.php @@ -128,7 +128,7 @@ public function authenticate(Request $request): PassportInterface if($doctrineUser instanceof User === false) { $doctrineUser = new User(); } - $doctrineUser->setName($result['name']); + $doctrineUser->setName($result['name'] ?? $result['sub']); $doctrineUser->setEmail($result['email']); $doctrineUser->setPassword(''); $doctrineUser->addApplication($this->applicationService->getApplication()); From f2f350ebd6f89e6ef43fadc76cc319dcbfccd80d Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Tue, 24 Oct 2023 15:59:46 +0200 Subject: [PATCH 24/68] Fix a typo --- api/src/Logger/SessionDataProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/Logger/SessionDataProcessor.php b/api/src/Logger/SessionDataProcessor.php index 3641d8dc9..317f119b2 100644 --- a/api/src/Logger/SessionDataProcessor.php +++ b/api/src/Logger/SessionDataProcessor.php @@ -56,7 +56,7 @@ public function updateContext($context): array $context['schema'] = $this->session->has('schema') ? $this->session->get('schema') : ''; $context['object'] = $this->session->has('object') === true ? $this->session->get('object') : ''; $context['cronjob'] = $this->session->has('cronjob') ? $this->session->get('cronjob') : ''; - $context['action'] = $this->session->has('cronjob') ? $this->session->get('action') : ''; + $context['action'] = $this->session->has('action') ? $this->session->get('action') : ''; $context['mapping'] = $this->session->has('mapping') ? $this->session->get('mapping') : ''; $context['source'] = $this->session->has('source') ? $this->session->get('source') : ''; $context['plugin'] = isset($record['data']['plugin']) === true ? $record['data']['plugin'] : ''; From 425954d14784623f650f606e8cb0f842b6dc21e2 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Thu, 26 Oct 2023 12:01:49 +0200 Subject: [PATCH 25/68] Renamed log created event, consistency with other default gateway events --- api/src/Logger/SessionDataProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/Logger/SessionDataProcessor.php b/api/src/Logger/SessionDataProcessor.php index 317f119b2..ce568dbca 100644 --- a/api/src/Logger/SessionDataProcessor.php +++ b/api/src/Logger/SessionDataProcessor.php @@ -86,7 +86,7 @@ public function dispatchLogCreateAction(array $record): array && $this->entityManager->getConnection()->getSchemaManager()->tablesExist('action') === true && in_array($record['level_name'], ['DEBUG', 'INFO', 'NOTICE', 'WARNING']) === false ){ - $event = new ActionEvent('commongateway.action.event', $record, 'core.log.create'); + $event = new ActionEvent('commongateway.action.event', $record, 'commongateway.log.create'); $this->eventDispatcher->dispatch($event, 'commongateway.action.event'); From 8b01caeedd9b670a01903dfacd131550d135cb92 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Fri, 27 Oct 2023 16:54:22 +0200 Subject: [PATCH 26/68] Make sure to not create new (duplicate) subobjects when we don't need to --- api/src/Entity/Value.php | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/api/src/Entity/Value.php b/api/src/Entity/Value.php index e83e0f249..49d2d03ee 100644 --- a/api/src/Entity/Value.php +++ b/api/src/Entity/Value.php @@ -659,21 +659,38 @@ public function setValue($value, bool $unsafe = false, ?DateTimeInterface $dateM foreach ($valueArray as $value) { // Catch Array input (for hydrator) if (is_array($value)) { - $object = new ObjectEntity($this->getAttribute()->getObject()); + $object = null; + + // Make sure to not create new objects if we don't have to... + if (isset($value['id'])) { + $objects = $this->objects->filter(function ($item) use ($value) { + return $item->getId() !== null && $item->getId()->toString() === $value['id']; + }); + + if (count($objects) > 0) { + $object = $objects[0]; + } + } + if ($object instanceof ObjectEntity === false) { + // failsafe to not create duplicate sub objects. In some weird cases $objects[0] doesn't return an ObjectEntity. + if (isset($objects) === true && count($objects) > 0) { + continue; + } + $object = new ObjectEntity($this->getAttribute()->getObject()); + } $object->setOwner($this->getObjectEntity()->getOwner()); $object->setApplication($this->getObjectEntity()->getApplication()); $object->setOrganization($this->getObjectEntity()->getOrganization()); $object->hydrate($value, $unsafe, $dateModified); $value = $object; - $this->hydratedObjects[] = $object; } if (is_string($value)) { $idArray[] = $value; } elseif (!$value) { continue; - } elseif ($value instanceof ObjectEntity) { + } elseif ($value instanceof ObjectEntity && $this->objects->contains($value) === false) { $this->addObject($value); } } From 1f4773d29264cde1939260babe2e9a0f8d1fbce4 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Thu, 9 Nov 2023 12:02:53 +0100 Subject: [PATCH 27/68] Some code cleanup for login, applications and session (/logging) stuff --- api/src/Controller/LoginController.php | 5 +- api/src/Controller/UserController.php | 31 +-- api/src/Repository/ObjectEntityRepository.php | 185 +++++++++--------- api/src/Security/ApiKeyAuthenticator.php | 23 +-- api/src/Security/OIDCAuthenticator.php | 26 +-- api/src/Security/TokenAuthenticator.php | 3 +- api/src/Service/ApplicationService.php | 79 ++++---- api/src/Service/AuthorizationService.php | 6 +- api/src/Service/EavService.php | 40 ++-- api/src/Service/ObjectEntityService.php | 14 +- 10 files changed, 181 insertions(+), 231 deletions(-) diff --git a/api/src/Controller/LoginController.php b/api/src/Controller/LoginController.php index 6df1818aa..0ca3a1caf 100644 --- a/api/src/Controller/LoginController.php +++ b/api/src/Controller/LoginController.php @@ -52,9 +52,8 @@ public function MeAction(Request $request, CommonGroundService $commonGroundServ 'last_name' => $this->getUser()->getLastName(), 'name' => $this->getUser()->getName(), 'email' => $this->getUser()->getEmail(), - // TODO: if we have no person connected to this user create one? with $this->createPersonForUser() - 'person' => $userService->getPersonForUser($this->getUser()), - 'organization' => $userService->getOrganizationForUser($this->getUser()), + 'person' => $userService->getPersonForUser($this->getUser()), // Get person ObjectEntity (->Entity with function = person) by id + 'organization' => $userService->getOrganizationForUser($this->getUser()), // Get organization ObjectEntity (->Entity with function = organization) by id ]; $result = json_encode($result); } else { diff --git a/api/src/Controller/UserController.php b/api/src/Controller/UserController.php index a4393d862..be91f2227 100644 --- a/api/src/Controller/UserController.php +++ b/api/src/Controller/UserController.php @@ -100,17 +100,8 @@ public function resetTokenAction(SerializerInterface $serializer, \CommonGateway $user = $this->entityManager->getRepository('App:User')->find($user->getUserIdentifier()); - if ($user->getOrganization() !== null) { - $organizations[] = $user->getOrganization(); - } - foreach ($user->getApplications() as $application) { - if ($application->getOrganization() !== null) { - $organizations[] = $application->getOrganization(); - } - } - - // If user has no organization, we default activeOrganization to an organization of a userGroup this user has and else the application organization; - $this->session->set('activeOrganization', $user->getOrganization()->getId()->toString()); + // Set organization in session + $this->session->set('organization', $user->getOrganization() !== null ? $user->getOrganization()->getId()->toString() : null); $user->setJwtToken($authenticationService->createJwtToken($user->getApplications()[0]->getPrivateKey(), $authenticationService->serializeUser($user, $this->session))); @@ -166,10 +157,11 @@ public function createAuthenticationUser(User $user): AuthenticationUser } /** - * Add the logged in user to session. + * Add the logged-in user to session. * - * @param User $user The user to log in. + * @param User $user The user to log in. * @param EventDispatcherInterface $eventDispatcher The event dispatcher. + * @param Request $request * * @return void */ @@ -203,17 +195,8 @@ public function apiLoginAction(Request $request, UserPasswordHasherInterface $ha return new Response(json_encode($response), 401, ['Content-type' => 'application/json']); } - if ($user->getOrganization() !== null) { - $organizations[] = $user->getOrganization(); - } - foreach ($user->getApplications() as $application) { - if ($application->getOrganization() !== null) { - $organizations[] = $application->getOrganization(); - } - } - - // If user has no organization, we default activeOrganization to an organization of a userGroup this user has and else the application organization; - $this->session->set('activeOrganization', $user->getOrganization()->getId()->toString()); + // Set organization in session + $this->session->set('organization', $user->getOrganization() !== null ? $user->getOrganization()->getId()->toString() : null); $token = $authenticationService->createJwtToken($user->getApplications()[0]->getPrivateKey(), $authenticationService->serializeUser($user, $this->session)); diff --git a/api/src/Repository/ObjectEntityRepository.php b/api/src/Repository/ObjectEntityRepository.php index 6823c370f..8da2f4a17 100644 --- a/api/src/Repository/ObjectEntityRepository.php +++ b/api/src/Repository/ObjectEntityRepository.php @@ -35,6 +35,96 @@ public function __construct(ManagerRegistry $registry, SessionInterface $session parent::__construct($registry, ObjectEntity::class); } + /** + * Gets and returns an array with the allowed filters on an Entity (including its subEntities / sub-filters). + * + * @param Entity $Entity The Entity we are currently doing a get collection on. + * @param string $prefix + * @param int $level + * @param bool $embedded + * + * @return array The array with allowed filters. + */ + public function getFilterParameters(Entity $Entity, string $prefix = '', int $level = 1, bool $embedded = false): array + { + $prefix = $embedded && $level === 2 ? "embedded.$prefix" : $prefix; + + //todo: we only check for the allowed keys/attributes to filter on, if this attribute is a dateTime (or date), we should also check if the value is a valid dateTime string? + // NOTE: + // Filter id looks for ObjectEntity id and externalId + // Filter _id looks specifically/only for ObjectEntity id + // Filter _externalId looks specifically/only for ObjectEntity externalId + + // defaults + $filters = [ + $prefix.'id', $prefix.'_id', $prefix.'_externalId', $prefix.'_uri', $prefix.'_self', $prefix.'_organization', + $prefix.'_application', $prefix.'_dateCreated', $prefix.'_dateModified', $prefix.'_mapping', + ]; + + foreach ($Entity->getAttributes() as $attribute) { + if (in_array($attribute->getType(), ['string', 'date', 'datetime', 'integer', 'float', 'number', 'boolean']) && $attribute->getSearchable()) { + $filters[] = $prefix.$attribute->getName(); + } elseif ($attribute->getObject() && $level < 3 && !str_contains($prefix, $attribute->getName().'.')) { + $attribute->getSearchable() && $filters[] = $prefix.$attribute->getName(); + $embeddedString = $embedded && $level > 1 ? 'embedded.' : ''; + $filters = array_merge($filters, $this->getFilterParameters($attribute->getObject(), $prefix.$embeddedString.$attribute->getName().'.', $level + 1, $embedded)); + } + } + + return $filters; + } + + /** + * Gets and returns an array with the allowed sortable attributes on an Entity (including its subEntities). + * + * @param Entity $Entity The Entity we are currently doing a get collection on. + * @param string $prefix + * @param int $level + * @param bool $embedded + * + * @return array The array with allowed attributes to sort by. + */ + public function getOrderParameters(Entity $Entity, string $prefix = '', int $level = 1, bool $embedded = false): array + { + $prefix = $embedded && $level === 2 ? "embedded.$prefix" : $prefix; + // defaults + $sortable = [$prefix.'_dateCreated', $prefix.'_dateModified']; + + foreach ($Entity->getAttributes() as $attribute) { + if (in_array($attribute->getType(), ['string', 'date', 'datetime', 'integer', 'float', 'number']) && $attribute->getSortable()) { + $sortable[] = $prefix.$attribute->getName(); + } elseif ($attribute->getObject() && $level < 3 && !str_contains($prefix, $attribute->getName().'.')) { + $embeddedString = $embedded && $level > 1 ? 'embedded.' : ''; + $sortable = array_merge($sortable, $this->getOrderParameters($attribute->getObject(), $prefix.$embeddedString.$attribute->getName().'.', $level + 1)); + } + } + + return $sortable; + } + + /** + * Finds object entities on their id or a sourceId of a synchronization this ObjectEntity has. + * + * @param string $identifier + * + * @throws NonUniqueResultException + * + * @return ObjectEntity The found object entity + */ + public function findByAnyId(string $identifier): ?ObjectEntity + { + $query = $this->createQueryBuilder('o') + ->leftJoin('o.synchronizations', 's') + ->where('s.sourceId = :identifier') + ->setParameter('identifier', $identifier); + + if (Uuid::isValid($identifier)) { + $query->orWhere('o.id = :identifier'); + } + + return $query->getQuery()->getOneOrNullResult(); + } + /** * Does the same as findByEntity(), but also returns an integer representing the total amount of results using the input to create a sql statement. $entity is required. * @@ -47,6 +137,7 @@ public function __construct(ManagerRegistry $registry, SessionInterface $session * @throws NoResultException|NonUniqueResultException * * @return array With a key 'objects' containing the actual objects found and a key 'total' with an integer representing the total amount of results found. + * @deprecated */ public function findAndCountByEntity(Entity $entity, array $filters = [], array $order = [], int $offset = 0, int $limit = 25): array { @@ -81,6 +172,7 @@ public function findAndCountByEntity(Entity $entity, array $filters = [], array * @throws Exception * * @return array Returns an array of ObjectEntity objects + * @deprecated */ public function findByEntity(Entity $entity, array $filters = [], array $order = [], int $offset = 0, int $limit = 25, QueryBuilder $query = null): array { @@ -103,6 +195,7 @@ public function findByEntity(Entity $entity, array $filters = [], array $order = * @throws NoResultException|NonUniqueResultException * * @return int Returns an integer, for the total ObjectEntities found with this Entity and with the given filters. + * @deprecated */ public function countByEntity(Entity $entity, array $filters = [], QueryBuilder $query = null): int { @@ -155,11 +248,11 @@ private function createQuery(Entity $entity, array $filters = [], array $order = // Multitenancy, only show objects this user is allowed to see. // Only show objects this user owns or object that have an organization this user is part of or that are inhereted down the line - $organizations = $this->session->get('organizations', []); + $organizations = []; $parentOrganizations = []; // Make sure we only check for parentOrganizations if inherited is true in the (ObjectEntity)->entity->inherited if ($entity->getInherited()) { - $parentOrganizations = $this->session->get('parentOrganizations', []); + $parentOrganizations = []; } // $query->andWhere('o.organization IN (:organizations) OR o.organization IN (:parentOrganizations) OR o.organization = :defaultOrganization OR o.owner = :userId') @@ -872,92 +965,4 @@ private function makeKeySqlFriendly(string $key): string // todo, probably add more special characters to replace... return str_replace('-', 'Dash', $key); } - - /** - * Gets and returns an array with the allowed filters on an Entity (including its subEntities / sub-filters). - * - * @param Entity $Entity The Entity we are currently doing a get collection on. - * @param string $prefix - * @param int $level - * - * @return array The array with allowed filters. - */ - public function getFilterParameters(Entity $Entity, string $prefix = '', int $level = 1, bool $embedded = false): array - { - $prefix = $embedded && $level === 2 ? "embedded.$prefix" : $prefix; - - //todo: we only check for the allowed keys/attributes to filter on, if this attribute is a dateTime (or date), we should also check if the value is a valid dateTime string? - // NOTE: - // Filter id looks for ObjectEntity id and externalId - // Filter _id looks specifically/only for ObjectEntity id - // Filter _externalId looks specifically/only for ObjectEntity externalId - - // defaults - $filters = [ - $prefix.'id', $prefix.'_id', $prefix.'_externalId', $prefix.'_uri', $prefix.'_self', $prefix.'_organization', - $prefix.'_application', $prefix.'_dateCreated', $prefix.'_dateModified', $prefix.'_mapping', - ]; - - foreach ($Entity->getAttributes() as $attribute) { - if (in_array($attribute->getType(), ['string', 'date', 'datetime', 'integer', 'float', 'number', 'boolean']) && $attribute->getSearchable()) { - $filters[] = $prefix.$attribute->getName(); - } elseif ($attribute->getObject() && $level < 3 && !str_contains($prefix, $attribute->getName().'.')) { - $attribute->getSearchable() && $filters[] = $prefix.$attribute->getName(); - $embeddedString = $embedded && $level > 1 ? 'embedded.' : ''; - $filters = array_merge($filters, $this->getFilterParameters($attribute->getObject(), $prefix.$embeddedString.$attribute->getName().'.', $level + 1, $embedded)); - } - } - - return $filters; - } - - /** - * Gets and returns an array with the allowed sortable attributes on an Entity (including its subEntities). - * - * @param Entity $Entity The Entity we are currently doing a get collection on. - * @param string $prefix - * @param int $level - * - * @return array The array with allowed attributes to sort by. - */ - public function getOrderParameters(Entity $Entity, string $prefix = '', int $level = 1, bool $embedded = false): array - { - $prefix = $embedded && $level === 2 ? "embedded.$prefix" : $prefix; - // defaults - $sortable = [$prefix.'_dateCreated', $prefix.'_dateModified']; - - foreach ($Entity->getAttributes() as $attribute) { - if (in_array($attribute->getType(), ['string', 'date', 'datetime', 'integer', 'float', 'number']) && $attribute->getSortable()) { - $sortable[] = $prefix.$attribute->getName(); - } elseif ($attribute->getObject() && $level < 3 && !str_contains($prefix, $attribute->getName().'.')) { - $embeddedString = $embedded && $level > 1 ? 'embedded.' : ''; - $sortable = array_merge($sortable, $this->getOrderParameters($attribute->getObject(), $prefix.$embeddedString.$attribute->getName().'.', $level + 1)); - } - } - - return $sortable; - } - - /** - * Finds object entities on their id or a sourceId of a synchronization this ObjectEntity has. - * - * @param string $identifier - * - * @throws NonUniqueResultException - * - * @return ObjectEntity The found object entity - */ - public function findByAnyId(string $identifier): ?ObjectEntity - { - $query = $this->createQueryBuilder('o') - ->leftJoin('o.synchronizations', 's') - ->where('s.sourceId = :identifier') - ->setParameter('identifier', $identifier); - - if (Uuid::isValid($identifier)) { - $query->orWhere('o.id = :identifier'); - } - - return $query->getQuery()->getOneOrNullResult(); - } } diff --git a/api/src/Security/ApiKeyAuthenticator.php b/api/src/Security/ApiKeyAuthenticator.php index d9c19f97e..4fbb33ea1 100644 --- a/api/src/Security/ApiKeyAuthenticator.php +++ b/api/src/Security/ApiKeyAuthenticator.php @@ -2,6 +2,7 @@ namespace App\Security; +use App\Entity\Application; use App\Entity\User; use App\Service\FunctionService; use Conduction\CommonGroundBundle\Service\AuthenticationService; @@ -146,20 +147,21 @@ public function authenticate(Request $request): PassportInterface { $key = $request->headers->get('Authorization'); $application = $this->entityManager->getRepository('App:Application')->findOneBy(['secret' => $key]); - if (!$application) { + if ($application === null) { throw new AuthenticationException('Invalid ApiKey'); } try { $user = $application->getOrganization()->getUsers()[0]; } catch (\Exception $exception) { - throw new AuthenticationException('Invalid User'); + throw new AuthenticationException('An invalid User is configured for this ApiKey'); } - $this->session->set('apiKeyApplication', $application->getId()->toString()); - if (!$user || !($user instanceof User)) { - throw new AuthenticationException('The provided token does not match the user it refers to'); + if ($user instanceof User === false) { + throw new AuthenticationException('An invalid User is configured for this ApiKey'); } + $this->session->set('apiKeyApplication', $application->getId()->toString()); + $roleArray = []; foreach ($user->getSecurityGroups() as $securityGroup) { $roleArray['roles'][] = "Role_{$securityGroup->getName()}"; @@ -175,15 +177,8 @@ public function authenticate(Request $request): PassportInterface } } - $organizations = []; - if ($user->getOrganization()) { - $organizations[] = $user->getOrganization(); - } - - $organizations[] = 'localhostOrganization'; - $this->session->set('organizations', $organizations); - // If user has no organization, we default activeOrganization to an organization of a userGroup this user has and else the application organization; - $this->session->set('activeOrganization', $user->getOrganization()); + // Set organization in session + $this->session->set('organization', $user->getOrganization() !== null ? $user->getOrganization()->getId()->toString() : null); $userArray = [ 'id' => $user->getId()->toString(), diff --git a/api/src/Security/OIDCAuthenticator.php b/api/src/Security/OIDCAuthenticator.php index 8fbf5111d..15e9704be 100644 --- a/api/src/Security/OIDCAuthenticator.php +++ b/api/src/Security/OIDCAuthenticator.php @@ -113,13 +113,6 @@ public function authenticate(Request $request): PassportInterface } } - // Set default organization in session for multitenancy (see how this is done in other Authenticators, this can be different for each one!) - $defaultOrganization = $this->getDefaultOrganization(); - $organizations = [$defaultOrganization, 'localhostOrganization']; - $parentOrganizations[] = 'localhostOrganization'; - $this->session->set('organizations', $organizations); - $this->session->set('parentOrganizations', $parentOrganizations); - $this->session->set('activeOrganization', $defaultOrganization); // if (isset($accessToken['refresh_token'])) { // $this->session->set('refresh_token', $accessToken['refresh_token']); // $userIdentifier = $result['email']; @@ -133,6 +126,7 @@ public function authenticate(Request $request): PassportInterface $doctrineUser->setPassword(''); $doctrineUser->addApplication($this->applicationService->getApplication()); $doctrineUser->setOrganization($doctrineUser->getApplications()->first()->getOrganization()); + $this->session->set('organization', $doctrineUser->getApplications()->first()->getOrganization()); foreach ($result['groups'] as $group) { $securityGroup = $this->entityManager->getRepository('App:SecurityGroup')->findOneBy(['name' => $group]); @@ -178,24 +172,6 @@ function ($credentials, $user) { ); } - private function getDefaultOrganization(): string - { - // Find application->organization - if ($this->session->get('application')) { - $application = $this->entityManager->getRepository('App:Application')->findOneBy(['id' => $this->session->get('application')]); - if (!empty($application) && $application->getOrganization()) { - return $application->getOrganization(); - } - } - // Else find and return 'the' default organization - $organization = $this->entityManager->getRepository('App:ObjectEntity')->findOneBy(['id' => 'a1c8e0b6-2f78-480d-a9fb-9792142f4761']); - if (!empty($organization) && $organization->getOrganization()) { - return $organization->getOrganization(); - } - - return 'http://api/admin/organizations/a1c8e0b6-2f78-480d-a9fb-9792142f4761'; - } - public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response { return new RedirectResponse($this->session->get('backUrl', $this->parameterBag->get('defaultBackUrl')) ?? $request->headers->get('referer') ?? $request->getSchemeAndHttpHost()); diff --git a/api/src/Security/TokenAuthenticator.php b/api/src/Security/TokenAuthenticator.php index 10b2d63ac..f12845b68 100644 --- a/api/src/Security/TokenAuthenticator.php +++ b/api/src/Security/TokenAuthenticator.php @@ -99,6 +99,7 @@ public function getPublicKey(string $token): string * @param string $token The token provided by the user * * @return array The payload of the token + * @throws GatewayException */ public function validateToken(string $token): array { @@ -139,9 +140,7 @@ private function prefixRoles(array $roles): array * * @param Request $request * - * @throws CacheException * @throws GatewayException - * @throws InvalidArgumentException * * @return PassportInterface */ diff --git a/api/src/Service/ApplicationService.php b/api/src/Service/ApplicationService.php index 119049407..ce5db8001 100644 --- a/api/src/Service/ApplicationService.php +++ b/api/src/Service/ApplicationService.php @@ -2,6 +2,7 @@ namespace App\Service; +use App\Entity\Application; use App\Exception\GatewayException; use Doctrine\Common\Collections\Criteria; use Doctrine\ORM\EntityManagerInterface; @@ -29,67 +30,67 @@ public function __construct( } /** - * A function that finds an application or creates one. + * A function that finds an application. * * @throws GatewayException */ - public function getApplication() + public function getApplication(): Application { - if ($application = $this->session->get('application')) { + // If application is already in the session + if ($this->session->has('application')) { $application = $this->entityManager->getRepository('App:Application')->findOneBy(['id' => $this->session->get('application')]); - if (!empty($application)) { + if ($application !== null) { return $application; } - } elseif ($this->session->get('apiKeyApplication')) { - // If an api-key is used for authentication we already know which application is used - return $this->entityManager->getRepository('App:Application')->findOneBy(['id' => $this->session->get('apiKeyApplication')]); } - // get publickey + // If an api-key is used for authentication we already know which application is used + if ($this->session->has('apiKeyApplication')) { + $application = $this->entityManager->getRepository('App:Application')->findOneBy(['id' => $this->session->get('apiKeyApplication')]); + if ($application !== null) { + $this->session->set('application', $application->getId()->toString()); + return $application; + } + } + + // Find application using the publicKey $public = ($this->request->headers->get('public') ?? $this->request->query->get('public')); + if (empty($public) === false) { + $application = $this->entityManager->getRepository('App:Application')->findOneBy(['public' => $public]); + if ($application !== null) { + $this->session->set('application', $application->getId()->toString()); + return $application; + } + } - // get host/domain + // Find application using the host/domain $host = ($this->request->headers->get('host') ?? $this->request->query->get('host')); -// $host = 'api.buren.commonground.nu'; - ($application = $this->entityManager->getRepository('App:Application')->findOneBy(['public' => $public])) && !empty($application) && $this->session->set('application', $application->getId()->toString()); - - if (!$application) { + if (empty($host) === false) { // @todo Create and use query in ApplicationRepository - $criteria = new Criteria(); - - // $application = $this->entityManager->getRepository('App:Application')->findAll()-> $applications = $this->entityManager->getRepository('App:Application')->findAll(); foreach ($applications as $app) { - $app->getDomains() !== null && in_array($host, $app->getDomains()) && $application = $app; - if (isset($application)) { - break; + if ($app->getDomains() !== null && in_array($host, $app->getDomains()) === true) { + $this->session->set('application', $app->getId()->toString()); + return $app; } } -// if(count($applications) > 0) { -// $application = $applications[0]; -// } } - if (!$application) { - $this->session->set('application', null); + // No application was found + $this->session->set('application', null); - // Set message - $public && $message = 'No application found with public '.$public; - $host && $message = 'No application found with host '.$host; - !$public && !$host && $message = 'No host or application given'; - - // Set data - $public && $data = ['public' => $public]; - $host && $data = ['host' => $host]; - - throw new GatewayException($message ?? null, null, null, [ - 'data' => $data ?? null, 'path' => $public ?? $host ?? 'Header', 'responseType' => Response::HTTP_FORBIDDEN, - ]); - } + // Set message + $public && $message = 'No application found with public '.$public; + $host && $message = 'No application found with host '.$host; + !$public && !$host && $message = 'No host or application given'; - $this->session->set('application', $application->getId()->toString()); + // Set data + $public && $data = ['public' => $public]; + $host && $data = ['host' => $host]; - return $application; + throw new GatewayException($message ?? null, null, null, [ + 'data' => $data ?? null, 'path' => $public ?? $host ?? 'Header', 'responseType' => Response::HTTP_FORBIDDEN, + ]); } } diff --git a/api/src/Service/AuthorizationService.php b/api/src/Service/AuthorizationService.php index 8f9bd00b0..13add636a 100644 --- a/api/src/Service/AuthorizationService.php +++ b/api/src/Service/AuthorizationService.php @@ -231,8 +231,7 @@ public function getScopesForAnonymous(): array $item = $this->cache->getItem('anonymousScopes'); $itemOrg = $this->cache->getItem('anonymousOrg'); if ($item->isHit() && $itemOrg->isHit()) { - $this->session->set('organizations', [$itemOrg->get()]); - $this->session->set('activeOrganization', $itemOrg->get()); + $this->session->set('organization', $itemOrg->get()); return $item->get(); } @@ -244,8 +243,7 @@ public function getScopesForAnonymous(): array foreach ($groups[0]['scopes'] as $scope) { $scopes[] = strtolower($scope['code']); } - $this->session->set('organizations', [$groups[0]['organization']]); - $this->session->set('activeOrganization', $groups[0]['organization']); + $this->session->set('organization', $groups[0]['organization']); $itemOrg->set($groups[0]['organization']); $itemOrg->tag('anonymousOrg'); $this->cache->save($itemOrg); diff --git a/api/src/Service/EavService.php b/api/src/Service/EavService.php index 29a8b63db..c5e68a4a1 100644 --- a/api/src/Service/EavService.php +++ b/api/src/Service/EavService.php @@ -175,7 +175,7 @@ public function getObject(?string $id, string $method, Entity $entity) $object = new ObjectEntity(); $object->setEntity($entity); // if entity->function == 'organization', organization for this ObjectEntity will be changed later in handleMutation - $this->session->get('activeOrganization') ? $object->setOrganization($this->session->get('activeOrganization')) : $object->setOrganization('http://testdata-organization'); + $this->session->get('organization') ? $object->setOrganization($this->session->get('organization')) : $object->setOrganization('http://testdata-organization'); $application = $this->em->getRepository('App:Application')->findOneBy(['id' => $this->session->get('application')]); $object->setApplication(!empty($application) ? $application : null); @@ -401,15 +401,9 @@ public function generateResult(Request $request, Entity $entity, array $requestB } } - if (!$this->session->get('activeOrganization') && $this->session->get('application')) { + if (!$this->session->get('organization') && $this->session->get('application')) { $application = $this->em->getRepository('App:Application')->findOneBy(['id' => $this->session->get('application')]); - $this->session->set('activeOrganization', !empty($application) ? $application->getOrganization() : null); - } - if (!$this->session->get('organizations') && $this->session->get('activeOrganization')) { - $this->session->set('organizations', [$this->session->get('activeOrganization')]); - } - if (!$this->session->get('parentOrganizations')) { - $this->session->set('parentOrganizations', []); + $this->session->set('organization', !empty($application) ? $application->getOrganization() : null); } // Lets create an object @@ -420,19 +414,19 @@ public function generateResult(Request $request, Entity $entity, array $requestB $result = $object; $object = null; } // Lets check if the user is allowed to view/edit this resource. - elseif (!$this->objectEntityService->checkOwner($object)) { - // TODO: do we want to throw a different error if there are nog organizations in the session? (because of logging out for example) - if ($object->getOrganization() && !in_array($object->getOrganization(), $this->session->get('organizations') ?? [])) { - $object = null; // Needed so we return the error and not the object! - $responseType = Response::HTTP_FORBIDDEN; - $result = [ - 'message' => 'You are forbidden to view or edit this resource.', - 'type' => 'Forbidden', - 'path' => $entity->getName(), - 'data' => ['id' => $requestBase['id']], - ]; - } - } +// elseif (!$this->objectEntityService->checkOwner($object)) { +// // TODO: do we want to throw a different error if there are nog organizations in the session? (because of logging out for example) +// if ($object->getOrganization() && !in_array($object->getOrganization(), [])) { +// $object = null; // Needed so we return the error and not the object! +// $responseType = Response::HTTP_FORBIDDEN; +// $result = [ +// 'message' => 'You are forbidden to view or edit this resource.', +// 'type' => 'Forbidden', +// 'path' => $entity->getName(), +// 'data' => ['id' => $requestBase['id']], +// ]; +// } +// } } // Check for scopes, if forbidden to view/edit overwrite result so far to this forbidden error @@ -836,7 +830,7 @@ public function handleCollectionEndpoint(Request $request, array $info): array public function handleMutation(ObjectEntity $object, array $body, $fields, Request $request): array { // Check if session contains an activeOrganization, so we can't do calls without it. So we do not create objects with no organization! - if ($this->parameterBag->get('app_auth') && empty($this->session->get('activeOrganization'))) { + if ($this->parameterBag->get('app_auth') && empty($this->session->get('organization'))) { return [ 'message' => 'An active organization is required in the session, please login to create a new session.', 'type' => 'Forbidden', diff --git a/api/src/Service/ObjectEntityService.php b/api/src/Service/ObjectEntityService.php index 4b4be82b8..bc35f4468 100644 --- a/api/src/Service/ObjectEntityService.php +++ b/api/src/Service/ObjectEntityService.php @@ -461,12 +461,12 @@ public function checkGetObject(?string $id, string $method, Entity $entity) throw new GatewayException($object['message'], null, null, ['data' => $object['data'], 'path' => $object['path'], 'responseType' => Response::HTTP_BAD_REQUEST]); } // Let's check if the user is allowed to view/edit this resource. - if (!$method == 'POST' && !$this->checkOwner($object)) { - // TODO: do we want to throw a different error if there are no organizations in the session? (because of logging out for example) - if ($object->getOrganization() && !in_array($object->getOrganization(), $this->session->get('organizations') ?? [])) { - throw new GatewayException('You are forbidden to view or edit this resource.', null, null, ['data' => ['id' => $id ?? null], 'path' => $entity->getName(), 'responseType' => Response::HTTP_FORBIDDEN]); - } - } +// if (!$method == 'POST' && !$this->checkOwner($object)) { +// // TODO: do we want to throw a different error if there are no organizations in the session? (because of logging out for example) +// if ($object->getOrganization() && !in_array($object->getOrganization(), [])) { +// throw new GatewayException('You are forbidden to view or edit this resource.', null, null, ['data' => ['id' => $id ?? null], 'path' => $entity->getName(), 'responseType' => Response::HTTP_FORBIDDEN]); +// } +// } if ($object instanceof ObjectEntity && $object->getId() !== null) { $this->session->set('object', $object->getId()->toString()); @@ -1045,7 +1045,7 @@ private function saveSubObject(ObjectEntity $subObject, $object): ObjectEntity $subObject->setOrganization($subObject->getSubresourceOf()->first()->getObjectEntity()->getOrganization()); $subObject->setApplication($subObject->getSubresourceOf()->first()->getObjectEntity()->getApplication()); } else { - $subObject->setOrganization($this->session->get('activeOrganization')); + $subObject->setOrganization($this->session->get('organization')); $application = $this->entityManager->getRepository('App:Application')->findOneBy(['id' => $this->session->get('application')]); $subObject->setApplication(!empty($application) ? $application : null); } From a0e8f166efd5585b98456bb0866da2c836569e50 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Thu, 9 Nov 2023 13:01:51 +0100 Subject: [PATCH 28/68] Small changes for storing user id & org id in session (better logs) --- api/src/Controller/UserController.php | 6 ++++-- api/src/Logger/SessionDataProcessor.php | 3 ++- api/src/Security/ApiKeyAuthenticator.php | 9 ++++++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/api/src/Controller/UserController.php b/api/src/Controller/UserController.php index be91f2227..3910fb7c5 100644 --- a/api/src/Controller/UserController.php +++ b/api/src/Controller/UserController.php @@ -100,7 +100,8 @@ public function resetTokenAction(SerializerInterface $serializer, \CommonGateway $user = $this->entityManager->getRepository('App:User')->find($user->getUserIdentifier()); - // Set organization in session + // Set organization id and user id in session + $this->session->set('user', $user->getId()->toString()); $this->session->set('organization', $user->getOrganization() !== null ? $user->getOrganization()->getId()->toString() : null); $user->setJwtToken($authenticationService->createJwtToken($user->getApplications()[0]->getPrivateKey(), $authenticationService->serializeUser($user, $this->session))); @@ -195,7 +196,8 @@ public function apiLoginAction(Request $request, UserPasswordHasherInterface $ha return new Response(json_encode($response), 401, ['Content-type' => 'application/json']); } - // Set organization in session + // Set organization id and user id in session + $this->session->set('user', $user->getId()->toString()); $this->session->set('organization', $user->getOrganization() !== null ? $user->getOrganization()->getId()->toString() : null); $token = $authenticationService->createJwtToken($user->getApplications()[0]->getPrivateKey(), $authenticationService->serializeUser($user, $this->session)); diff --git a/api/src/Logger/SessionDataProcessor.php b/api/src/Logger/SessionDataProcessor.php index ce568dbca..1e0152568 100644 --- a/api/src/Logger/SessionDataProcessor.php +++ b/api/src/Logger/SessionDataProcessor.php @@ -27,6 +27,7 @@ class SessionDataProcessor * @param SessionInterface $session * @param RequestStack $requestStack * @param EventDispatcherInterface $eventDispatcher + * @param EntityManagerInterface $entityManager */ public function __construct( SessionInterface $session, @@ -48,7 +49,7 @@ public function __construct( * * @return array The updated context. */ - public function updateContext($context): array + public function updateContext(array $context): array { $context['session'] = $this->session->getId(); $context['process'] = $this->session->has('process') ? $this->session->get('process') : ''; diff --git a/api/src/Security/ApiKeyAuthenticator.php b/api/src/Security/ApiKeyAuthenticator.php index 4fbb33ea1..c7af2224d 100644 --- a/api/src/Security/ApiKeyAuthenticator.php +++ b/api/src/Security/ApiKeyAuthenticator.php @@ -160,8 +160,14 @@ public function authenticate(Request $request): PassportInterface if ($user instanceof User === false) { throw new AuthenticationException('An invalid User is configured for this ApiKey'); } + + // Set apiKey Application id in session $this->session->set('apiKeyApplication', $application->getId()->toString()); + // Set organization id and user id in session + $this->session->set('user', $user->getId()->toString()); + $this->session->set('organization', $user->getOrganization() !== null ? $user->getOrganization()->getId()->toString() : null); + $roleArray = []; foreach ($user->getSecurityGroups() as $securityGroup) { $roleArray['roles'][] = "Role_{$securityGroup->getName()}"; @@ -177,9 +183,6 @@ public function authenticate(Request $request): PassportInterface } } - // Set organization in session - $this->session->set('organization', $user->getOrganization() !== null ? $user->getOrganization()->getId()->toString() : null); - $userArray = [ 'id' => $user->getId()->toString(), 'email' => $user->getEmail(), From eee5ed817ae0698fe3b409c2656dca240cadb737 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Thu, 9 Nov 2023 16:07:59 +0100 Subject: [PATCH 29/68] Improve login error responses & added dql finding applications by domain --- api/src/Controller/UserController.php | 42 ++++++++++++++++++-- api/src/Repository/ApplicationRepository.php | 17 ++++---- api/src/Security/OIDCAuthenticator.php | 7 ++++ api/src/Service/ApplicationService.php | 11 ++--- 4 files changed, 57 insertions(+), 20 deletions(-) diff --git a/api/src/Controller/UserController.php b/api/src/Controller/UserController.php index 3910fb7c5..79e5fafd3 100644 --- a/api/src/Controller/UserController.php +++ b/api/src/Controller/UserController.php @@ -64,7 +64,7 @@ public function resetTokenAction(SerializerInterface $serializer, \CommonGateway $accessToken = $this->authenticationService->refreshAccessToken($session->get('refresh_token'), $session->get('authenticator')); $user = $this->getUser(); if ($user instanceof AuthenticationUser === false) { - return new Response('User not found', 401); + return new Response(json_encode(["Message" => 'User not found.']), 401, ['Content-type' => 'application/json']); } $serializeUser = new User(); @@ -95,7 +95,7 @@ public function resetTokenAction(SerializerInterface $serializer, \CommonGateway $status = 200; $user = $this->getUser(); if ($user instanceof AuthenticationUser === false) { - return new Response('User not found', 401); + return new Response(json_encode(["Message" => 'User not found.']), 401, ['Content-type' => 'application/json']); } $user = $this->entityManager->getRepository('App:User')->find($user->getUserIdentifier()); @@ -104,13 +104,17 @@ public function resetTokenAction(SerializerInterface $serializer, \CommonGateway $this->session->set('user', $user->getId()->toString()); $this->session->set('organization', $user->getOrganization() !== null ? $user->getOrganization()->getId()->toString() : null); + $response = $this->validateUserApp($user); + if ($response !== null) + return $response; + $user->setJwtToken($authenticationService->createJwtToken($user->getApplications()[0]->getPrivateKey(), $authenticationService->serializeUser($user, $this->session))); return new Response($serializer->serialize($user, 'json'), $status, ['Content-type' => 'application/json']); } /** - * Create an authentication user from a entity user. + * Create an authentication user from an entity user. * * @param User $user The user to log in. * @@ -200,6 +204,10 @@ public function apiLoginAction(Request $request, UserPasswordHasherInterface $ha $this->session->set('user', $user->getId()->toString()); $this->session->set('organization', $user->getOrganization() !== null ? $user->getOrganization()->getId()->toString() : null); + $response = $this->validateUserApp($user); + if ($response !== null) + return $response; + $token = $authenticationService->createJwtToken($user->getApplications()[0]->getPrivateKey(), $authenticationService->serializeUser($user, $this->session)); $user->setJwtToken($token); @@ -223,6 +231,34 @@ public function apiLoginAction(Request $request, UserPasswordHasherInterface $ha return new Response(json_encode($userArray), $status, ['Content-type' => 'application/json']); } + /** + * Checks if $user has an application and if that application has a PrivateKey set. If not return error Response. + * + * @param User $user A user to check. + * + * @return Response|null Error Response or null. + */ + private function validateUserApp(User $user): ?Response + { + if (empty($user->getApplications()) === true) { + return new Response( + json_encode(["Message" => 'This user is not yet connected to any application.']), + 409, + ['Content-type' => 'application/json'] + ); + } + + if (empty($user->getApplications()[0]->getPrivateKey()) === true) { + return new Response( + json_encode(["Message" => "Can't create a token because application ({$user->getApplications()[0]->getId()->toString()}) doesn't have a PrivateKey."]), + 409, + ['Content-type' => 'application/json'] + ); + } + + return null; + } + /** * Removes some sensitive data from the login response. * diff --git a/api/src/Repository/ApplicationRepository.php b/api/src/Repository/ApplicationRepository.php index 15765f22b..53752d1c2 100644 --- a/api/src/Repository/ApplicationRepository.php +++ b/api/src/Repository/ApplicationRepository.php @@ -21,24 +21,21 @@ public function __construct(ManagerRegistry $registry) } /** - * @param string $domain + * Find all applications that have the given $domain in there list of domains. * - * @throws NonUniqueResultException + * @param string $domain A domain to search with. * - * @return Application|null + * @return array|null */ - public function findByDomain(string $domain): ?Application + public function findByDomain(string $domain): ?array { - // TODO: something like this $query = $this->createQueryBuilder('a') - ->andWhere(':domain IN (a.domains)') - ->setParameters(['domain' => $domain]); - -// var_dump($query->getDQL()); + ->andWhere('a.domains LIKE :domain') + ->setParameters(['domain' => "%$domain%"]); return $query ->getQuery() - ->getOneOrNullResult(); + ->getResult(); } // /** diff --git a/api/src/Security/OIDCAuthenticator.php b/api/src/Security/OIDCAuthenticator.php index 15e9704be..4d4118028 100644 --- a/api/src/Security/OIDCAuthenticator.php +++ b/api/src/Security/OIDCAuthenticator.php @@ -4,6 +4,7 @@ use App\Entity\SecurityGroup; use App\Entity\User; +use App\Exception\GatewayException; use App\Security\User\AuthenticationUser; use App\Service\ApplicationService; use App\Service\AuthenticationService; @@ -140,6 +141,12 @@ public function authenticate(Request $request): PassportInterface $userIdentifier = $doctrineUser->getId()->toString(); + if (empty($doctrineUser->getApplications()[0]->getPrivateKey()) === true) { + throw new GatewayException("Can't create a token because application doesn't have a PrivateKey." ?? null, 409, null, [ + 'data' => ['application_id' => $doctrineUser->getApplications()[0]->getId()->toString()], 'path' => '', 'responseType' => Response::HTTP_CONFLICT, + ]); + } + $token = $this->coreAuthenticationService->createJwtToken($doctrineUser->getApplications()[0]->getPrivateKey(), $this->coreAuthenticationService->serializeUser($doctrineUser, $this->session)); $doctrineUser->setJwtToken($token); diff --git a/api/src/Service/ApplicationService.php b/api/src/Service/ApplicationService.php index ce5db8001..6536ff90c 100644 --- a/api/src/Service/ApplicationService.php +++ b/api/src/Service/ApplicationService.php @@ -66,14 +66,11 @@ public function getApplication(): Application // Find application using the host/domain $host = ($this->request->headers->get('host') ?? $this->request->query->get('host')); if (empty($host) === false) { - // @todo Create and use query in ApplicationRepository + $applications = $this->entityManager->getRepository('App:Application')->findByDomain($host); + if (count($applications) > 0) { + $this->session->set('application', $applications[0]->getId()->toString()); - $applications = $this->entityManager->getRepository('App:Application')->findAll(); - foreach ($applications as $app) { - if ($app->getDomains() !== null && in_array($host, $app->getDomains()) === true) { - $this->session->set('application', $app->getId()->toString()); - return $app; - } + return $applications[0]; } } From 8e9b1292586a241764102dfcde2dcbfb301325cb Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Thu, 9 Nov 2023 16:36:10 +0100 Subject: [PATCH 30/68] Added note for later, only allow user login on app/domain they may use --- api/src/Controller/UserController.php | 4 ++++ api/src/Security/OIDCAuthenticator.php | 2 ++ 2 files changed, 6 insertions(+) diff --git a/api/src/Controller/UserController.php b/api/src/Controller/UserController.php index 79e5fafd3..4cc28408a 100644 --- a/api/src/Controller/UserController.php +++ b/api/src/Controller/UserController.php @@ -108,6 +108,8 @@ public function resetTokenAction(SerializerInterface $serializer, \CommonGateway if ($response !== null) return $response; + // TODO: maybe do not just get the first Application here, but get application using ApplicationService->getApplication() and ... + // todo... if this returns an application check if the user is part of this application or one of the organizations of this application? $user->setJwtToken($authenticationService->createJwtToken($user->getApplications()[0]->getPrivateKey(), $authenticationService->serializeUser($user, $this->session))); return new Response($serializer->serialize($user, 'json'), $status, ['Content-type' => 'application/json']); @@ -208,6 +210,8 @@ public function apiLoginAction(Request $request, UserPasswordHasherInterface $ha if ($response !== null) return $response; + // TODO: maybe do not just get the first Application here, but get application using ApplicationService->getApplication() and ... + // todo... if this returns an application check if the user is part of this application or one of the organizations of this application? $token = $authenticationService->createJwtToken($user->getApplications()[0]->getPrivateKey(), $authenticationService->serializeUser($user, $this->session)); $user->setJwtToken($token); diff --git a/api/src/Security/OIDCAuthenticator.php b/api/src/Security/OIDCAuthenticator.php index 4d4118028..c7a000716 100644 --- a/api/src/Security/OIDCAuthenticator.php +++ b/api/src/Security/OIDCAuthenticator.php @@ -147,6 +147,8 @@ public function authenticate(Request $request): PassportInterface ]); } + // TODO: maybe do not just get the first Application here, but get application using ApplicationService->getApplication() and ... + // todo... if this returns an application check if the user is part of this application or one of the organizations of this application? $token = $this->coreAuthenticationService->createJwtToken($doctrineUser->getApplications()[0]->getPrivateKey(), $this->coreAuthenticationService->serializeUser($doctrineUser, $this->session)); $doctrineUser->setJwtToken($token); From b84ab13c71b505c7703da27257b93f395fddeeca Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Fri, 10 Nov 2023 08:57:50 +0100 Subject: [PATCH 31/68] remove one line if statements --- api/src/Repository/ObjectEntityRepository.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/api/src/Repository/ObjectEntityRepository.php b/api/src/Repository/ObjectEntityRepository.php index 8da2f4a17..9f3b68051 100644 --- a/api/src/Repository/ObjectEntityRepository.php +++ b/api/src/Repository/ObjectEntityRepository.php @@ -66,7 +66,10 @@ public function getFilterParameters(Entity $Entity, string $prefix = '', int $le $filters[] = $prefix.$attribute->getName(); } elseif ($attribute->getObject() && $level < 3 && !str_contains($prefix, $attribute->getName().'.')) { $attribute->getSearchable() && $filters[] = $prefix.$attribute->getName(); - $embeddedString = $embedded && $level > 1 ? 'embedded.' : ''; + $embeddedString = ''; + if ($embedded && $level > 1) { + $embeddedString = 'embedded.'; + } $filters = array_merge($filters, $this->getFilterParameters($attribute->getObject(), $prefix.$embeddedString.$attribute->getName().'.', $level + 1, $embedded)); } } @@ -86,7 +89,10 @@ public function getFilterParameters(Entity $Entity, string $prefix = '', int $le */ public function getOrderParameters(Entity $Entity, string $prefix = '', int $level = 1, bool $embedded = false): array { - $prefix = $embedded && $level === 2 ? "embedded.$prefix" : $prefix; + $prefix = $prefix; + if ($embedded && $level === 2) { + $prefix = "embedded.$prefix"; + } // defaults $sortable = [$prefix.'_dateCreated', $prefix.'_dateModified']; @@ -94,7 +100,10 @@ public function getOrderParameters(Entity $Entity, string $prefix = '', int $lev if (in_array($attribute->getType(), ['string', 'date', 'datetime', 'integer', 'float', 'number']) && $attribute->getSortable()) { $sortable[] = $prefix.$attribute->getName(); } elseif ($attribute->getObject() && $level < 3 && !str_contains($prefix, $attribute->getName().'.')) { - $embeddedString = $embedded && $level > 1 ? 'embedded.' : ''; + $embeddedString = ''; + if ($embedded && $level > 1) { + $embeddedString = 'embedded.'; + } $sortable = array_merge($sortable, $this->getOrderParameters($attribute->getObject(), $prefix.$embeddedString.$attribute->getName().'.', $level + 1)); } } From 2c809b47df94890587b1512acffe258179d853d9 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Fri, 10 Nov 2023 15:36:34 +0100 Subject: [PATCH 32/68] Added BL for synchronization sha --- api/migrations/Version20231110140316.php | 31 ++++++++++++++++++++++ api/src/Entity/Synchronization.php | 23 +++++++++++++++- api/src/Service/SynchronizationService.php | 30 +++++++++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 api/migrations/Version20231110140316.php diff --git a/api/migrations/Version20231110140316.php b/api/migrations/Version20231110140316.php new file mode 100644 index 000000000..72fb31735 --- /dev/null +++ b/api/migrations/Version20231110140316.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE synchronization ADD sha VARCHAR(255) DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE synchronization DROP sha'); + } +} diff --git a/api/src/Entity/Synchronization.php b/api/src/Entity/Synchronization.php index 4bd2025e7..678594050 100644 --- a/api/src/Entity/Synchronization.php +++ b/api/src/Entity/Synchronization.php @@ -154,7 +154,16 @@ class Synchronization private ?string $hash = ''; /** - * @var bool Whether or not the synchronization is blocked + * @var ?string The sha(256) used to check if a Sync should be triggered cause the object has changed + * + * @Groups({"read","write"}) + * + * @ORM\Column(type="string", nullable=true) + */ + private ?string $sha = null; + + /** + * @var bool Whether the synchronization is blocked * * @Groups({"read", "write"}) * @@ -361,6 +370,18 @@ public function setHash(?string $hash): self return $this; } + public function getSha(): ?string + { + return $this->sha; + } + + public function setSha(?string $sha): self + { + $this->sha = $sha; + + return $this; + } + public function getSourceLastChanged(): ?\DateTimeInterface { return $this->sourceLastChanged; diff --git a/api/src/Service/SynchronizationService.php b/api/src/Service/SynchronizationService.php index cd48120b9..b70a62ee0 100644 --- a/api/src/Service/SynchronizationService.php +++ b/api/src/Service/SynchronizationService.php @@ -66,6 +66,7 @@ class SynchronizationService private Logger $logger; private bool $asyncError = false; + private ?string $sha = null; /** * @param CallService $callService @@ -917,6 +918,29 @@ public function handleSync(Synchronization $synchronization, array $sourceObject return $synchronization; } + /** + * This function checks if the sha of $synchronization matches the given $sha. + * When $synchronization->getSha() doesn't match with the given $sha, the given $sha will be stored in the SynchronizationService. + * Always call the ->synchronize() function after this, because only then the stored $sha will be used to update $synchronization->setSha(). + * + * @param Synchronization $synchronization The Synchronization to check the sha of. + * @param string $sha The sha to check / compare. + * + * @return bool Returns True if sha matches, and false if it does not match. + */ + public function doesShaMatch(Synchronization $synchronization, string $sha): bool + { + $this->sha = null; + + if ($synchronization->getSha() === $sha) { + return true; + } + + $this->sha = $sha; + + return false; + } + /** * Executes the synchronization between source and gateway. * @@ -988,6 +1012,12 @@ public function synchronize(Synchronization $synchronization, array $sourceObjec $sourceObject = $this->mappingService->mapping($synchronization->getMapping(), $sourceObject); } $synchronization->getObject()->hydrate($sourceObject, $unsafe); + + if ($this->sha !== null) { + $synchronization->setSha($this->sha); + $this->sha = null; + } + $this->entityManager->persist($synchronization->getObject()); $this->entityManager->persist($synchronization); From f7c1ce342c7e269081b70425ff10757621993218 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Fri, 17 Nov 2023 15:33:22 +0100 Subject: [PATCH 33/68] Log more data for Error, Critical and higher level logs --- api/src/Logger/SessionDataProcessor.php | 56 ++++++++++++++++++++----- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/api/src/Logger/SessionDataProcessor.php b/api/src/Logger/SessionDataProcessor.php index 1e0152568..588569735 100644 --- a/api/src/Logger/SessionDataProcessor.php +++ b/api/src/Logger/SessionDataProcessor.php @@ -4,6 +4,7 @@ use App\Event\ActionEvent; use Doctrine\ORM\EntityManagerInterface; +use Exception; use Psr\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Session\SessionInterface; @@ -52,24 +53,52 @@ public function __construct( public function updateContext(array $context): array { $context['session'] = $this->session->getId(); - $context['process'] = $this->session->has('process') ? $this->session->get('process') : ''; - $context['endpoint'] = $this->session->has('endpoint') ? $this->session->get('endpoint') : ''; - $context['schema'] = $this->session->has('schema') ? $this->session->get('schema') : ''; + $context['process'] = $this->session->has('process') === true ? $this->session->get('process') : ''; + $context['endpoint'] = $this->session->has('endpoint') === true ? $this->session->get('endpoint') : ''; + $context['schema'] = $this->session->has('schema') === true ? $this->session->get('schema') : ''; $context['object'] = $this->session->has('object') === true ? $this->session->get('object') : ''; - $context['cronjob'] = $this->session->has('cronjob') ? $this->session->get('cronjob') : ''; - $context['action'] = $this->session->has('action') ? $this->session->get('action') : ''; - $context['mapping'] = $this->session->has('mapping') ? $this->session->get('mapping') : ''; - $context['source'] = $this->session->has('source') ? $this->session->get('source') : ''; + $context['cronjob'] = $this->session->has('cronjob') === true ? $this->session->get('cronjob') : ''; + $context['action'] = $this->session->has('action') === true ? $this->session->get('action') : ''; + $context['mapping'] = $this->session->has('mapping') === true ? $this->session->get('mapping') : ''; + $context['source'] = $this->session->has('source') === true ? $this->session->get('source') : ''; $context['plugin'] = isset($record['data']['plugin']) === true ? $record['data']['plugin'] : ''; - $context['user'] = $this->session->has('user') ? $this->session->get('user') : ''; - $context['organization'] = $this->session->has('organization') ? $this->session->get('organization') : ''; - $context['application'] = $this->session->has('application') ? $this->session->get('application') : ''; + $context['user'] = $this->session->has('user') === true ? $this->session->get('user') : ''; + $context['organization'] = $this->session->has('organization') === true ? $this->session->get('organization') : ''; + $context['application'] = $this->session->has('application') === true ? $this->session->get('application') : ''; $context['host'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getHost() : ''; $context['ip'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getClientIp() : ''; return $context; } + /** + * Update the context with data from the session and the request stack. For log records with level ERROR or higher. + * See: https://github.com/Seldaek/monolog/blob/main/doc/01-usage.md for all possible log levels. + * + * @param array $record The log record to update the context for. + * + * @return array The updated context. + */ + private function updateErrorContext(array $record): array + { + $context['method'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getMethod() : ''; + $context['pathRaw'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getPathInfo() : ''; + $context['querystring'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getQueryString() : ''; + $context['contentType'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getContentType() : ''; + + // Do not log entire body for normal errors, only critical and higher + if ($record['level_name'] !== 'ERROR') { + try { + $context['body'] = $this->requestStack->getMainRequest()->toArray(); + } catch (Exception $exception) { + $context['body'] = ''; + } + $context['crude_body'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getContent() : ''; + } + + return $context; + } + /** * Dispatches a log create action. * @@ -85,7 +114,7 @@ public function dispatchLogCreateAction(array $record): array $this->entityManager->getConnection()->getSchemaManager()->listDatabases() ) === true && $this->entityManager->getConnection()->getSchemaManager()->tablesExist('action') === true - && in_array($record['level_name'], ['DEBUG', 'INFO', 'NOTICE', 'WARNING']) === false + && in_array($record['level_name'], ['ERROR', 'CRITICAL', 'ALERT', 'EMERGENCY']) === true ){ $event = new ActionEvent('commongateway.action.event', $record, 'commongateway.log.create'); @@ -108,6 +137,11 @@ public function __invoke(array $record): array { $record['context'] = $this->updateContext($record['context']); + // Add more to context for higher level logs + if (in_array($record['level_name'], ['ERROR', 'CRITICAL', 'ALERT', 'EMERGENCY']) === true) { + $record['context'] = $this->updateErrorContext($record); + } + return $this->dispatchLogCreateAction($record); } } From d59ac91abc4cab28ee4a6cd10c37eaa465728f98 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Fri, 17 Nov 2023 15:53:49 +0100 Subject: [PATCH 34/68] Let's always log Method --- api/src/Logger/SessionDataProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/Logger/SessionDataProcessor.php b/api/src/Logger/SessionDataProcessor.php index 588569735..e8fcf4ccc 100644 --- a/api/src/Logger/SessionDataProcessor.php +++ b/api/src/Logger/SessionDataProcessor.php @@ -67,6 +67,7 @@ public function updateContext(array $context): array $context['application'] = $this->session->has('application') === true ? $this->session->get('application') : ''; $context['host'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getHost() : ''; $context['ip'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getClientIp() : ''; + $context['method'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getMethod() : ''; return $context; } @@ -81,7 +82,6 @@ public function updateContext(array $context): array */ private function updateErrorContext(array $record): array { - $context['method'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getMethod() : ''; $context['pathRaw'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getPathInfo() : ''; $context['querystring'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getQueryString() : ''; $context['contentType'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getContentType() : ''; From f4660515457b8a73ba419ab7025488b588c525e1 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Fri, 17 Nov 2023 17:03:28 +0100 Subject: [PATCH 35/68] Fix & added mongoDBFilter to error logs --- api/src/Logger/SessionDataProcessor.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/src/Logger/SessionDataProcessor.php b/api/src/Logger/SessionDataProcessor.php index e8fcf4ccc..46c484ceb 100644 --- a/api/src/Logger/SessionDataProcessor.php +++ b/api/src/Logger/SessionDataProcessor.php @@ -82,8 +82,11 @@ public function updateContext(array $context): array */ private function updateErrorContext(array $record): array { + $context = $record['context']; + $context['pathRaw'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getPathInfo() : ''; $context['querystring'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getQueryString() : ''; + $context['mongoDBFilter'] = $this->session->has('mongoDBFilter') === true ? json_encode($this->session->get('mongoDBFilter')) : ''; $context['contentType'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getContentType() : ''; // Do not log entire body for normal errors, only critical and higher From 20e3b9404f5e433c4c843356ac50ebcee42c59cd Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Mon, 20 Nov 2023 15:36:37 +0100 Subject: [PATCH 36/68] Added logging for request calls & responses on Sources --- api/.env | 5 ++ api/src/Logger/SessionDataProcessor.php | 70 ++++++++++++++++++++----- api/symfony.lock | 9 ++++ 3 files changed, 70 insertions(+), 14 deletions(-) diff --git a/api/.env b/api/.env index 8caf27abc..06c02c64d 100644 --- a/api/.env +++ b/api/.env @@ -30,3 +30,8 @@ DATABASE_URL="postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVers ###> nelmio/cors-bundle ### CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$' ###< nelmio/cors-bundle ### + +###> symfony/sendinblue-mailer ### +# MAILER_DSN=sendinblue+api://KEY@default +# MAILER_DSN=sendinblue+smtp://USERNAME:PASSWORD@default +###< symfony/sendinblue-mailer ### diff --git a/api/src/Logger/SessionDataProcessor.php b/api/src/Logger/SessionDataProcessor.php index 46c484ceb..c0d1ca436 100644 --- a/api/src/Logger/SessionDataProcessor.php +++ b/api/src/Logger/SessionDataProcessor.php @@ -46,12 +46,14 @@ public function __construct( /** * Update the context with data from the session and the request stack. * - * @param array $context The context to update. + * @param array $record The log record to update the context for. * * @return array The updated context. */ - public function updateContext(array $context): array + public function updateContext(array $record): array { + $context = $record['context']; + $context['session'] = $this->session->getId(); $context['process'] = $this->session->has('process') === true ? $this->session->get('process') : ''; $context['endpoint'] = $this->session->has('endpoint') === true ? $this->session->get('endpoint') : ''; @@ -61,36 +63,81 @@ public function updateContext(array $context): array $context['action'] = $this->session->has('action') === true ? $this->session->get('action') : ''; $context['mapping'] = $this->session->has('mapping') === true ? $this->session->get('mapping') : ''; $context['source'] = $this->session->has('source') === true ? $this->session->get('source') : ''; - $context['plugin'] = isset($record['data']['plugin']) === true ? $record['data']['plugin'] : ''; + + // Add more to context if we are dealing with a log containing sourceCall data + if (isset($record['data']['sourceCall'])) { + $context = $this->addSourceCallContext($context, $record['data']['sourceCall'], $record['level_name']); + } + $context['user'] = $this->session->has('user') === true ? $this->session->get('user') : ''; $context['organization'] = $this->session->has('organization') === true ? $this->session->get('organization') : ''; $context['application'] = $this->session->has('application') === true ? $this->session->get('application') : ''; + $context['plugin'] = isset($record['data']['plugin']) === true ? $record['data']['plugin'] : ''; $context['host'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getHost() : ''; $context['ip'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getClientIp() : ''; $context['method'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getMethod() : ''; + // Add more to context for higher level logs + if (in_array($record['level_name'], ['ERROR', 'CRITICAL', 'ALERT', 'EMERGENCY']) === true) { + $context = $this->addErrorContext($context, $record['level_name']); + } + return $context; } /** * Update the context with data from the session and the request stack. For log records with level ERROR or higher. - * See: https://github.com/Seldaek/monolog/blob/main/doc/01-usage.md for all possible log levels. * - * @param array $record The log record to update the context for. + * @param array $context The log context we are updating. + * @param array $callData The [data][sourceCall] info of the log record we are updating the context for. + * @param string $levelName The level name of the log record we are updating the context for. * * @return array The updated context. */ - private function updateErrorContext(array $record): array + private function addSourceCallContext(array $context, array $callData, string $levelName): array { - $context = $record['context']; + $maxStrLength = 500; + if (in_array($levelName, ['ERROR', 'CRITICAL', 'ALERT', 'EMERGENCY']) === true) { + $maxStrLength = 2000; + } + + $context['callMethod'] = isset($callData['callMethod']) === true ? $callData['callMethod'] : ''; + $context['callUrl'] = isset($callData['callUrl']) === true ? $callData['callUrl'] : ''; + $context['callQuery'] = isset($callData['callQuery']) === true ? json_encode($callData['callQuery']) : ''; + $context['callContentType'] = isset($callData['callContentType']) === true ? $callData['callContentType'] : ''; + $context['callBody'] = isset($callData['callBody']) === true ? $callData['callBody'] : ''; + if (strlen($context['callBody']) > $maxStrLength) { + $context['callBody'] = substr($context['callBody'], 0, $maxStrLength) . '...'; + } + $context['responseStatusCode'] = isset($callData['responseStatusCode']) === true ? $callData['responseStatusCode'] : ''; + $context['responseContentType'] = isset($callData['responseContentType']) === true ? $callData['responseContentType'] : ''; + $context['responseBody'] = isset($callData['responseBody']) === true ? $callData['responseBody'] : ''; + if (strlen($context['responseBody']) > $maxStrLength) { + $context['responseBody'] = substr($context['responseBody'], 0, $maxStrLength) . '...'; + } + + return $context; + } + + /** + * Update the context with data from the session and the request stack. For log records with level ERROR or higher. + * See: https://github.com/Seldaek/monolog/blob/main/doc/01-usage.md for all possible log levels. + * + * @param array $context The log context we are updating. + * @param string $levelName The level name of the log record we are updating the context for. + * + * @return array The updated context. + */ + private function addErrorContext(array $context, string $levelName): array + { $context['pathRaw'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getPathInfo() : ''; $context['querystring'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getQueryString() : ''; $context['mongoDBFilter'] = $this->session->has('mongoDBFilter') === true ? json_encode($this->session->get('mongoDBFilter')) : ''; $context['contentType'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getContentType() : ''; // Do not log entire body for normal errors, only critical and higher - if ($record['level_name'] !== 'ERROR') { + if ($levelName !== 'ERROR') { try { $context['body'] = $this->requestStack->getMainRequest()->toArray(); } catch (Exception $exception) { @@ -138,12 +185,7 @@ public function dispatchLogCreateAction(array $record): array */ public function __invoke(array $record): array { - $record['context'] = $this->updateContext($record['context']); - - // Add more to context for higher level logs - if (in_array($record['level_name'], ['ERROR', 'CRITICAL', 'ALERT', 'EMERGENCY']) === true) { - $record['context'] = $this->updateErrorContext($record); - } + $record['context'] = $this->updateContext($record); return $this->dispatchLogCreateAction($record); } diff --git a/api/symfony.lock b/api/symfony.lock index 0664d2039..d20a5abee 100644 --- a/api/symfony.lock +++ b/api/symfony.lock @@ -785,6 +785,15 @@ "symfony/security-http": { "version": "v5.3.6" }, + "symfony/sendinblue-mailer": { + "version": "5.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "5.2", + "ref": "ae1cf494ce06b9a4578a8445c610402a1676ee8d" + } + }, "symfony/serializer": { "version": "v5.3.4" }, From a53a77df2c32e1ccadb31cde9a52203e9d4aea8d Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Mon, 20 Nov 2023 16:29:01 +0100 Subject: [PATCH 37/68] Fixes in logging context --- api/src/Logger/SessionDataProcessor.php | 31 +++++++++++-------------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/api/src/Logger/SessionDataProcessor.php b/api/src/Logger/SessionDataProcessor.php index c0d1ca436..df324e6e5 100644 --- a/api/src/Logger/SessionDataProcessor.php +++ b/api/src/Logger/SessionDataProcessor.php @@ -65,14 +65,13 @@ public function updateContext(array $record): array $context['source'] = $this->session->has('source') === true ? $this->session->get('source') : ''; // Add more to context if we are dealing with a log containing sourceCall data - if (isset($record['data']['sourceCall'])) { - $context = $this->addSourceCallContext($context, $record['data']['sourceCall'], $record['level_name']); + if (isset($context['sourceCall'])) { + $context = $this->updateSourceCallContext($context, $record['level_name']); } $context['user'] = $this->session->has('user') === true ? $this->session->get('user') : ''; $context['organization'] = $this->session->has('organization') === true ? $this->session->get('organization') : ''; $context['application'] = $this->session->has('application') === true ? $this->session->get('application') : ''; - $context['plugin'] = isset($record['data']['plugin']) === true ? $record['data']['plugin'] : ''; $context['host'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getHost() : ''; $context['ip'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getClientIp() : ''; $context['method'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getMethod() : ''; @@ -86,35 +85,31 @@ public function updateContext(array $record): array } /** - * Update the context with data from the session and the request stack. For log records with level ERROR or higher. + * Update the context for Source call logs. * * @param array $context The log context we are updating. - * @param array $callData The [data][sourceCall] info of the log record we are updating the context for. * @param string $levelName The level name of the log record we are updating the context for. * * @return array The updated context. */ - private function addSourceCallContext(array $context, array $callData, string $levelName): array + private function updateSourceCallContext(array $context, string $levelName): array { $maxStrLength = 500; if (in_array($levelName, ['ERROR', 'CRITICAL', 'ALERT', 'EMERGENCY']) === true) { $maxStrLength = 2000; } - $context['callMethod'] = isset($callData['callMethod']) === true ? $callData['callMethod'] : ''; - $context['callUrl'] = isset($callData['callUrl']) === true ? $callData['callUrl'] : ''; - $context['callQuery'] = isset($callData['callQuery']) === true ? json_encode($callData['callQuery']) : ''; - $context['callContentType'] = isset($callData['callContentType']) === true ? $callData['callContentType'] : ''; - $context['callBody'] = isset($callData['callBody']) === true ? $callData['callBody'] : ''; - if (strlen($context['callBody']) > $maxStrLength) { - $context['callBody'] = substr($context['callBody'], 0, $maxStrLength) . '...'; + $context['sourceCall']['callQuery'] = isset($context['sourceCall']['callQuery']) === true ? json_encode($context['sourceCall']['callQuery']) : ''; + if ($context['sourceCall']['callQuery'] === "[]") { + $context['sourceCall']['callQuery'] = ''; + } + + if (strlen($context['sourceCall']['callBody']) > $maxStrLength) { + $context['sourceCall']['callBody'] = substr($context['sourceCall']['callBody'], 0, $maxStrLength) . '...'; } - $context['responseStatusCode'] = isset($callData['responseStatusCode']) === true ? $callData['responseStatusCode'] : ''; - $context['responseContentType'] = isset($callData['responseContentType']) === true ? $callData['responseContentType'] : ''; - $context['responseBody'] = isset($callData['responseBody']) === true ? $callData['responseBody'] : ''; - if (strlen($context['responseBody']) > $maxStrLength) { - $context['responseBody'] = substr($context['responseBody'], 0, $maxStrLength) . '...'; + if (strlen($context['sourceCall']['responseBody']) > $maxStrLength) { + $context['sourceCall']['responseBody'] = substr($context['sourceCall']['responseBody'], 0, $maxStrLength) . '...'; } return $context; From b6620dd95650154417e7291fe5e91b2e56a1beb2 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Thu, 23 Nov 2023 09:51:40 +0100 Subject: [PATCH 38/68] Fix to prevent duplicates when loading in testdata --- api/src/Entity/Value.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/Entity/Value.php b/api/src/Entity/Value.php index 49d2d03ee..91154a87d 100644 --- a/api/src/Entity/Value.php +++ b/api/src/Entity/Value.php @@ -661,10 +661,10 @@ public function setValue($value, bool $unsafe = false, ?DateTimeInterface $dateM if (is_array($value)) { $object = null; - // Make sure to not create new objects if we don't have to... - if (isset($value['id'])) { + // Make sure to not create new objects if we don't have to (_id in testdata)... + if (isset($value['_id'])) { $objects = $this->objects->filter(function ($item) use ($value) { - return $item->getId() !== null && $item->getId()->toString() === $value['id']; + return $item->getId() !== null && $item->getId()->toString() === $value['_id']; }); if (count($objects) > 0) { From d3c7b5c1d5ee10051e8b3bbd29df3af397357650 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Thu, 23 Nov 2023 13:44:29 +0100 Subject: [PATCH 39/68] Added loggingConfig for Sources --- api/migrations/Version20230228084820.php | 1 - api/migrations/Version20230228095524.php | 1 - api/migrations/Version20230303141510.php | 1 - api/migrations/Version20230309160743.php | 1 - api/migrations/Version20230328151236.php | 1 - api/migrations/Version20230504111926.php | 1 - api/migrations/Version20230602151620.php | 1 - api/migrations/Version20230626125323.php | 1 - api/migrations/Version20230922133622.php | 1 - api/migrations/Version20230926112500.php | 3 +- api/migrations/Version20231123112218.php | 41 ++++++++++++++++++++++++ api/src/Entity/Gateway.php | 31 ++++++++++++++---- api/src/Logger/SessionDataProcessor.php | 18 ++++++----- 13 files changed, 76 insertions(+), 26 deletions(-) create mode 100644 api/migrations/Version20231123112218.php diff --git a/api/migrations/Version20230228084820.php b/api/migrations/Version20230228084820.php index 7255ac41a..51a9a8365 100644 --- a/api/migrations/Version20230228084820.php +++ b/api/migrations/Version20230228084820.php @@ -31,7 +31,6 @@ public function up(Schema $schema): void public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE SCHEMA public'); $this->addSql('ALTER TABLE cronjob DROP reference'); $this->addSql('ALTER TABLE cronjob DROP version'); $this->addSql('ALTER TABLE collection_entity DROP reference'); diff --git a/api/migrations/Version20230228095524.php b/api/migrations/Version20230228095524.php index 31c68443d..b81793b5c 100644 --- a/api/migrations/Version20230228095524.php +++ b/api/migrations/Version20230228095524.php @@ -27,7 +27,6 @@ public function up(Schema $schema): void public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE SCHEMA public'); $this->addSql('ALTER TABLE endpoint DROP reference'); $this->addSql('ALTER TABLE endpoint DROP version'); } diff --git a/api/migrations/Version20230303141510.php b/api/migrations/Version20230303141510.php index 822b540f6..2c2afd818 100644 --- a/api/migrations/Version20230303141510.php +++ b/api/migrations/Version20230303141510.php @@ -27,7 +27,6 @@ public function up(Schema $schema): void public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE SCHEMA public'); $this->addSql('ALTER TABLE gateway DROP endpoints_config'); } } diff --git a/api/migrations/Version20230309160743.php b/api/migrations/Version20230309160743.php index d3ee7bebe..3da4c568c 100644 --- a/api/migrations/Version20230309160743.php +++ b/api/migrations/Version20230309160743.php @@ -27,7 +27,6 @@ public function up(Schema $schema): void public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE SCHEMA public'); $this->addSql('ALTER TABLE application DROP configuration'); } } diff --git a/api/migrations/Version20230328151236.php b/api/migrations/Version20230328151236.php index 3595cc2e6..aecf9f48b 100644 --- a/api/migrations/Version20230328151236.php +++ b/api/migrations/Version20230328151236.php @@ -52,7 +52,6 @@ public function up(Schema $schema): void public function down(Schema $schema): void { // This down() migration is auto-generated, please modify it to your needs. - $this->addSql('CREATE SCHEMA public'); $this->addSql('ALTER TABLE security_group DROP reference'); $this->addSql('ALTER TABLE security_group DROP version'); $this->addSql('ALTER TABLE "user" DROP reference'); diff --git a/api/migrations/Version20230504111926.php b/api/migrations/Version20230504111926.php index 45bcf9466..9cb7cad35 100644 --- a/api/migrations/Version20230504111926.php +++ b/api/migrations/Version20230504111926.php @@ -29,7 +29,6 @@ public function up(Schema $schema): void public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE SCHEMA public'); $this->addSql('DROP TABLE "gateway_audit_trail"'); $this->addSql('ALTER TABLE audit_trail ALTER id TYPE UUID'); $this->addSql('ALTER TABLE audit_trail ALTER id DROP DEFAULT'); diff --git a/api/migrations/Version20230602151620.php b/api/migrations/Version20230602151620.php index e8b03f5d0..a6a6a49cd 100644 --- a/api/migrations/Version20230602151620.php +++ b/api/migrations/Version20230602151620.php @@ -26,7 +26,6 @@ public function up(Schema $schema): void public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE SCHEMA public'); $this->addSql('DROP INDEX entity_attribute_unique'); } } diff --git a/api/migrations/Version20230626125323.php b/api/migrations/Version20230626125323.php index 2f4f71ec3..dfb936b4b 100644 --- a/api/migrations/Version20230626125323.php +++ b/api/migrations/Version20230626125323.php @@ -26,7 +26,6 @@ public function up(Schema $schema): void public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE SCHEMA public'); $this->addSql('ALTER TABLE attribute ALTER allow_cascade DROP DEFAULT'); } } diff --git a/api/migrations/Version20230922133622.php b/api/migrations/Version20230922133622.php index 03fe88ae2..12ef7ca0e 100644 --- a/api/migrations/Version20230922133622.php +++ b/api/migrations/Version20230922133622.php @@ -27,7 +27,6 @@ public function up(Schema $schema): void public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE SCHEMA public'); $this->addSql('ALTER TABLE action DROP user_id'); $this->addSql('ALTER TABLE cronjob DROP user_id'); } diff --git a/api/migrations/Version20230926112500.php b/api/migrations/Version20230926112500.php index cbbde0146..8456f62ce 100644 --- a/api/migrations/Version20230926112500.php +++ b/api/migrations/Version20230926112500.php @@ -26,7 +26,6 @@ public function up(Schema $schema): void public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE SCHEMA public'); - $this->addSql('ALTER TABLE "user" DROP organisation_id'); + $this->addSql('ALTER TABLE "user" RENAME COLUMN organization_id TO organisation_id'); } } diff --git a/api/migrations/Version20231123112218.php b/api/migrations/Version20231123112218.php new file mode 100644 index 000000000..89747f4b4 --- /dev/null +++ b/api/migrations/Version20231123112218.php @@ -0,0 +1,41 @@ +addSql('ALTER TABLE gateway ALTER logging TYPE TEXT'); + $this->addSql('ALTER TABLE gateway ALTER logging SET DEFAULT \'a:10:{s:10:"callMethod";b:1;s:7:"callUrl";b:1;s:9:"callQuery";b:1;s:15:"callContentType";b:1;s:8:"callBody";b:1;s:18:"responseStatusCode";b:1;s:19:"responseContentType";b:1;s:12:"responseBody";b:1;s:16:"maxCharCountBody";i:500;s:21:"maxCharCountErrorBody";i:2000;}\''); + $this->addSql('UPDATE gateway SET logging = \'a:10:{s:10:"callMethod";b:1;s:7:"callUrl";b:1;s:9:"callQuery";b:1;s:15:"callContentType";b:1;s:8:"callBody";b:1;s:18:"responseStatusCode";b:1;s:19:"responseContentType";b:1;s:12:"responseBody";b:1;s:16:"maxCharCountBody";i:500;s:21:"maxCharCountErrorBody";i:2000;}\''); + $this->addSql('ALTER TABLE gateway ALTER logging SET NOT NULL'); + $this->addSql('COMMENT ON COLUMN gateway.logging IS \'(DC2Type:array)\''); + $this->addSql('ALTER TABLE gateway RENAME COLUMN logging TO logging_config'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE gateway RENAME COLUMN logging_config TO logging'); + $this->addSql('ALTER TABLE gateway ALTER logging DROP NOT NULL'); + $this->addSql('ALTER TABLE gateway ALTER logging DROP DEFAULT'); + $this->addSql('UPDATE gateway SET logging = NULL'); + $this->addSql('ALTER TABLE gateway ALTER logging TYPE BOOLEAN USING logging::boolean'); + $this->addSql('COMMENT ON COLUMN gateway.logging IS NULL'); + } +} diff --git a/api/src/Entity/Gateway.php b/api/src/Entity/Gateway.php index fc0e8c491..64888e31e 100644 --- a/api/src/Entity/Gateway.php +++ b/api/src/Entity/Gateway.php @@ -559,11 +559,25 @@ class Gateway private ?string $documentation = null; /** - * Setting logging to true will couse ALL responses to be logged (normaly we only log errors). Doing so wil dramaticly slow down the gateway and couse an increase in database size. This is not recomended outside of development purposes. + * Configuration for logging, when an api call is made on the source we can log some information for this call. + * With this array you can enable/disable what will be logged. * - * @ORM\Column(type="boolean", nullable=true) + * @Groups({"read","read_secure","write"}) + * + * @ORM\Column(type="array") */ - private $logging; + private array $loggingConfig = [ + 'callMethod' => true, + 'callUrl' => true, + 'callQuery' => true, + 'callContentType' => true, + 'callBody' => true, + 'responseStatusCode' => true, + 'responseContentType' => true, + 'responseBody' => true, + 'maxCharCountBody' => 500, + 'maxCharCountErrorBody' => 2000, + ]; /** * @var array ... @@ -792,6 +806,7 @@ public function fromSchema(array $schema): self array_key_exists('jwtId', $schema) ? $this->setJwtId($schema['jwtId']) : ''; array_key_exists('username', $schema) ? $this->setUsername($schema['username']) : ''; array_key_exists('documentation', $schema) ? $this->setDocumentation($schema['documentation']) : ''; + array_key_exists('loggingConfig', $schema) ? $this->setLoggingConfig($schema['loggingConfig']) : ''; array_key_exists('headers', $schema) ? $this->setHeaders($schema['headers']) : ''; array_key_exists('translationConfig', $schema) ? $this->setTranslationConfig($schema['translationConfig']) : ''; array_key_exists('type', $schema) ? $this->setType($schema['type']) : ''; @@ -826,6 +841,7 @@ public function toSchema(): array 'jwtId' => $this->getJwtId(), 'username' => $this->getUsername(), 'documentation' => $this->getDocumentation(), + 'loggingConfig' => $this->getLoggingConfig(), 'headers' => $this->getHeaders(), 'translationConfig' => $this->getTranslationConfig(), 'type' => $this->getType(), @@ -857,6 +873,7 @@ public function export(): ?array 'password' => $this->getPassword(), 'apikey' => $this->getApikey(), 'documentation' => $this->getDocumentation(), + 'loggingConfig' => $this->getLoggingConfig(), 'headers' => $this->getHeaders(), 'translationConfig' => $this->getTranslationConfig(), 'type' => $this->getType(), @@ -1129,14 +1146,14 @@ public function setDocumentation(?string $documentation): self return $this; } - public function getLogging(): ?bool + public function getLoggingConfig(): ?array { - return $this->logging; + return $this->loggingConfig; } - public function setLogging(?bool $logging): self + public function setLoggingConfig(array $loggingConfig): self { - $this->logging = $logging; + $this->loggingConfig = array_merge($loggingConfig); return $this; } diff --git a/api/src/Logger/SessionDataProcessor.php b/api/src/Logger/SessionDataProcessor.php index df324e6e5..74d6bb889 100644 --- a/api/src/Logger/SessionDataProcessor.php +++ b/api/src/Logger/SessionDataProcessor.php @@ -94,21 +94,23 @@ public function updateContext(array $record): array */ private function updateSourceCallContext(array $context, string $levelName): array { - $maxStrLength = 500; + $maxStrLength = $context['sourceCall']['maxCharCountBody'] ?? 500; if (in_array($levelName, ['ERROR', 'CRITICAL', 'ALERT', 'EMERGENCY']) === true) { - $maxStrLength = 2000; + $maxStrLength = $context['sourceCall']['maxCharCountErrorBody'] ?? 2000; } - $context['sourceCall']['callQuery'] = isset($context['sourceCall']['callQuery']) === true ? json_encode($context['sourceCall']['callQuery']) : ''; - if ($context['sourceCall']['callQuery'] === "[]") { - $context['sourceCall']['callQuery'] = ''; + if (isset($context['sourceCall']['callQuery']) === true) { + $context['sourceCall']['callQuery'] = json_encode($context['sourceCall']['callQuery']); + if ($context['sourceCall']['callQuery'] === "[]") { + $context['sourceCall']['callQuery'] = ''; + } } - if (strlen($context['sourceCall']['callBody']) > $maxStrLength) { + if (isset($context['sourceCall']['callBody']) === true && strlen($context['sourceCall']['callBody']) > $maxStrLength) { $context['sourceCall']['callBody'] = substr($context['sourceCall']['callBody'], 0, $maxStrLength) . '...'; } - if (strlen($context['sourceCall']['responseBody']) > $maxStrLength) { + if (isset($context['sourceCall']['responseBody']) === true && strlen($context['sourceCall']['responseBody']) > $maxStrLength) { $context['sourceCall']['responseBody'] = substr($context['sourceCall']['responseBody'], 0, $maxStrLength) . '...'; } @@ -132,7 +134,7 @@ private function addErrorContext(array $context, string $levelName): array $context['contentType'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getContentType() : ''; // Do not log entire body for normal errors, only critical and higher - if ($levelName !== 'ERROR') { + if ($this->requestStack->getMainRequest() && $levelName !== 'ERROR') { try { $context['body'] = $this->requestStack->getMainRequest()->toArray(); } catch (Exception $exception) { From de9d304cdbfa4dce07af2c9bcd66a8401a28c52a Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Thu, 23 Nov 2023 13:57:48 +0100 Subject: [PATCH 40/68] Don't show irrelevant properties in logs for sources --- api/src/Logger/SessionDataProcessor.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/api/src/Logger/SessionDataProcessor.php b/api/src/Logger/SessionDataProcessor.php index 74d6bb889..7568ac884 100644 --- a/api/src/Logger/SessionDataProcessor.php +++ b/api/src/Logger/SessionDataProcessor.php @@ -94,9 +94,12 @@ public function updateContext(array $record): array */ private function updateSourceCallContext(array $context, string $levelName): array { - $maxStrLength = $context['sourceCall']['maxCharCountBody'] ?? 500; if (in_array($levelName, ['ERROR', 'CRITICAL', 'ALERT', 'EMERGENCY']) === true) { $maxStrLength = $context['sourceCall']['maxCharCountErrorBody'] ?? 2000; + unset($context['sourceCall']['maxCharCountBody']); + } else { + $maxStrLength = $context['sourceCall']['maxCharCountBody'] ?? 500; + unset($context['sourceCall']['maxCharCountErrorBody']); } if (isset($context['sourceCall']['callQuery']) === true) { From e92115cb015c969adcd8f6b80158c222fca6249d Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Thu, 23 Nov 2023 14:33:47 +0100 Subject: [PATCH 41/68] Some codacy fixes --- api/migrations/Version20231123112218.php | 33 +++++++++++++++++++----- api/src/Logger/SessionDataProcessor.php | 32 +++++++++++------------ 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/api/migrations/Version20231123112218.php b/api/migrations/Version20231123112218.php index 89747f4b4..d9f5e1d82 100644 --- a/api/migrations/Version20231123112218.php +++ b/api/migrations/Version20231123112218.php @@ -12,30 +12,49 @@ */ final class Version20231123112218 extends AbstractMigration { + + + /** + * Description. + * + * @return string Description. + */ public function getDescription(): string { - return ''; - } + return 'Changed Gateway->logging boolean to Gateway->configLogging array'; + + }//end getDescription() + + /** + * Migrate up. + * + * @param Schema $schema Schema. + * @return void + */ public function up(Schema $schema): void { - // this up() migration is auto-generated, please modify it to your needs $this->addSql('ALTER TABLE gateway ALTER logging TYPE TEXT'); $this->addSql('ALTER TABLE gateway ALTER logging SET DEFAULT \'a:10:{s:10:"callMethod";b:1;s:7:"callUrl";b:1;s:9:"callQuery";b:1;s:15:"callContentType";b:1;s:8:"callBody";b:1;s:18:"responseStatusCode";b:1;s:19:"responseContentType";b:1;s:12:"responseBody";b:1;s:16:"maxCharCountBody";i:500;s:21:"maxCharCountErrorBody";i:2000;}\''); $this->addSql('UPDATE gateway SET logging = \'a:10:{s:10:"callMethod";b:1;s:7:"callUrl";b:1;s:9:"callQuery";b:1;s:15:"callContentType";b:1;s:8:"callBody";b:1;s:18:"responseStatusCode";b:1;s:19:"responseContentType";b:1;s:12:"responseBody";b:1;s:16:"maxCharCountBody";i:500;s:21:"maxCharCountErrorBody";i:2000;}\''); $this->addSql('ALTER TABLE gateway ALTER logging SET NOT NULL'); $this->addSql('COMMENT ON COLUMN gateway.logging IS \'(DC2Type:array)\''); $this->addSql('ALTER TABLE gateway RENAME COLUMN logging TO logging_config'); - } + }//end up() + /** + * Migrate down. + * + * @param Schema $schema Schema. + * @return void + */ public function down(Schema $schema): void { - // this down() migration is auto-generated, please modify it to your needs $this->addSql('ALTER TABLE gateway RENAME COLUMN logging_config TO logging'); $this->addSql('ALTER TABLE gateway ALTER logging DROP NOT NULL'); $this->addSql('ALTER TABLE gateway ALTER logging DROP DEFAULT'); $this->addSql('UPDATE gateway SET logging = NULL'); $this->addSql('ALTER TABLE gateway ALTER logging TYPE BOOLEAN USING logging::boolean'); $this->addSql('COMMENT ON COLUMN gateway.logging IS NULL'); - } -} + }//end down() +}//end class diff --git a/api/src/Logger/SessionDataProcessor.php b/api/src/Logger/SessionDataProcessor.php index 7568ac884..e16385ee2 100644 --- a/api/src/Logger/SessionDataProcessor.php +++ b/api/src/Logger/SessionDataProcessor.php @@ -64,19 +64,19 @@ public function updateContext(array $record): array $context['mapping'] = $this->session->has('mapping') === true ? $this->session->get('mapping') : ''; $context['source'] = $this->session->has('source') === true ? $this->session->get('source') : ''; - // Add more to context if we are dealing with a log containing sourceCall data - if (isset($context['sourceCall'])) { + // Add more to context if we are dealing with a log containing sourceCall data. + if (isset($context['sourceCall']) === true) { $context = $this->updateSourceCallContext($context, $record['level_name']); } $context['user'] = $this->session->has('user') === true ? $this->session->get('user') : ''; $context['organization'] = $this->session->has('organization') === true ? $this->session->get('organization') : ''; $context['application'] = $this->session->has('application') === true ? $this->session->get('application') : ''; - $context['host'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getHost() : ''; - $context['ip'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getClientIp() : ''; - $context['method'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getMethod() : ''; + $context['host'] = $this->requestStack->getMainRequest() !== null ? $this->requestStack->getMainRequest()->getHost() : ''; + $context['ip'] = $this->requestStack->getMainRequest() !== null ? $this->requestStack->getMainRequest()->getClientIp() : ''; + $context['method'] = $this->requestStack->getMainRequest() !== null ? $this->requestStack->getMainRequest()->getMethod() : ''; - // Add more to context for higher level logs + // Add more to context for higher level logs. if (in_array($record['level_name'], ['ERROR', 'CRITICAL', 'ALERT', 'EMERGENCY']) === true) { $context = $this->addErrorContext($context, $record['level_name']); } @@ -87,7 +87,7 @@ public function updateContext(array $record): array /** * Update the context for Source call logs. * - * @param array $context The log context we are updating. + * @param array $context The log context we are updating. * @param string $levelName The level name of the log record we are updating the context for. * * @return array The updated context. @@ -95,10 +95,10 @@ public function updateContext(array $record): array private function updateSourceCallContext(array $context, string $levelName): array { if (in_array($levelName, ['ERROR', 'CRITICAL', 'ALERT', 'EMERGENCY']) === true) { - $maxStrLength = $context['sourceCall']['maxCharCountErrorBody'] ?? 2000; + $maxStrLength = ($context['sourceCall']['maxCharCountErrorBody'] ?? 2000); unset($context['sourceCall']['maxCharCountBody']); } else { - $maxStrLength = $context['sourceCall']['maxCharCountBody'] ?? 500; + $maxStrLength = ($context['sourceCall']['maxCharCountBody'] ?? 500); unset($context['sourceCall']['maxCharCountErrorBody']); } @@ -110,11 +110,11 @@ private function updateSourceCallContext(array $context, string $levelName): arr } if (isset($context['sourceCall']['callBody']) === true && strlen($context['sourceCall']['callBody']) > $maxStrLength) { - $context['sourceCall']['callBody'] = substr($context['sourceCall']['callBody'], 0, $maxStrLength) . '...'; + $context['sourceCall']['callBody'] = substr($context['sourceCall']['callBody'], 0, $maxStrLength).'...'; } if (isset($context['sourceCall']['responseBody']) === true && strlen($context['sourceCall']['responseBody']) > $maxStrLength) { - $context['sourceCall']['responseBody'] = substr($context['sourceCall']['responseBody'], 0, $maxStrLength) . '...'; + $context['sourceCall']['responseBody'] = substr($context['sourceCall']['responseBody'], 0, $maxStrLength).'...'; } return $context; @@ -131,13 +131,13 @@ private function updateSourceCallContext(array $context, string $levelName): arr */ private function addErrorContext(array $context, string $levelName): array { - $context['pathRaw'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getPathInfo() : ''; - $context['querystring'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getQueryString() : ''; + $context['pathRaw'] = $this->requestStack->getMainRequest() !== null ? $this->requestStack->getMainRequest()->getPathInfo() : ''; + $context['querystring'] = $this->requestStack->getMainRequest() !== null ? $this->requestStack->getMainRequest()->getQueryString() : ''; $context['mongoDBFilter'] = $this->session->has('mongoDBFilter') === true ? json_encode($this->session->get('mongoDBFilter')) : ''; - $context['contentType'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getContentType() : ''; + $context['contentType'] = $this->requestStack->getMainRequest() !== null ? $this->requestStack->getMainRequest()->getContentType() : ''; - // Do not log entire body for normal errors, only critical and higher - if ($this->requestStack->getMainRequest() && $levelName !== 'ERROR') { + // Do not log entire body for normal errors, only critical and higher. + if ($this->requestStack->getMainRequest() !== null && $levelName !== 'ERROR') { try { $context['body'] = $this->requestStack->getMainRequest()->toArray(); } catch (Exception $exception) { From 919e23ec50fc7cec287cb472bec7b0d7214888b1 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Fri, 24 Nov 2023 11:39:55 +0100 Subject: [PATCH 42/68] Let's remove read secure for Gateway->loggingConfig --- api/src/Entity/Gateway.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/Entity/Gateway.php b/api/src/Entity/Gateway.php index 64888e31e..58f74e900 100644 --- a/api/src/Entity/Gateway.php +++ b/api/src/Entity/Gateway.php @@ -562,7 +562,7 @@ class Gateway * Configuration for logging, when an api call is made on the source we can log some information for this call. * With this array you can enable/disable what will be logged. * - * @Groups({"read","read_secure","write"}) + * @Groups({"read","write"}) * * @ORM\Column(type="array") */ From 7396daf0b4e519beda0dcc5c37e612fc50ad018c Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Fri, 24 Nov 2023 12:57:58 +0100 Subject: [PATCH 43/68] Clear query,result & metadata doctrine cache before migrations in init --- api/docker/php/docker-entrypoint.sh | 8 ++++++++ api/src/Entity/Gateway.php | 2 ++ 2 files changed, 10 insertions(+) diff --git a/api/docker/php/docker-entrypoint.sh b/api/docker/php/docker-entrypoint.sh index 7050456b6..a7a99f934 100644 --- a/api/docker/php/docker-entrypoint.sh +++ b/api/docker/php/docker-entrypoint.sh @@ -34,6 +34,14 @@ if [ "$1" = 'php-fpm' ] || [ "$1" = 'php' ] || [ "$1" = 'bin/console' ]; then echo "Creating the database" bin/console doctrine:database:create --if-not-exists --no-interaction + # Make sure we clear query,result & metadata doctrine cache before migrations during init + if [ "$APP_INIT" = 'true' ]; then + echo "Clearing query,result & metadata doctrine cache" + bin/console doctrine:cache:clear-query + bin/console doctrine:cache:clear-result + bin/console doctrine:cache:clear-metadata + fi + # Get the database inline with the newest version. echo "Migrating the database to the currently used version" bin/console doctrine:migrations:migrate --no-interaction diff --git a/api/src/Entity/Gateway.php b/api/src/Entity/Gateway.php index 58f74e900..9ff175dff 100644 --- a/api/src/Entity/Gateway.php +++ b/api/src/Entity/Gateway.php @@ -562,6 +562,8 @@ class Gateway * Configuration for logging, when an api call is made on the source we can log some information for this call. * With this array you can enable/disable what will be logged. * + * @Assert\NotNull + * * @Groups({"read","write"}) * * @ORM\Column(type="array") From a236b5a646de0fe357c66d485e3f13d5edfa39a0 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Mon, 27 Nov 2023 15:24:39 +0100 Subject: [PATCH 44/68] Require latest version CoreBundle or higher --- api/composer.json | 2 +- api/composer.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/composer.json b/api/composer.json index aa2726af6..e770835d7 100644 --- a/api/composer.json +++ b/api/composer.json @@ -13,7 +13,7 @@ "alcaeus/mongo-php-adapter": "^1.2", "api-platform/core": "^2.6", "beberlei/doctrineextensions": "^1.3", - "commongateway/corebundle": "^1.1.74", + "commongateway/corebundle": "^1.2.30", "composer/package-versions-deprecated": "1.11.99.3", "conduction/commongroundbundle": "dev-feature-gateway", "conduction/digidbundle": "dev-master", diff --git a/api/composer.lock b/api/composer.lock index 299b34e96..d774589af 100644 --- a/api/composer.lock +++ b/api/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7c977e35c7047aed418241d4b550d8af", + "content-hash": "7fea6053a03cf5e0d75d8c8180484df3", "packages": [ { "name": "adbario/php-dot-notation", @@ -660,16 +660,16 @@ }, { "name": "commongateway/corebundle", - "version": "1.1.74", + "version": "1.2.30", "source": { "type": "git", "url": "https://github.com/CommonGateway/CoreBundle.git", - "reference": "7a4bd00b95f84e1a5ca5f787b64635941ad22a83" + "reference": "86fe52055235f37195e9eaa276f786a7f8f39ac8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CommonGateway/CoreBundle/zipball/7a4bd00b95f84e1a5ca5f787b64635941ad22a83", - "reference": "7a4bd00b95f84e1a5ca5f787b64635941ad22a83", + "url": "https://api.github.com/repos/CommonGateway/CoreBundle/zipball/86fe52055235f37195e9eaa276f786a7f8f39ac8", + "reference": "86fe52055235f37195e9eaa276f786a7f8f39ac8", "shasum": "" }, "require": { @@ -783,7 +783,7 @@ "issues": "https://github.com/CommonGateway/CoreBundle/issues", "source": "https://github.com/CommonGateway/CoreBundle" }, - "time": "2023-08-16T09:52:03+00:00" + "time": "2023-11-24T15:08:02+00:00" }, { "name": "composer/ca-bundle", @@ -15554,5 +15554,5 @@ "ext-json": "*" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } From 0b1870f05e7f53b91dd697ee0543dd4e63a06d2a Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Fri, 1 Dec 2023 16:27:57 +0100 Subject: [PATCH 45/68] Start removing Email BL --- api/src/ActionHandler/EmailHandler.php | 109 ---- api/src/Service/EmailService.php | 133 ----- .../bisc/password-forgot-e-mail.html.twig | 525 ------------------ .../emails/gateway/new-log-e-mail.html.twig | 503 ----------------- .../emails/kiss/new-review-e-mail.html.twig | 491 ---------------- 5 files changed, 1761 deletions(-) delete mode 100644 api/src/ActionHandler/EmailHandler.php delete mode 100644 api/src/Service/EmailService.php delete mode 100644 api/templates/emails/bisc/password-forgot-e-mail.html.twig delete mode 100644 api/templates/emails/gateway/new-log-e-mail.html.twig delete mode 100644 api/templates/emails/kiss/new-review-e-mail.html.twig diff --git a/api/src/ActionHandler/EmailHandler.php b/api/src/ActionHandler/EmailHandler.php deleted file mode 100644 index f21e338e5..000000000 --- a/api/src/ActionHandler/EmailHandler.php +++ /dev/null @@ -1,109 +0,0 @@ -emailService = $emailService; - } - - /** - * This function returns the requered configuration as a [json-schema](https://json-schema.org/) array. - * - * @throws array a [json-schema](https://json-schema.org/) that this action should comply to - */ - public function getConfiguration(): array - { - return [ - '$id' => 'https://commongateway.nl/ActionHandler/EmailHandler.ActionHandler.json', - '$schema' => 'https://docs.commongateway.nl/schemas/ActionHandler.schema.json', - 'title' => 'EmailHandler', - 'required' => ['ServiceDNS', 'template', 'sender', 'receiver', 'subject'], - 'properties' => [ - 'serviceDNS' => [ - 'type' => 'string', - 'description' => 'The DNS of the mail provider, see https://symfony.com/doc/6.2/mailer.html for details', - 'example' => 'native://default', - 'required' => true, - ], - 'template' => [ - 'type' => 'string', - 'description' => 'The actual email template, should be a base64 encoded twig template', - 'example' => 'eyMgdG9kbzogbW92ZSB0aGlzIHRvIGFuIGVtYWlsIHBsdWdpbiAoc2VlIEVtYWlsU2VydmljZS5waHApICN9CjwhRE9DVFlQRSBodG1sIFBVQkxJQyAiLS8vVzNDLy9EVEQgWEhUTUwgMS4wIFRyYW5zaXRpb25hbC8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9UUi94aHRtbDEvRFREL3hodG1sMS10cmFuc2l0aW9uYWwuZHRkIj4KPGh0bWwgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiPgo8aGVhZD4KICA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMCIgLz4KICA8bWV0YSBodHRwLWVxdWl2PSJDb250ZW50LVR5cGUiIGNvbnRlbnQ9InRleHQvaHRtbDsgY2hhcnNldD1VVEYtOCIgLz4KICA8dGl0bGU+e3sgc3ViamVjdCB9fTwvdGl0bGU+CgogIDxsaW5rIHJlbD0icHJlY29ubmVjdCIgaHJlZj0iaHR0cHM6Ly9mb250cy5nc3RhdGljLmNvbSIgLz4KICA8bGluawogICAgICAgICAgaHJlZj0iaHR0cHM6Ly9mb250cy5nb29nbGVhcGlzLmNvbS9jc3MyP2ZhbWlseT1GYXVzdGluYTp3Z2h0QDYwMCZkaXNwbGF5PXN3YXAiCiAgICAgICAgICByZWw9InN0eWxlc2hlZXQiCiAgLz4KICA8bGluawogICAgICAgICAgaHJlZj0iaHR0cHM6Ly9mb250cy5nb29nbGVhcGlzLmNvbS9jc3MyP2ZhbWlseT1Tb3VyY2UrU2FucytQcm8mZGlzcGxheT1zd2FwIgogICAgICAgICAgcmVsPSJzdHlsZXNoZWV0IgogIC8+CgogIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyIgcmVsPSJzdHlsZXNoZWV0IiBtZWRpYT0iYWxsIj4KICAgIC8qIEJhc2UgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICovCgogICAgYm9keSB7CiAgICAgIHdpZHRoOiAxMDAlICFpbXBvcnRhbnQ7CiAgICAgIGhlaWdodDogMTAwJTsKICAgICAgbWFyZ2luOiAwOwogICAgICBtc28tbGluZS1oZWlnaHQtcnVsZTogZXhhY3RseTsKICAgICAgbGluZS1oZWlnaHQ6IDEuNDsKICAgICAgYmFja2dyb3VuZC1jb2xvcjogI2ZmZmZmZjsKICAgICAgY29sb3I6ICM3NDc4N2U7CiAgICAgIC13ZWJraXQtdGV4dC1zaXplLWFkanVzdDogbm9uZTsKICAgIH0KCiAgICBwLAogICAgdWwsCiAgICBvbCwKICAgIGJsb2NrcXVvdGUgewogICAgICBtc28tbGluZS1oZWlnaHQtcnVsZTogZXhhY3RseTsKICAgICAgbGluZS1oZWlnaHQ6IDEuNDsKICAgICAgdGV4dC1hbGlnbjogbGVmdDsKICAgIH0KCiAgICBhIHsKICAgICAgY29sb3I6ICMxZDU1ZmY7CiAgICAgIHRleHQtZGVjb3JhdGlvbjogbm9uZTsKICAgIH0KCiAgICBhOmhvdmVyIHsKICAgICAgdGV4dC1kZWNvcmF0aW9uOiB1bmRlcmxpbmU7CiAgICB9CgogICAgcCBhIHsKICAgICAgdGV4dC1kZWNvcmF0aW9uOiB1bmRlcmxpbmU7CiAgICB9CgogICAgYSBpbWcgewogICAgICBib3JkZXI6IG5vbmU7CiAgICB9CgogICAgdGQgewogICAgICB3b3JkLWJyZWFrOiBicmVhay13b3JkOwogICAgfQogICAgLyogTGF5b3V0IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAqLwoKICAgIC5oZWFkZXIgewogICAgICBiYWNrZ3JvdW5kOiAjMWQ1NWZmOwogICAgICB3aWR0aDogMTAwJTsKICAgICAgaGVpZ2h0OiAyMzZweDsKICAgICAgYmFja2dyb3VuZC1yZXBlYXQ6IG5vLXJlcGVhdDsKICAgICAgYmFja2dyb3VuZC1wb3NpdGlvbjogY2VudGVyOwogICAgfQoKICAgIC5oZWFkZXItY2VsbCB7CiAgICAgIHBhZGRpbmc6IDE2cHggMjRweDsKICAgIH0KCiAgICAuZW1haWwtd3JhcHBlciB7CiAgICAgIHdpZHRoOiAxMDAlOwogICAgICBtYXJnaW46IDA7CiAgICAgIHBhZGRpbmc6IDA7CiAgICAgIC1wcmVtYWlsZXItd2lkdGg6IDEwMCU7CiAgICAgIC1wcmVtYWlsZXItY2VsbHBhZGRpbmc6IDA7CiAgICAgIC1wcmVtYWlsZXItY2VsbHNwYWNpbmc6IDA7CiAgICAgIGJhY2tncm91bmQtY29sb3I6ICNmZmZmZmY7CiAgICB9CgogICAgLmVtYWlsLWNvbnRlbnQgewogICAgICB3aWR0aDogMTAwJTsKICAgICAgbWFyZ2luOiAwOwogICAgICBwYWRkaW5nOiAwOwogICAgICAtcHJlbWFpbGVyLXdpZHRoOiAxMDAlOwogICAgICAtcHJlbWFpbGVyLWNlbGxwYWRkaW5nOiAwOwogICAgICAtcHJlbWFpbGVyLWNlbGxzcGFjaW5nOiAwOwogICAgfQogICAgLyogTWFzdGhlYWQgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gKi8KCiAgICAuZW1haWwtbWFzdGhlYWQgewogICAgICBwYWRkaW5nOiAyNXB4IDA7CiAgICAgIHRleHQtYWxpZ246IGNlbnRlcjsKICAgIH0KCiAgICAuZW1haWwtbWFzdGhlYWRfbG9nbyB7CiAgICAgIHdpZHRoOiA5NHB4OwogICAgfQoKICAgIC5lbWFpbC1tYXN0aGVhZF9uYW1lIHsKICAgICAgZm9udC1zaXplOiAxNnB4OwogICAgICBmb250LXdlaWdodDogNjAwOwogICAgICBjb2xvcjogI2JiYmZjMzsKICAgICAgdGV4dC1kZWNvcmF0aW9uOiBub25lOwogICAgICB0ZXh0LXNoYWRvdzogMCAxcHggMCB3aGl0ZTsKICAgIH0KICAgIC8qIEJvZHkgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICovCgogICAgLmVtYWlsLWJvZHkgewogICAgICB3aWR0aDogMTAwJTsKICAgICAgbWFyZ2luOiAwOwogICAgICBwYWRkaW5nOiAwOwogICAgICAtcHJlbWFpbGVyLXdpZHRoOiAxMDAlOwogICAgICAtcHJlbWFpbGVyLWNlbGxwYWRkaW5nOiAwOwogICAgICAtcHJlbWFpbGVyLWNlbGxzcGFjaW5nOiAwOwogICAgICBiYWNrZ3JvdW5kOiBub25lOwogICAgfQoKICAgIC5lbWFpbC1ib2R5X2lubmVyIHsKICAgICAgd2lkdGg6IDY0MHB4OwogICAgICBtYXJnaW46IDAgYXV0bzsKICAgICAgcGFkZGluZzogMDsKICAgICAgLXByZW1haWxlci13aWR0aDogNTcwcHg7CiAgICAgIC1wcmVtYWlsZXItY2VsbHBhZGRpbmc6IDA7CiAgICAgIC1wcmVtYWlsZXItY2VsbHNwYWNpbmc6IDA7CiAgICAgIGJhY2tncm91bmQtY29sb3I6ICNmZmZmZmY7CiAgICB9CgogICAgLmVtYWlsLWZvb3RlciB7CiAgICAgIHdpZHRoOiA2NDBweDsKICAgICAgbWFyZ2luOiAwIGF1dG87CiAgICAgIHBhZGRpbmc6IDA7CiAgICAgIC1wcmVtYWlsZXItd2lkdGg6IDU3MHB4OwogICAgICAtcHJlbWFpbGVyLWNlbGxwYWRkaW5nOiAwOwogICAgICAtcHJlbWFpbGVyLWNlbGxzcGFjaW5nOiAwOwogICAgICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgICB9CgogICAgLmVtYWlsLWZvb3RlciBwIHsKICAgICAgY29sb3I6ICNhZWFlYWU7CiAgICB9CgogICAgLmJvZHktYWN0aW9uIHsKICAgICAgd2lkdGg6IDEwMCU7CiAgICAgIG1hcmdpbjogNDBweCBhdXRvOwogICAgICBwYWRkaW5nOiAwOwogICAgICAtcHJlbWFpbGVyLXdpZHRoOiAxMDAlOwogICAgICAtcHJlbWFpbGVyLWNlbGxwYWRkaW5nOiAwOwogICAgICAtcHJlbWFpbGVyLWNlbGxzcGFjaW5nOiAwOwogICAgICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgICB9CgogICAgLmJvZHktc3ViIHsKICAgICAgbWFyZ2luLXRvcDogMjVweDsKICAgICAgcGFkZGluZy10b3A6IDI1cHg7CiAgICAgIGJvcmRlci10b3A6IDFweCBzb2xpZCAjZWRlZmYyOwogICAgfQoKICAgIC5jb250ZW50LWNlbGwgewogICAgICBwYWRkaW5nOiAzNnB4IDE2cHg7CiAgICB9CgogICAgLnByZWhlYWRlciB7CiAgICAgIGRpc3BsYXk6IG5vbmUgIWltcG9ydGFudDsKICAgICAgdmlzaWJpbGl0eTogaGlkZGVuOwogICAgICBtc28taGlkZTogYWxsOwogICAgICBmb250LXNpemU6IDFweDsKICAgICAgbXNvLWxpbmUtaGVpZ2h0LXJ1bGU6IGV4YWN0bHk7CiAgICAgIGxpbmUtaGVpZ2h0OiAxcHg7CiAgICAgIG1heC1oZWlnaHQ6IDA7CiAgICAgIG1heC13aWR0aDogMDsKICAgICAgb3BhY2l0eTogMDsKICAgICAgb3ZlcmZsb3c6IGhpZGRlbjsKICAgIH0KICAgIC8qIEF0dHJpYnV0ZSBsaXN0IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAqLwoKICAgIC5hdHRyaWJ1dGVzIHsKICAgICAgbWFyZ2luOiAwIDAgMjFweDsKICAgIH0KCiAgICAuYXR0cmlidXRlc19jb250ZW50IHsKICAgICAgYmFja2dyb3VuZC1jb2xvcjogI2VkZWZmMjsKICAgICAgcGFkZGluZzogMTZweDsKICAgIH0KCiAgICAuYXR0cmlidXRlc19pdGVtIHsKICAgICAgcGFkZGluZzogMDsKICAgIH0KICAgIC8qIFJlbGF0ZWQgSXRlbXMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICovCgogICAgLnJlbGF0ZWQgewogICAgICB3aWR0aDogMTAwJTsKICAgICAgbWFyZ2luOiAwOwogICAgICBwYWRkaW5nOiAyNXB4IDAgMCAwOwogICAgICAtcHJlbWFpbGVyLXdpZHRoOiAxMDAlOwogICAgICAtcHJlbWFpbGVyLWNlbGxwYWRkaW5nOiAwOwogICAgICAtcHJlbWFpbGVyLWNlbGxzcGFjaW5nOiAwOwogICAgfQoKICAgIC5yZWxhdGVkX2l0ZW0gewogICAgICBwYWRkaW5nOiAxMHB4IDA7CiAgICAgIGNvbG9yOiAjNzQ3ODdlOwogICAgICBmb250LXNpemU6IDE1cHg7CiAgICAgIG1zby1saW5lLWhlaWdodC1ydWxlOiBleGFjdGx5OwogICAgICBsaW5lLWhlaWdodDogMThweDsKICAgIH0KCiAgICAucmVsYXRlZF9pdGVtLXRpdGxlIHsKICAgICAgZGlzcGxheTogYmxvY2s7CiAgICAgIG1hcmdpbjogMC41ZW0gMCAwOwogICAgfQoKICAgIC5yZWxhdGVkX2l0ZW0tdGh1bWIgewogICAgICBkaXNwbGF5OiBibG9jazsKICAgICAgcGFkZGluZy1ib3R0b206IDEwcHg7CiAgICB9CgogICAgLnJlbGF0ZWRfaGVhZGluZyB7CiAgICAgIGJvcmRlci10b3A6IDFweCBzb2xpZCAjZWRlZmYyOwogICAgICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgICAgIHBhZGRpbmc6IDI1cHggMCAxMHB4OwogICAgfQoKICAgIC8qIFV0aWxpdGllcyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gKi8KCiAgICAubm8tbWFyZ2luIHsKICAgICAgbWFyZ2luOiAwOwogICAgfQoKICAgIC5tYXJnaW4tdG9wIHsKICAgICAgbWFyZ2luLXRvcDogOHB4OwogICAgfQoKICAgIC5hbGlnbi1yaWdodCB7CiAgICAgIHRleHQtYWxpZ246IHJpZ2h0OwogICAgfQoKICAgIC5hbGlnbi1sZWZ0IHsKICAgICAgdGV4dC1hbGlnbjogbGVmdDsKICAgIH0KCiAgICAuYWxpZ24tY2VudGVyIHsKICAgICAgdGV4dC1hbGlnbjogY2VudGVyOwogICAgfQogICAgLypNZWRpYSBRdWVyaWVzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAqLwoKICAgIEBtZWRpYSBvbmx5IHNjcmVlbiBhbmQgKG1heC13aWR0aDogNjAwcHgpIHsKICAgICAgLmVtYWlsLWJvZHlfaW5uZXIsCiAgICAgIC5lbWFpbC1mb290ZXIgewogICAgICAgIHdpZHRoOiAxMDAlICFpbXBvcnRhbnQ7CiAgICAgIH0KICAgIH0KCiAgICBAbWVkaWEgb25seSBzY3JlZW4gYW5kIChtYXgtd2lkdGg6IDUwMHB4KSB7CiAgICAgIC5idXR0b24gewogICAgICAgIHdpZHRoOiAxMDAlICFpbXBvcnRhbnQ7CiAgICAgIH0KICAgIH0KCiAgICAvKiBDYXJkcyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gKi8KICAgIC5jYXJkIHsKICAgICAgYmFja2dyb3VuZC1jb2xvcjogI2ZmZjsKICAgICAgYm9yZGVyLXRvcDogMXB4IHNvbGlkICNlMGUxZTU7CiAgICAgIGJvcmRlci1yaWdodDogMXB4IHNvbGlkICNlMGUxZTU7CiAgICAgIGJvcmRlci1ib3R0b206IDFweCBzb2xpZCAjZTBlMWU1OwogICAgICBib3JkZXItbGVmdDogMXB4IHNvbGlkICNlMGUxZTU7CiAgICAgIHBhZGRpbmc6IDI0cHg7CiAgICAgIGRpc3BsYXk6IGlubGluZS1ibG9jazsKICAgICAgY29sb3I6ICMzOTM5M2E7CiAgICAgIHRleHQtZGVjb3JhdGlvbjogbm9uZTsKICAgICAgd2lkdGg6IDEwMCU7CiAgICAgIGJvcmRlci1yYWRpdXM6IDNweDsKICAgICAgYm94LXNoYWRvdzogMCA0cHggM3B4IC0zcHggcmdiYSgwLCAwLCAwLCAwLjA4KTsKICAgICAgLXdlYmtpdC10ZXh0LXNpemUtYWRqdXN0OiBub25lOwogICAgICBtc28tbGluZS1oZWlnaHQtcnVsZTogZXhhY3RseTsKICAgICAgbGluZS1oZWlnaHQ6IDEuNzU7CiAgICAgIGxldHRlci1zcGFjaW5nOiAwLjhweDsKICAgIH0KCiAgICAvKiBCdXR0b25zIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAqLwoKICAgIC5idXR0b24gewogICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjMWRiNGVkOwogICAgICBib3JkZXItdG9wOiAxMHB4IHNvbGlkICMxZGI0ZWQ7CiAgICAgIGJvcmRlci1yaWdodDogMThweCBzb2xpZCAjMWRiNGVkOwogICAgICBib3JkZXItYm90dG9tOiAxMHB4IHNvbGlkICMxZGI0ZWQ7CiAgICAgIGJvcmRlci1sZWZ0OiAxOHB4IHNvbGlkICMxZGI0ZWQ7CiAgICAgIGRpc3BsYXk6IGlubGluZS1ibG9jazsKICAgICAgY29sb3I6ICNmZmY7CiAgICAgIHRleHQtZGVjb3JhdGlvbjogbm9uZTsKICAgICAgYm9yZGVyLXJhZGl1czogNHB4OwogICAgICBib3gtc2hhZG93OiAwIDJweCAzcHggcmdiYSgwLCAwLCAwLCAwLjE2KTsKICAgICAgLXdlYmtpdC10ZXh0LXNpemUtYWRqdXN0OiBub25lOwogICAgICBtc28tbGluZS1oZWlnaHQtcnVsZTogZXhhY3RseTsKICAgICAgd2lkdGg6IDEwMCU7CiAgICAgIHRleHQtYWxpZ246IGNlbnRlcjsKICAgICAgZm9udC1zaXplOiAxNHB4OwogICAgICBmb250LXdlaWdodDogNjAwOwogICAgfQoKICAgIC5zbWFsbC1sb2dvIHsKICAgICAgd2lkdGg6IDI0cHg7CiAgICAgIGhlaWdodDogMjRweDsKICAgIH0KCiAgICAuaW5saW5lIHsKICAgICAgZGlzcGxheTogaW5saW5lOwogICAgfQogICAgLyogVHlwZSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gKi8KCiAgICBwIHsKICAgICAgbWFyZ2luOiAwOwogICAgICBjb2xvcjogIzM5MzkzYTsKICAgICAgZm9udC1zaXplOiAxNXB4OwogICAgICBtc28tbGluZS1oZWlnaHQtcnVsZTogZXhhY3RseTsKICAgICAgbGV0dGVyLXNwYWNpbmc6IG5vcm1hbDsKICAgICAgdGV4dC1hbGlnbjogbGVmdDsKICAgICAgbGluZS1oZWlnaHQ6IDIwcHg7CiAgICB9CgogICAgcCArIHAgewogICAgICBtYXJnaW4tdG9wOiAyMHB4OwogICAgfQoKICAgIHAuc3VmZml4IHsKICAgICAgZm9udC1zaXplOiAxNHB4OwogICAgfQoKICAgIHAuc3ViIHsKICAgICAgZm9udC1zaXplOiAxMnB4OwogICAgfQoKICAgIHAuY2VudGVyIHsKICAgICAgdGV4dC1hbGlnbjogY2VudGVyOwogICAgfQoKICAgIC5zdWJ0bGUgewogICAgICBjb2xvcjogI2IxYjFiMTsKICAgIH0KCiAgICAvKiBGb290ZXIgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICovCgogICAgLmxvZ28tbGFiZWwgewogICAgICB2ZXJ0aWNhbC1hbGlnbjogdG9wOwogICAgICBmb250LXNpemU6IDE0cHg7CiAgICAgIG1hcmdpbi1sZWZ0OiA0cHg7CiAgICB9CgogICAgLmZvb3Rlci1jZWxsIHsKICAgICAgcGFkZGluZzogOHB4IDI0cHg7CiAgICB9CgogICAgLmZvb3Rlci1uYXYgewogICAgICBtYXJnaW4tbGVmdDogOHB4OwogICAgICBmb250LXNpemU6IDE0cHg7CiAgICAgIGNvbG9yOiAjMzkzOTNhOwogICAgICB0ZXh0LWRlY29yYXRpb246IG5vbmU7CiAgICB9CgogICAgLmhlYWRlci1saW5rIHsKICAgICAgdGV4dC1kZWNvcmF0aW9uOiBub25lOwogICAgICBmb250LXNpemU6IDE0cHg7CiAgICAgIGNvbG9yOiAjMWQ1NWZmOwogICAgICBmb250LXdlaWdodDogNTAwOwogICAgfQoKICAgIC5tYXJnaW4tdG9wIHsKICAgICAgbWFyZ2luLXRvcDogMTZweDsKICAgIH0KCiAgICAubG9nby1jb250YWluZXIgewogICAgICB3aWR0aDogMTAwJTsKICAgICAgbWFyZ2luLWJvdHRvbTogNTZweDsKICAgIH0KCiAgICAubG9nbyB7CiAgICAgIGRpc3BsYXk6IGJsb2NrOwogICAgfQoKICAgIC8qIEN1c3RvbSBzdHlsZXMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICovCiAgICBociB7CiAgICAgIGJvcmRlci10b3A6IDFweCBzb2xpZCAjZDlkOWRlOwogICAgICBjb2xvcjogI2Q5ZDlkZTsKICAgICAgYmFja2dyb3VuZC1jb2xvcjogI2Q5ZDlkZTsKICAgICAgbWFyZ2luLXRvcDogMzJweDsKICAgICAgbWFyZ2luLWJvdHRvbTogNDBweDsKICAgIH0KCiAgICBoMSB7CiAgICAgIGZvbnQtZmFtaWx5OiAiRmF1c3RpbmEiLCBzZXJpZjsKICAgICAgZm9udC1zaXplOiAzMnB4OwogICAgICBmb250LXdlaWdodDogNjAwOwogICAgICBjb2xvcjogIzIzMjMyNjsKICAgICAgbWFyZ2luLWJvdHRvbTogMjJweDsKICAgIH0KCiAgICBwIHsKICAgICAgZm9udC1mYW1pbHk6ICJTb3VyY2UgU2FucyBQcm8iLCBzYW5zLXNlcmlmOwogICAgICBmb250LXNpemU6IDE4cHg7CiAgICAgIGxpbmUtaGVpZ2h0OiAxLjY7CiAgICAgIGNvbG9yOiAjMjMyMzI2OwogICAgfQoKICAgIC5idXR0b24gewogICAgICBmb250LWZhbWlseTogIlNvdXJjZSBTYW5zIFBybyIsIHNhbnMtc2VyaWY7CiAgICB9CgogICAgLmNvbnRlbnQtY2VsbCB7CiAgICAgIHBhZGRpbmc6IDQwcHggNDBweDsKICAgIH0KCiAgICAuYnV0dG9uIHsKICAgICAgYmFja2dyb3VuZC1jb2xvcjogI2ZmNWEyNjsKICAgICAgYm9yZGVyLXRvcDogMTBweCBzb2xpZCAjZmY1YTI2OwogICAgICBib3JkZXItcmlnaHQ6IDE4cHggc29saWQgI2ZmNWEyNjsKICAgICAgYm9yZGVyLWJvdHRvbTogMTBweCBzb2xpZCAjZmY1YTI2OwogICAgICBib3JkZXItbGVmdDogMThweCBzb2xpZCAjZmY1YTI2OwogICAgICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7CiAgICAgIGNvbG9yOiAjZmZmOwogICAgICB3aWR0aDogYXV0bzsKICAgICAgYm94LXNoYWRvdzogbm9uZTsKICAgICAgdGV4dC1kZWNvcmF0aW9uOiBub25lOwogICAgICBib3JkZXItcmFkaXVzOiA4cHg7CiAgICAgIC13ZWJraXQtdGV4dC1zaXplLWFkanVzdDogbm9uZTsKICAgICAgbXNvLWxpbmUtaGVpZ2h0LXJ1bGU6IGV4YWN0bHk7CiAgICAgIHRleHQtYWxpZ246IGNlbnRlcjsKICAgICAgZm9udC1zaXplOiAxNHB4OwogICAgICBmb250LXdlaWdodDogNjAwOwogICAgfQogIDwvc3R5bGU+CjwvaGVhZD4KPGJvZHk+Cjx0YWJsZSBjbGFzcz0iZW1haWwtd3JhcHBlciIgd2lkdGg9IjEwMCUiIGNlbGxwYWRkaW5nPSIwIiBjZWxsc3BhY2luZz0iMCI+CiAgPHRyPgogICAgPHRkIGFsaWduPSJjZW50ZXIiPgogICAgICA8dGFibGUKICAgICAgICAgICAgICBjbGFzcz0iZW1haWwtY29udGVudCIKICAgICAgICAgICAgICB3aWR0aD0iMTAwJSIKICAgICAgICAgICAgICBjZWxscGFkZGluZz0iMCIKICAgICAgICAgICAgICBjZWxsc3BhY2luZz0iMCIKICAgICAgPgogICAgICAgIDx0cj4KICAgICAgICAgIDx0ZCBjbGFzcz0iZW1haWwtbWFzdGhlYWQiPjwvdGQ+CiAgICAgICAgPC90cj4KICAgICAgICA8IS0tIEVtYWlsIEJvZHkgLS0+CiAgICAgICAgPHRyPgogICAgICAgICAgPHRkCiAgICAgICAgICAgICAgICAgIGNsYXNzPSJlbWFpbC1ib2R5IgogICAgICAgICAgICAgICAgICB3aWR0aD0iMTAwJSIKICAgICAgICAgICAgICAgICAgY2VsbHBhZGRpbmc9IjAiCiAgICAgICAgICAgICAgICAgIGNlbGxzcGFjaW5nPSIwIgogICAgICAgICAgPgogICAgICAgICAgICA8dGFibGUKICAgICAgICAgICAgICAgICAgICBjbGFzcz0iZW1haWwtYm9keV9pbm5lciIKICAgICAgICAgICAgICAgICAgICBhbGlnbj0iY2VudGVyIgogICAgICAgICAgICAgICAgICAgIHdpZHRoPSIxMDAlIgogICAgICAgICAgICAgICAgICAgIGJhY2tncm91bmQtY29sb3I9IiNlZGVmZjIiCiAgICAgICAgICAgICAgICAgICAgY2VsbHBhZGRpbmc9IjAiCiAgICAgICAgICAgICAgICAgICAgY2VsbHNwYWNpbmc9IjAiCiAgICAgICAgICAgID4KICAgICAgICAgICAgICA8IS0tIEJvZHkgY29udGVudCAtLT4KICAgICAgICAgICAgICA8dHI+PC90cj4KICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImNvbnRlbnQtY2VsbCIgd2lkdGg9IjEwMCUiPgogICAgICAgICAgICAgICAgICA8cD4KICAgICAgICAgICAgICAgICAgICAgIHt7IGF1dGhvciB9fSBoZWVmdCBmZWVkYmFjayBnZWdldmVuIG9wOiB7eyB0b3BpYyB9fQogICAgICAgICAgICAgICAgICA8L3A+CiAgICAgICAgICAgICAgICAgIDxici8+CiAgICAgICAgICAgICAgICAgIDxwPgogICAgICAgICAgICAgICAgICAgICAge3sgZGVzY3JpcHRpb24gfCBubDJiciB9fQogICAgICAgICAgICAgICAgICA8L3A+CiAgICAgICAgICAgICAgICAgIDxociAvPgogICAgICAgICAgICAgICAgICA8cD5NZXQgdnJpZW5kZWxpamtlIGdyb2V0LDwvcD4KICAgICAgICAgICAgICAgICAgPHA+S0lTUzwvcD4KICAgICAgICAgICAgICAgIDwvdGQ+CiAgICAgICAgICAgICAgPC90cj4KICAgICAgICAgICAgICA8dHI+PC90cj4KICAgICAgICAgICAgPC90YWJsZT4KICAgICAgICAgIDwvdGQ+CiAgICAgICAgPC90cj4KICAgICAgPC90YWJsZT4KICAgIDwvdGQ+CiAgPC90cj4KPC90YWJsZT4KPC9ib2R5Pgo8L2h0bWw+Cg==', - 'required' => true, - ], - 'variables' => [ - 'type' => 'array', - 'description' => 'The variables supported by this template (might contain default vallues)', - 'nullable' => true, - ], - 'sender' => [ - 'type' => 'string', - 'description' => 'The sender of the email', - 'example' => 'info@conduction.nl', - 'required' => true, - ], - 'receiver' => [ - 'type' => 'string', - 'description' => 'The receiver of the email', - 'example' => 'j.do@conduction.nl', - 'required' => true, - ], - 'subject' => [ - 'type' => 'string', - 'description' => 'The subject of the email', - 'example' => 'Your weekly update', - 'required' => true, - ], - 'cc' => [ - 'type' => 'string', - 'description' => 'Carbon copy, email boxes that should receive a copy of this mail', - 'example' => 'archive@conduction.nl', - 'nullable' => true, - ], - 'bcc' => [ - 'type' => 'string', - 'description' => 'Blind carbon copy, people that should receive a copy without other recipient knowing', - 'example' => 'b.brother@conduction.nl', - 'nullable' => true, - ], - 'replyTo' => [ - 'type' => 'string', - 'description' => 'The address the receiver should reply to, only provide this if it differs from the sender address', - 'example' => 'no-reply@conduction.nl', - 'nullable' => true, - ], - 'priority' => [ - 'type' => 'int', - 'description' => 'An optional priority for the email', - 'nullable' => true, - ], - ], - ]; - } - - /** - * This function runs the email service plugin. - * - * @param array $data The data from the call - * @param array $configuration The configuration of the action - * - * @throws TransportExceptionInterface|LoaderError|RuntimeError|SyntaxError - * - * @return array - */ - public function run(array $data, array $configuration): array - { - return $this->emailService->emailHandler($data, $configuration); - } -} diff --git a/api/src/Service/EmailService.php b/api/src/Service/EmailService.php deleted file mode 100644 index 7625caabc..000000000 --- a/api/src/Service/EmailService.php +++ /dev/null @@ -1,133 +0,0 @@ -, Ruben van der Linde , Sarai Misidjan - * - * @license EUPL - * - * @category Service - */ -class EmailService -{ - private Environment $twig; - private array $data; - private array $configuration; - - public function __construct( - Environment $twig - ) { - $this->twig = $twig; - } - - /** - * Handles the sending of an email based on an event. - * - * @param array $data - * @param array $configuration - * - * @throws LoaderError|RuntimeError|SyntaxError|TransportExceptionInterface - * - * @return array - */ - public function EmailHandler(array $data, array $configuration): array - { - $this->data = $data; - $this->configuration = $configuration; - - $this->sendEmail(); - - return $data; - } - - /** - * Sends and email using an EmailTemplate with configuration for it. It is possible to use $object data in the email if configured right. - * - * @throws LoaderError - * @throws SyntaxError - * @throws TransportExceptionInterface - * - * @return bool - */ - private function sendEmail(): bool - { - // Create mailer with mailgun url - $transport = Transport::fromDsn($this->configuration['serviceDNS']); - $mailer = new Mailer($transport); - - // Ready the email template with configured variables - $variables = []; - - foreach ($this->configuration['variables'] as $key => $variable) { - // Response is the default used for creating emails after an /api endpoint has been called and returned a response. - if (isset($this->data['response']) === true && array_key_exists($variable, $this->data['response'])) { - $variables[$key] = $this->data['response'][$variable]; - } elseif (array_key_exists($variable, $this->data)) { - $variables[$key] = $this->data[$variable]; - } - } - - // Render the template - $html = $this->twig->createTemplate(base64_decode($this->configuration['template']))->render($variables); - $text = strip_tags(preg_replace('##i', "\n", $html), '\n'); - - // Lets allow the use of values from the object Created/Updated with {attributeName.attributeName} in the these^ strings. - $subject = $this->twig->createTemplate($this->configuration['subject'])->render($variables); - $receiver = $this->twig->createTemplate($this->configuration['receiver'])->render($variables); - $sender = $this->twig->createTemplate($this->configuration['sender'])->render($variables); - - // If we have no sender, set sender to receiver - if (!$sender) { - $sender = $receiver; - } - - // Create the email - $email = (new Email()) - ->from($sender) - ->to($receiver) - //->cc('cc@example.com') - //->bcc('bcc@example.com') - //->replyTo('fabien@example.com') - //->priority(Email::PRIORITY_HIGH) - ->subject($subject) - ->html($html) - ->text($text); - - // Then we can handle some optional configuration - if (array_key_exists('cc', $this->configuration)) { - $email->cc($this->configuration['cc']); - } - - if (array_key_exists('bcc', $this->configuration)) { - $email->bcc($this->configuration['bcc']); - } - - if (array_key_exists('replyTo', $this->configuration)) { - $email->replyTo($this->configuration['replyTo']); - } - - if (array_key_exists('priority', $this->configuration)) { - $email->priority($this->configuration['priority']); - } - - // todo: attachments - - // Send the email - /** @var Symfony\Component\Mailer\SentMessage $sentEmail */ - $mailer->send($email); - - return true; - } -} diff --git a/api/templates/emails/bisc/password-forgot-e-mail.html.twig b/api/templates/emails/bisc/password-forgot-e-mail.html.twig deleted file mode 100644 index a19fcbc74..000000000 --- a/api/templates/emails/bisc/password-forgot-e-mail.html.twig +++ /dev/null @@ -1,525 +0,0 @@ - - - - - - {{ subject }} - - - - - - - - - - - - - - - diff --git a/api/templates/emails/gateway/new-log-e-mail.html.twig b/api/templates/emails/gateway/new-log-e-mail.html.twig deleted file mode 100644 index 672102e69..000000000 --- a/api/templates/emails/gateway/new-log-e-mail.html.twig +++ /dev/null @@ -1,503 +0,0 @@ -{# todo: move this to an email plugin (see EmailService.php) #} - - - - - - {{ subject }} - - - - - - - - - - - - - - - diff --git a/api/templates/emails/kiss/new-review-e-mail.html.twig b/api/templates/emails/kiss/new-review-e-mail.html.twig deleted file mode 100644 index 2b3720643..000000000 --- a/api/templates/emails/kiss/new-review-e-mail.html.twig +++ /dev/null @@ -1,491 +0,0 @@ -{# todo: move this to an email plugin (see EmailService.php) #} - - - - - - {{ subject }} - - - - - - - - - - - - - - - From 36fbf45fa8dd1a3406e86db1be07a90a60d64ea6 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Thu, 7 Dec 2023 11:05:48 +0100 Subject: [PATCH 46/68] Set Gateway->configuration default value to verify=true --- api/src/Entity/Gateway.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/api/src/Entity/Gateway.php b/api/src/Entity/Gateway.php index 9ff175dff..723573295 100644 --- a/api/src/Entity/Gateway.php +++ b/api/src/Entity/Gateway.php @@ -633,7 +633,9 @@ class Gateway * * @ORM\Column(type="array", nullable=true) */ - private ?array $configuration = []; + private ?array $configuration = [ + "verify" => true + ]; /** * @var array|null The configuration for endpoints on this source, mostly mapping for now. @@ -812,7 +814,12 @@ public function fromSchema(array $schema): self array_key_exists('headers', $schema) ? $this->setHeaders($schema['headers']) : ''; array_key_exists('translationConfig', $schema) ? $this->setTranslationConfig($schema['translationConfig']) : ''; array_key_exists('type', $schema) ? $this->setType($schema['type']) : ''; - array_key_exists('configuration', $schema) ? $this->setConfiguration($schema['configuration']) : ''; + if (isset($schema['configuration']) === true) { + if (isset($schema['configuration']['verify']) === false) { + $schema['configuration']['verify'] = true; + } + $this->setConfiguration($schema['configuration']); + } array_key_exists('endpointsConfig', $schema) ? $this->setEndpointsConfig($schema['endpointsConfig']) : ''; array_key_exists('isEnabled', $schema) ? $this->setIsEnabled($schema['isEnabled']) : ''; From d7b0f40c666f56298f49f5ce8a3d3207a14ae067 Mon Sep 17 00:00:00 2001 From: Sarai Misidjan <55533449+smisidjan@users.noreply.github.com> Date: Tue, 12 Dec 2023 16:15:53 +0100 Subject: [PATCH 47/68] Create publiccode.yaml --- publiccode.yaml | 106 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 publiccode.yaml diff --git a/publiccode.yaml b/publiccode.yaml new file mode 100644 index 000000000..d6c940eab --- /dev/null +++ b/publiccode.yaml @@ -0,0 +1,106 @@ +publiccodeYmlVersion: '0.1' +name: commonground-gateway +applicationSuite: null +url: https://github.com/ConductionNL/commonground-gateway +landingURL: https://conductor-gateway.app +isBasedOn: null +softwareVersion: null +logo: https://avatars.githubusercontent.com/u/46676163?v=4 +monochromeLogo: null +platforms: + - web +releaseDate: 2021-08-2 +categories: + - collaboration + - it-development +developmentStatus: beta +softwareType: !!! +description: + en: + localisedName: CommonGateway + genericName: null + shortDescription: CommonGateway + longDescription: "The Common Gateway repository provides a quick Kubernetes wrapper for the Common Gateway Symfony Bundle. In other words, it doesn't aim to be its own code base but simply contains the files needed to create Kubernetes images and Helm installers for the core bundle." + documentation: https://conductor-gateway.app + apiDocumentation: null + features: [] + screenshots: [] + videos: [] + awards: [] + nl: + localisedName: CommonGateway + genericName: null + shortDescription: CommonGateway + longDescription: "De Common Gateway-repository biedt een snelle Kubernetes-wrapper voor de Common Gateway Symfony-bundel. Met andere woorden, het is niet bedoeld als een eigen codebasis, maar bevat eenvoudigweg de bestanden die nodig zijn om Kubernetes-images en Helm-installatieprogramma's voor de core bundle te maken." + documentation: https://conductor-gateway.app + apiDocumentation: null + features: [] + screenshots: [] + videos: [] + awards: [] +intendedaudience: + countries: + - nl + unsupportedCountries: [] + scope: + - government + - local-authorities +legal: + license: EUPL-1.2-or-later + mainCopyrightOwner: legal/authorsFile + repoOwner: CommonGateway + authorsFile: null +maintenance: + type: internal + contractors: + name: Conduction + until: 2035-01-01 + email: info\x64conduction.nl + website: https://www.conduction.nl + contact: + name: Ruben van der Linde + email: info\x64conduction.nl + phone: 085 303 6840 + affiliation: Conduction +localisation: + localisationReady: true + availableLanguages: + - en + - nl +dependsOn: + open: + - name: CoreBundle + versionMin: 1.2 + versionMax: 1.3 + version: 1.2.43 + optional: true + proprietary: + - name: Docker + versionMin: 24.0.6 + version: 24.0.6 + optional: false +roadmap: null +inputTypes: + - application/json + - application/xml + - application/x-yaml +outputTypes: + - application/json + - application/json+hal + - application/json+ld + - application/xml + - application/x-yaml +nl: + countryExtensionVersion: null + commonground: + intendedOrganisations: [] + installationType: null + layerType: null + gemma: + bedrijfsfuncties: [] + bedrijfsservices: [] + applicatiefunctie: null + model: null + upl: [] +downloadUrls: + - https://github.com/ConductionNL/commonground-gateway/archive/refs/heads/main.zip From 46a9a8716a1aba0152133aee67181a4b4f8f4b81 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Tue, 12 Dec 2023 16:39:38 +0100 Subject: [PATCH 48/68] Reference should always be unique --- api/src/Entity/Action.php | 3 +++ api/src/Entity/Application.php | 3 +++ api/src/Entity/CollectionEntity.php | 1 + api/src/Entity/Cronjob.php | 3 +++ api/src/Entity/Endpoint.php | 3 +++ api/src/Entity/Entity.php | 3 +++ api/src/Entity/Gateway.php | 1 + api/src/Entity/Mapping.php | 3 +++ api/src/Entity/Organization.php | 3 +++ api/src/Entity/SecurityGroup.php | 3 +++ api/src/Entity/Template.php | 6 +++++- api/src/Entity/User.php | 3 +++ 12 files changed, 34 insertions(+), 1 deletion(-) diff --git a/api/src/Entity/Action.php b/api/src/Entity/Action.php index 17c9e6cb4..c2431a087 100644 --- a/api/src/Entity/Action.php +++ b/api/src/Entity/Action.php @@ -15,6 +15,7 @@ use Gedmo\Mapping\Annotation as Gedmo; use Ramsey\Uuid\Uuid; use Ramsey\Uuid\UuidInterface; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; @@ -48,6 +49,8 @@ * "name": "exact", * "reference": "exact" * }) + * + * @UniqueEntity("reference") */ class Action { diff --git a/api/src/Entity/Application.php b/api/src/Entity/Application.php index 2afb2f2fd..bc355792b 100644 --- a/api/src/Entity/Application.php +++ b/api/src/Entity/Application.php @@ -16,6 +16,7 @@ use Gedmo\Mapping\Annotation as Gedmo; use Ramsey\Uuid\Uuid; use Ramsey\Uuid\UuidInterface; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Serializer\Annotation\MaxDepth; use Symfony\Component\Validator\Constraints as Assert; @@ -50,6 +51,8 @@ * "name": "exact", * "reference": "exact" * }) + * + * @UniqueEntity("reference") */ class Application { diff --git a/api/src/Entity/CollectionEntity.php b/api/src/Entity/CollectionEntity.php index 97823f723..8d97afdb0 100644 --- a/api/src/Entity/CollectionEntity.php +++ b/api/src/Entity/CollectionEntity.php @@ -51,6 +51,7 @@ * }) * * @UniqueEntity("name") + * @UniqueEntity("reference") */ class CollectionEntity { diff --git a/api/src/Entity/Cronjob.php b/api/src/Entity/Cronjob.php index 862ba6b03..c05d7a116 100644 --- a/api/src/Entity/Cronjob.php +++ b/api/src/Entity/Cronjob.php @@ -13,6 +13,7 @@ use Doctrine\ORM\Mapping as ORM; use Gedmo\Mapping\Annotation as Gedmo; use Ramsey\Uuid\UuidInterface; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; @@ -43,6 +44,8 @@ * "name": "exact", * "reference": "exact" * }) + * + * @UniqueEntity("reference") */ class Cronjob { diff --git a/api/src/Entity/Endpoint.php b/api/src/Entity/Endpoint.php index 567c865c4..fb4087242 100644 --- a/api/src/Entity/Endpoint.php +++ b/api/src/Entity/Endpoint.php @@ -18,6 +18,7 @@ use Gedmo\Mapping\Annotation as Gedmo; use Ramsey\Uuid\Uuid; use Ramsey\Uuid\UuidInterface; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Serializer\Annotation\MaxDepth; use Symfony\Component\Validator\Constraints as Assert; @@ -57,6 +58,8 @@ * "entities", * "proxy" * }) + * + * @UniqueEntity("reference") */ class Endpoint { diff --git a/api/src/Entity/Entity.php b/api/src/Entity/Entity.php index a3091daed..0826b08d7 100644 --- a/api/src/Entity/Entity.php +++ b/api/src/Entity/Entity.php @@ -19,6 +19,7 @@ use Gedmo\Mapping\Annotation as Gedmo; use phpDocumentor\Reflection\Types\This; use Ramsey\Uuid\UuidInterface; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Serializer\Annotation\MaxDepth; use Symfony\Component\Validator\Constraints as Assert; @@ -63,6 +64,8 @@ * "name": "exact", * "reference": "exact" * }) + * + * @UniqueEntity("reference") */ class Entity { diff --git a/api/src/Entity/Gateway.php b/api/src/Entity/Gateway.php index 723573295..9da2242f0 100644 --- a/api/src/Entity/Gateway.php +++ b/api/src/Entity/Gateway.php @@ -149,6 +149,7 @@ * }) * * @UniqueEntity("name") + * @UniqueEntity("reference") */ class Gateway { diff --git a/api/src/Entity/Mapping.php b/api/src/Entity/Mapping.php index 6187d7fd4..ebf53a2bc 100644 --- a/api/src/Entity/Mapping.php +++ b/api/src/Entity/Mapping.php @@ -15,6 +15,7 @@ use Doctrine\ORM\Mapping as ORM; use Gedmo\Mapping\Annotation as Gedmo; use Ramsey\Uuid\UuidInterface; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; @@ -44,6 +45,8 @@ * "name": "exact", * "reference": "exact" * }) + * + * @UniqueEntity("reference") */ class Mapping { diff --git a/api/src/Entity/Organization.php b/api/src/Entity/Organization.php index cd231688b..8fd89a1bb 100644 --- a/api/src/Entity/Organization.php +++ b/api/src/Entity/Organization.php @@ -16,6 +16,7 @@ use Gedmo\Mapping\Annotation as Gedmo; use Ramsey\Uuid\Uuid; use Ramsey\Uuid\UuidInterface; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Serializer\Annotation\MaxDepth; use Symfony\Component\Validator\Constraints as Assert; @@ -49,6 +50,8 @@ * "name": "exact", * "reference": "exact" * }) + * + * @UniqueEntity("reference") */ class Organization { diff --git a/api/src/Entity/SecurityGroup.php b/api/src/Entity/SecurityGroup.php index ded4d2b26..457b35f2f 100644 --- a/api/src/Entity/SecurityGroup.php +++ b/api/src/Entity/SecurityGroup.php @@ -16,6 +16,7 @@ use Gedmo\Mapping\Annotation as Gedmo; use Ramsey\Uuid\Uuid; use Ramsey\Uuid\UuidInterface; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Serializer\Annotation\MaxDepth; use Symfony\Component\Validator\Constraints as Assert; @@ -50,6 +51,8 @@ * "name": "exact", * "reference": "exact" * }) + * + * @UniqueEntity("reference") */ class SecurityGroup { diff --git a/api/src/Entity/Template.php b/api/src/Entity/Template.php index a400d7a1a..1041057f2 100644 --- a/api/src/Entity/Template.php +++ b/api/src/Entity/Template.php @@ -13,6 +13,7 @@ use Doctrine\ORM\Mapping as ORM; use Gedmo\Mapping\Annotation as Gedmo; use Ramsey\Uuid\UuidInterface; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; @@ -41,8 +42,11 @@ * @ApiFilter(DateFilter::class, strategy=DateFilter::EXCLUDE_NULL) * @ApiFilter(SearchFilter::class, properties={ * "supportedSchemas": "exact", - * "name": "exact" + * "name": "exact", + * "reference": "exact" * }) + * + * @UniqueEntity("reference") */ class Template { diff --git a/api/src/Entity/User.php b/api/src/Entity/User.php index 845610de9..1f26d4939 100644 --- a/api/src/Entity/User.php +++ b/api/src/Entity/User.php @@ -16,6 +16,7 @@ use Gedmo\Mapping\Annotation as Gedmo; use Ramsey\Uuid\Uuid; use Ramsey\Uuid\UuidInterface; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Serializer\Annotation\MaxDepth; @@ -50,6 +51,8 @@ * "reference": "exact" * }) * + * @UniqueEntity("reference") + * * @ORM\Table(name="`user`") */ class User implements PasswordAuthenticatedUserInterface From c22ac2b576e979e4563cad4eba3d0c05790d9cda Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Thu, 14 Dec 2023 10:41:17 +0100 Subject: [PATCH 49/68] Make contractors & contactS in publiccode arrays --- publiccode.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/publiccode.yaml b/publiccode.yaml index d6c940eab..bc02a26fa 100644 --- a/publiccode.yaml +++ b/publiccode.yaml @@ -53,15 +53,15 @@ legal: maintenance: type: internal contractors: - name: Conduction - until: 2035-01-01 - email: info\x64conduction.nl - website: https://www.conduction.nl - contact: - name: Ruben van der Linde - email: info\x64conduction.nl - phone: 085 303 6840 - affiliation: Conduction + - name: Conduction + until: 2035-01-01 + email: info\x64conduction.nl + website: https://www.conduction.nl + contacts: + - name: Ruben van der Linde + email: info\x64conduction.nl + phone: 085 303 6840 + affiliation: Conduction localisation: localisationReady: true availableLanguages: From e58ae6da56b4d2c199fe6df49347bf50dcf071b2 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Thu, 14 Dec 2023 11:09:24 +0100 Subject: [PATCH 50/68] Added layerType --- publiccode.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/publiccode.yaml b/publiccode.yaml index bc02a26fa..81def295e 100644 --- a/publiccode.yaml +++ b/publiccode.yaml @@ -95,7 +95,7 @@ nl: commonground: intendedOrganisations: [] installationType: null - layerType: null + layerType: service gemma: bedrijfsfuncties: [] bedrijfsservices: [] From e5d04813450bc0be0f1353953c274ba8034b10c1 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Thu, 14 Dec 2023 12:01:52 +0100 Subject: [PATCH 51/68] CoreBundle is required and update Core version --- publiccode.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/publiccode.yaml b/publiccode.yaml index 81def295e..0c6fcab70 100644 --- a/publiccode.yaml +++ b/publiccode.yaml @@ -72,8 +72,8 @@ dependsOn: - name: CoreBundle versionMin: 1.2 versionMax: 1.3 - version: 1.2.43 - optional: true + version: 1.2.47 + optional: false proprietary: - name: Docker versionMin: 24.0.6 From c35e0758d8396912dc5dd3e7eb0149deb214fe4d Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Tue, 19 Dec 2023 16:36:37 +0100 Subject: [PATCH 52/68] Added version = 0.0.1 as default value for all Entity --- api/migrations/Version20231219151046.php | 51 ++++++++++++++++++++++++ api/src/Entity/Action.php | 2 +- api/src/Entity/Application.php | 2 +- api/src/Entity/CollectionEntity.php | 2 +- api/src/Entity/Cronjob.php | 2 +- api/src/Entity/Endpoint.php | 2 +- api/src/Entity/Entity.php | 2 +- api/src/Entity/Gateway.php | 2 +- api/src/Entity/Mapping.php | 2 +- api/src/Entity/Organization.php | 2 +- api/src/Entity/SecurityGroup.php | 2 +- api/src/Entity/Template.php | 2 +- api/src/Entity/User.php | 2 +- 13 files changed, 63 insertions(+), 12 deletions(-) create mode 100644 api/migrations/Version20231219151046.php diff --git a/api/migrations/Version20231219151046.php b/api/migrations/Version20231219151046.php new file mode 100644 index 000000000..464b3d7e2 --- /dev/null +++ b/api/migrations/Version20231219151046.php @@ -0,0 +1,51 @@ +addSql('ALTER TABLE action ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE application ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE collection_entity ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE cronjob ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE endpoint ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE entity ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE gateway ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE mapping ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE organization ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE security_group ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE template ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE "user" ALTER version SET DEFAULT \'0.0.1\''); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE action ALTER version SET DEFAULT NULL'); + $this->addSql('ALTER TABLE application ALTER version SET DEFAULT NULL'); + $this->addSql('ALTER TABLE collection_entity ALTER version SET DEFAULT NULL'); + $this->addSql('ALTER TABLE cronjob ALTER version SET DEFAULT NULL'); + $this->addSql('ALTER TABLE endpoint ALTER version SET DEFAULT NULL'); + $this->addSql('ALTER TABLE entity ALTER version SET DEFAULT NULL'); + $this->addSql('ALTER TABLE gateway ALTER version SET DEFAULT NULL'); + $this->addSql('ALTER TABLE mapping ALTER version SET DEFAULT NULL'); + $this->addSql('ALTER TABLE organization ALTER version SET DEFAULT NULL'); + $this->addSql('ALTER TABLE security_group ALTER version SET DEFAULT NULL'); + $this->addSql('ALTER TABLE template ALTER version SET DEFAULT NULL'); + $this->addSql('ALTER TABLE "user" ALTER version SET DEFAULT NULL'); + } +} diff --git a/api/src/Entity/Action.php b/api/src/Entity/Action.php index c2431a087..da3fd2700 100644 --- a/api/src/Entity/Action.php +++ b/api/src/Entity/Action.php @@ -85,7 +85,7 @@ class Action * * @ORM\Column(type="string", length=255, nullable=true) */ - private $version; + private ?string $version = '0.0.1'; /** * @var string The name of the action diff --git a/api/src/Entity/Application.php b/api/src/Entity/Application.php index bc355792b..fbb91e3e8 100644 --- a/api/src/Entity/Application.php +++ b/api/src/Entity/Application.php @@ -113,7 +113,7 @@ class Application * * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) */ - private ?string $version = null; + private ?string $version = '0.0.1'; /** * The hosts that this applications uses, keep in ind that a host is exluding a trailing slach / and https:// ot http://. diff --git a/api/src/Entity/CollectionEntity.php b/api/src/Entity/CollectionEntity.php index 8d97afdb0..9c679cc64 100644 --- a/api/src/Entity/CollectionEntity.php +++ b/api/src/Entity/CollectionEntity.php @@ -110,7 +110,7 @@ class CollectionEntity * * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) */ - private ?string $version = null; + private ?string $version = '0.0.1'; /** * @var ?string The location where the OAS can be loaded from diff --git a/api/src/Entity/Cronjob.php b/api/src/Entity/Cronjob.php index c05d7a116..f572197c0 100644 --- a/api/src/Entity/Cronjob.php +++ b/api/src/Entity/Cronjob.php @@ -106,7 +106,7 @@ class Cronjob * * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) */ - private ?string $version = null; + private ?string $version = '0.0.1'; /** * @var string The crontab that determines the interval https://crontab.guru/ diff --git a/api/src/Entity/Endpoint.php b/api/src/Entity/Endpoint.php index fb4087242..1aa030d7a 100644 --- a/api/src/Entity/Endpoint.php +++ b/api/src/Entity/Endpoint.php @@ -333,7 +333,7 @@ class Endpoint * * @ORM\Column(type="string", length=255, nullable=true) */ - private ?string $version = null; + private ?string $version = '0.0.1'; /** * Constructor for creating an Endpoint. Use $entity to create an Endpoint for an Entity or diff --git a/api/src/Entity/Entity.php b/api/src/Entity/Entity.php index 0826b08d7..08127f0ea 100644 --- a/api/src/Entity/Entity.php +++ b/api/src/Entity/Entity.php @@ -423,7 +423,7 @@ class Entity * * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) */ - private ?string $version = null; + private ?string $version = '0.0.1'; //todo: do we want read/write groups here? /** diff --git a/api/src/Entity/Gateway.php b/api/src/Entity/Gateway.php index 9da2242f0..8266b89ee 100644 --- a/api/src/Entity/Gateway.php +++ b/api/src/Entity/Gateway.php @@ -226,7 +226,7 @@ class Gateway * * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) */ - private ?string $version = null; + private ?string $version = '0.0.1'; /** * @var string The location where the Gateway needs to be accessed diff --git a/api/src/Entity/Mapping.php b/api/src/Entity/Mapping.php index ebf53a2bc..04b57e0c5 100644 --- a/api/src/Entity/Mapping.php +++ b/api/src/Entity/Mapping.php @@ -81,7 +81,7 @@ class Mapping * * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) */ - private ?string $version = null; + private ?string $version = '0.0.1'; /** * @var string The name of the mapping diff --git a/api/src/Entity/Organization.php b/api/src/Entity/Organization.php index 8fd89a1bb..5bb6cff25 100644 --- a/api/src/Entity/Organization.php +++ b/api/src/Entity/Organization.php @@ -112,7 +112,7 @@ class Organization * * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) */ - private ?string $version = null; + private ?string $version = '0.0.1'; /** * @Groups({"read", "write"}) diff --git a/api/src/Entity/SecurityGroup.php b/api/src/Entity/SecurityGroup.php index 457b35f2f..a4587ed16 100644 --- a/api/src/Entity/SecurityGroup.php +++ b/api/src/Entity/SecurityGroup.php @@ -113,7 +113,7 @@ class SecurityGroup * * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) */ - private ?string $version = null; + private ?string $version = '0.0.1'; /** * @Groups({"read", "write"}) diff --git a/api/src/Entity/Template.php b/api/src/Entity/Template.php index 1041057f2..b6e6f58a7 100644 --- a/api/src/Entity/Template.php +++ b/api/src/Entity/Template.php @@ -140,7 +140,7 @@ class Template * * @ORM\Column(type="string", length=255, nullable=true) */ - private ?string $version = null; + private ?string $version = '0.0.1'; /** * @var Datetime|null The moment this resource was created diff --git a/api/src/Entity/User.php b/api/src/Entity/User.php index 1f26d4939..52de1bd39 100644 --- a/api/src/Entity/User.php +++ b/api/src/Entity/User.php @@ -114,7 +114,7 @@ class User implements PasswordAuthenticatedUserInterface * * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) */ - private ?string $version = null; + private ?string $version = '0.0.1'; /** * @Groups({"write"}) From ddbe62a9bb6c19ca4473a2f7ee0084df23410ec5 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Tue, 9 Jan 2024 13:11:34 +0100 Subject: [PATCH 53/68] Add trusted proxy config to correct location --- api/config/packages/framework.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/api/config/packages/framework.yaml b/api/config/packages/framework.yaml index 23c913ea2..7be339232 100644 --- a/api/config/packages/framework.yaml +++ b/api/config/packages/framework.yaml @@ -22,6 +22,12 @@ framework: app: cache.adapter.redis system: cache.adapter.redis default_redis_provider: "redis://%env(REDIS_HOST)%:%env(int:REDIS_PORT)%" + + + trusted_hosts: "%env(TRUSTED_HOSTS)%" + trusted_proxies: "%env(TRUSTED_PROXIES)%" + trusted_headers: ['x-forwarded-for', 'x-forwarded-host', 'x-forwarded-proto', 'x-forwarded-port', 'x-forwarded-prefix'] + parameters: samesite: none when@test: From a5612bb83a3ecddfcdfe443e0f3bed0c12cfa9eb Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Tue, 9 Jan 2024 13:56:45 +0100 Subject: [PATCH 54/68] Set version default to 0.0.0 instead of 0.0.1 --- api/migrations/Version20240109135300.php | 51 ++++++++++++++++++++++++ api/src/Entity/Action.php | 4 +- api/src/Entity/Application.php | 4 +- api/src/Entity/CollectionEntity.php | 4 +- api/src/Entity/Cronjob.php | 4 +- api/src/Entity/Endpoint.php | 4 +- api/src/Entity/Entity.php | 4 +- api/src/Entity/Gateway.php | 4 +- api/src/Entity/Mapping.php | 4 +- api/src/Entity/Organization.php | 4 +- api/src/Entity/SecurityGroup.php | 4 +- api/src/Entity/Template.php | 4 +- api/src/Entity/User.php | 4 +- 13 files changed, 75 insertions(+), 24 deletions(-) create mode 100644 api/migrations/Version20240109135300.php diff --git a/api/migrations/Version20240109135300.php b/api/migrations/Version20240109135300.php new file mode 100644 index 000000000..60a5facc6 --- /dev/null +++ b/api/migrations/Version20240109135300.php @@ -0,0 +1,51 @@ +addSql('ALTER TABLE action ALTER version SET DEFAULT \'0.0.0\''); + $this->addSql('ALTER TABLE application ALTER version SET DEFAULT \'0.0.0\''); + $this->addSql('ALTER TABLE collection_entity ALTER version SET DEFAULT \'0.0.0\''); + $this->addSql('ALTER TABLE cronjob ALTER version SET DEFAULT \'0.0.0\''); + $this->addSql('ALTER TABLE endpoint ALTER version SET DEFAULT \'0.0.0\''); + $this->addSql('ALTER TABLE entity ALTER version SET DEFAULT \'0.0.0\''); + $this->addSql('ALTER TABLE gateway ALTER version SET DEFAULT \'0.0.0\''); + $this->addSql('ALTER TABLE mapping ALTER version SET DEFAULT \'0.0.0\''); + $this->addSql('ALTER TABLE organization ALTER version SET DEFAULT \'0.0.0\''); + $this->addSql('ALTER TABLE security_group ALTER version SET DEFAULT \'0.0.0\''); + $this->addSql('ALTER TABLE template ALTER version SET DEFAULT \'0.0.0\''); + $this->addSql('ALTER TABLE "user" ALTER version SET DEFAULT \'0.0.0\''); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE action ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE application ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE collection_entity ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE cronjob ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE endpoint ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE entity ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE gateway ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE mapping ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE organization ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE security_group ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE template ALTER version SET DEFAULT \'0.0.1\''); + $this->addSql('ALTER TABLE "user" ALTER version SET DEFAULT \'0.0.1\''); + } +} diff --git a/api/src/Entity/Action.php b/api/src/Entity/Action.php index da3fd2700..a7122de85 100644 --- a/api/src/Entity/Action.php +++ b/api/src/Entity/Action.php @@ -83,9 +83,9 @@ class Action /** * @Groups({"read", "write"}) * - * @ORM\Column(type="string", length=255, nullable=true) + * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ - private ?string $version = '0.0.1'; + private string $version = '0.0.0'; /** * @var string The name of the action diff --git a/api/src/Entity/Application.php b/api/src/Entity/Application.php index fbb91e3e8..0698bd2ba 100644 --- a/api/src/Entity/Application.php +++ b/api/src/Entity/Application.php @@ -111,9 +111,9 @@ class Application /** * @Groups({"read", "write"}) * - * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) + * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ - private ?string $version = '0.0.1'; + private string $version = '0.0.0'; /** * The hosts that this applications uses, keep in ind that a host is exluding a trailing slach / and https:// ot http://. diff --git a/api/src/Entity/CollectionEntity.php b/api/src/Entity/CollectionEntity.php index 9c679cc64..45aa05f46 100644 --- a/api/src/Entity/CollectionEntity.php +++ b/api/src/Entity/CollectionEntity.php @@ -108,9 +108,9 @@ class CollectionEntity /** * @Groups({"read", "write"}) * - * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) + * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ - private ?string $version = '0.0.1'; + private string $version = '0.0.0'; /** * @var ?string The location where the OAS can be loaded from diff --git a/api/src/Entity/Cronjob.php b/api/src/Entity/Cronjob.php index f572197c0..97c2899b6 100644 --- a/api/src/Entity/Cronjob.php +++ b/api/src/Entity/Cronjob.php @@ -104,9 +104,9 @@ class Cronjob /** * @Groups({"read", "write"}) * - * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) + * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ - private ?string $version = '0.0.1'; + private string $version = '0.0.0'; /** * @var string The crontab that determines the interval https://crontab.guru/ diff --git a/api/src/Entity/Endpoint.php b/api/src/Entity/Endpoint.php index 1aa030d7a..898226eb1 100644 --- a/api/src/Entity/Endpoint.php +++ b/api/src/Entity/Endpoint.php @@ -331,9 +331,9 @@ class Endpoint /** * @Groups({"read", "write"}) * - * @ORM\Column(type="string", length=255, nullable=true) + * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ - private ?string $version = '0.0.1'; + private string $version = '0.0.0'; /** * Constructor for creating an Endpoint. Use $entity to create an Endpoint for an Entity or diff --git a/api/src/Entity/Entity.php b/api/src/Entity/Entity.php index 08127f0ea..eeabc188e 100644 --- a/api/src/Entity/Entity.php +++ b/api/src/Entity/Entity.php @@ -421,9 +421,9 @@ class Entity /** * @Groups({"read", "write"}) * - * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) + * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ - private ?string $version = '0.0.1'; + private string $version = '0.0.0'; //todo: do we want read/write groups here? /** diff --git a/api/src/Entity/Gateway.php b/api/src/Entity/Gateway.php index 8266b89ee..162a8389c 100644 --- a/api/src/Entity/Gateway.php +++ b/api/src/Entity/Gateway.php @@ -224,9 +224,9 @@ class Gateway /** * @Groups({"read", "write"}) * - * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) + * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ - private ?string $version = '0.0.1'; + private string $version = '0.0.0'; /** * @var string The location where the Gateway needs to be accessed diff --git a/api/src/Entity/Mapping.php b/api/src/Entity/Mapping.php index 04b57e0c5..bfd9412f1 100644 --- a/api/src/Entity/Mapping.php +++ b/api/src/Entity/Mapping.php @@ -79,9 +79,9 @@ class Mapping /** * @Groups({"read", "write"}) * - * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) + * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ - private ?string $version = '0.0.1'; + private string $version = '0.0.0'; /** * @var string The name of the mapping diff --git a/api/src/Entity/Organization.php b/api/src/Entity/Organization.php index 5bb6cff25..2d35d240b 100644 --- a/api/src/Entity/Organization.php +++ b/api/src/Entity/Organization.php @@ -110,9 +110,9 @@ class Organization /** * @Groups({"read", "write"}) * - * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) + * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ - private ?string $version = '0.0.1'; + private string $version = '0.0.0'; /** * @Groups({"read", "write"}) diff --git a/api/src/Entity/SecurityGroup.php b/api/src/Entity/SecurityGroup.php index a4587ed16..86d573b01 100644 --- a/api/src/Entity/SecurityGroup.php +++ b/api/src/Entity/SecurityGroup.php @@ -111,9 +111,9 @@ class SecurityGroup /** * @Groups({"read", "write"}) * - * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) + * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ - private ?string $version = '0.0.1'; + private string $version = '0.0.0'; /** * @Groups({"read", "write"}) diff --git a/api/src/Entity/Template.php b/api/src/Entity/Template.php index b6e6f58a7..cbb6d9029 100644 --- a/api/src/Entity/Template.php +++ b/api/src/Entity/Template.php @@ -138,9 +138,9 @@ class Template /** * @Groups({"read", "write"}) * - * @ORM\Column(type="string", length=255, nullable=true) + * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ - private ?string $version = '0.0.1'; + private string $version = '0.0.0'; /** * @var Datetime|null The moment this resource was created diff --git a/api/src/Entity/User.php b/api/src/Entity/User.php index 52de1bd39..9b3a1aa2d 100644 --- a/api/src/Entity/User.php +++ b/api/src/Entity/User.php @@ -112,9 +112,9 @@ class User implements PasswordAuthenticatedUserInterface /** * @Groups({"read", "write"}) * - * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) + * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ - private ?string $version = '0.0.1'; + private string $version = '0.0.0'; /** * @Groups({"write"}) From 9b52c30d6baa6c85a22c8dffeb8fbbcf3d1e98f3 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Thu, 11 Jan 2024 11:15:59 +0100 Subject: [PATCH 55/68] Assert null for version & reference --- api/src/Entity/Action.php | 6 +++++- api/src/Entity/Application.php | 4 ++++ api/src/Entity/CollectionEntity.php | 4 ++++ api/src/Entity/Cronjob.php | 4 ++++ api/src/Entity/Endpoint.php | 4 ++++ api/src/Entity/Entity.php | 4 ++++ api/src/Entity/Gateway.php | 4 ++++ api/src/Entity/Mapping.php | 4 ++++ api/src/Entity/Organization.php | 4 ++++ api/src/Entity/SecurityGroup.php | 4 ++++ api/src/Entity/Template.php | 4 ++++ api/src/Entity/User.php | 4 ++++ 12 files changed, 49 insertions(+), 1 deletion(-) diff --git a/api/src/Entity/Action.php b/api/src/Entity/Action.php index a7122de85..a5afd465f 100644 --- a/api/src/Entity/Action.php +++ b/api/src/Entity/Action.php @@ -76,13 +76,17 @@ class Action /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, nullable=true) */ - private $reference; + private ?string $reference; /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ private string $version = '0.0.0'; diff --git a/api/src/Entity/Application.php b/api/src/Entity/Application.php index 0698bd2ba..9f47a5d40 100644 --- a/api/src/Entity/Application.php +++ b/api/src/Entity/Application.php @@ -104,6 +104,8 @@ class Application /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) */ private ?string $reference = null; @@ -111,6 +113,8 @@ class Application /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ private string $version = '0.0.0'; diff --git a/api/src/Entity/CollectionEntity.php b/api/src/Entity/CollectionEntity.php index 45aa05f46..add253305 100644 --- a/api/src/Entity/CollectionEntity.php +++ b/api/src/Entity/CollectionEntity.php @@ -101,6 +101,8 @@ class CollectionEntity /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) */ private ?string $reference = null; @@ -108,6 +110,8 @@ class CollectionEntity /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ private string $version = '0.0.0'; diff --git a/api/src/Entity/Cronjob.php b/api/src/Entity/Cronjob.php index 97c2899b6..c1fce81ff 100644 --- a/api/src/Entity/Cronjob.php +++ b/api/src/Entity/Cronjob.php @@ -97,6 +97,8 @@ class Cronjob /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) */ private ?string $reference = null; @@ -104,6 +106,8 @@ class Cronjob /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ private string $version = '0.0.0'; diff --git a/api/src/Entity/Endpoint.php b/api/src/Entity/Endpoint.php index 898226eb1..a125fd3a3 100644 --- a/api/src/Entity/Endpoint.php +++ b/api/src/Entity/Endpoint.php @@ -324,6 +324,8 @@ class Endpoint /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, nullable=true) */ private ?string $reference = null; @@ -331,6 +333,8 @@ class Endpoint /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ private string $version = '0.0.0'; diff --git a/api/src/Entity/Entity.php b/api/src/Entity/Entity.php index eeabc188e..12b470e79 100644 --- a/api/src/Entity/Entity.php +++ b/api/src/Entity/Entity.php @@ -414,6 +414,8 @@ class Entity /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) */ private ?string $reference = null; @@ -421,6 +423,8 @@ class Entity /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ private string $version = '0.0.0'; diff --git a/api/src/Entity/Gateway.php b/api/src/Entity/Gateway.php index 162a8389c..a4467a61f 100644 --- a/api/src/Entity/Gateway.php +++ b/api/src/Entity/Gateway.php @@ -217,6 +217,8 @@ class Gateway /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) */ private ?string $reference = null; @@ -224,6 +226,8 @@ class Gateway /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ private string $version = '0.0.0'; diff --git a/api/src/Entity/Mapping.php b/api/src/Entity/Mapping.php index bfd9412f1..98cd88254 100644 --- a/api/src/Entity/Mapping.php +++ b/api/src/Entity/Mapping.php @@ -72,6 +72,8 @@ class Mapping /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) */ private ?string $reference = null; @@ -79,6 +81,8 @@ class Mapping /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ private string $version = '0.0.0'; diff --git a/api/src/Entity/Organization.php b/api/src/Entity/Organization.php index 2d35d240b..3483a3ef4 100644 --- a/api/src/Entity/Organization.php +++ b/api/src/Entity/Organization.php @@ -103,6 +103,8 @@ class Organization /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) */ private ?string $reference = null; @@ -110,6 +112,8 @@ class Organization /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ private string $version = '0.0.0'; diff --git a/api/src/Entity/SecurityGroup.php b/api/src/Entity/SecurityGroup.php index 86d573b01..4d0e72249 100644 --- a/api/src/Entity/SecurityGroup.php +++ b/api/src/Entity/SecurityGroup.php @@ -104,6 +104,8 @@ class SecurityGroup /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) */ private ?string $reference = null; @@ -111,6 +113,8 @@ class SecurityGroup /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ private string $version = '0.0.0'; diff --git a/api/src/Entity/Template.php b/api/src/Entity/Template.php index cbb6d9029..c50bd2c35 100644 --- a/api/src/Entity/Template.php +++ b/api/src/Entity/Template.php @@ -131,6 +131,8 @@ class Template /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, nullable=true) */ private ?string $reference = null; @@ -138,6 +140,8 @@ class Template /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ private string $version = '0.0.0'; diff --git a/api/src/Entity/User.php b/api/src/Entity/User.php index 9b3a1aa2d..c576217e1 100644 --- a/api/src/Entity/User.php +++ b/api/src/Entity/User.php @@ -105,6 +105,8 @@ class User implements PasswordAuthenticatedUserInterface /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, nullable=true, options={"default": null}) */ private ?string $reference = null; @@ -112,6 +114,8 @@ class User implements PasswordAuthenticatedUserInterface /** * @Groups({"read", "write"}) * + * @Assert\NotNull + * * @ORM\Column(type="string", length=255, options={"default": "0.0.0"}) */ private string $version = '0.0.0'; From bf48bc8d78619816dc3bd8dabbb6352c327dbede Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Mon, 15 Jan 2024 11:02:01 +0100 Subject: [PATCH 56/68] Added @var for the property loggingConfig in Gateway.php --- api/src/Entity/Gateway.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/src/Entity/Gateway.php b/api/src/Entity/Gateway.php index a4467a61f..e3251a52c 100644 --- a/api/src/Entity/Gateway.php +++ b/api/src/Entity/Gateway.php @@ -564,8 +564,7 @@ class Gateway private ?string $documentation = null; /** - * Configuration for logging, when an api call is made on the source we can log some information for this call. - * With this array you can enable/disable what will be logged. + * @var array Configuration for logging, when an api call is made on the source we can log some information for this call. With this array you can enable/disable what will be logged. * * @Assert\NotNull * From c3a9f0297e1300e8f2939ff4583fea40bec3f186 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Mon, 15 Jan 2024 15:56:36 +0100 Subject: [PATCH 57/68] Fix the use of limit and page query param on /admin endpoints --- api/src/Entity/Action.php | 2 +- api/src/Entity/ActionHandler.php | 2 +- api/src/Entity/Application.php | 2 +- api/src/Entity/Attribute.php | 5 +++-- api/src/Entity/AuditTrail.php | 3 ++- api/src/Entity/CollectionEntity.php | 3 ++- api/src/Entity/Contract.php | 3 ++- api/src/Entity/Cronjob.php | 3 ++- api/src/Entity/DashboardCard.php | 3 ++- api/src/Entity/Endpoint.php | 3 ++- api/src/Entity/Entity.php | 3 ++- api/src/Entity/Mapping.php | 2 +- api/src/Entity/ObjectEntity.php | 2 +- api/src/Entity/Organization.php | 3 ++- api/src/Entity/Property.php | 2 +- api/src/Entity/Purpose.php | 3 ++- api/src/Entity/SecurityGroup.php | 2 +- api/src/Entity/Synchronization.php | 3 ++- api/src/Entity/Template.php | 3 ++- api/src/Entity/Translation.php | 3 ++- api/src/Entity/Unread.php | 2 +- api/src/Entity/User.php | 3 ++- api/src/Entity/Value.php | 3 ++- 23 files changed, 39 insertions(+), 24 deletions(-) diff --git a/api/src/Entity/Action.php b/api/src/Entity/Action.php index a5afd465f..64f8c0a41 100644 --- a/api/src/Entity/Action.php +++ b/api/src/Entity/Action.php @@ -33,7 +33,7 @@ * collectionOperations={ * "get"={"path"="/admin/actions"}, * "post"={"path"="/admin/actions"} - * }) + * } * ) * * @ORM\HasLifecycleCallbacks diff --git a/api/src/Entity/ActionHandler.php b/api/src/Entity/ActionHandler.php index 7c531ba14..50d3a301e 100644 --- a/api/src/Entity/ActionHandler.php +++ b/api/src/Entity/ActionHandler.php @@ -28,7 +28,7 @@ * collectionOperations={ * "get"={"path"="/admin/actionHandlers"}, * "post"={"path"="/admin/actionHandlers"} - * }) + * } * ) * * @ORM\Entity(repositoryClass=ActionHandlerRepository::class) diff --git a/api/src/Entity/Application.php b/api/src/Entity/Application.php index 9f47a5d40..3d3a52989 100644 --- a/api/src/Entity/Application.php +++ b/api/src/Entity/Application.php @@ -35,7 +35,7 @@ * collectionOperations={ * "get"={"path"="/admin/applications"}, * "post"={"path"="/admin/applications"} - * }) + * } * ) * * @ORM\HasLifecycleCallbacks diff --git a/api/src/Entity/Attribute.php b/api/src/Entity/Attribute.php index 1bcd2d559..8d899ac4d 100644 --- a/api/src/Entity/Attribute.php +++ b/api/src/Entity/Attribute.php @@ -24,7 +24,7 @@ use Symfony\Component\Validator\Constraints as Assert; /** - * An possible attribute on an Entity. + * A possible attribute on an Entity. * * @category Entity * @@ -39,7 +39,8 @@ * collectionOperations={ * "get"={"path"="/admin/attributes"}, * "post"={"path"="/admin/attributes"} - * }) + * } + * ) * * @ORM\Entity(repositoryClass=AttributeRepository::class) * diff --git a/api/src/Entity/AuditTrail.php b/api/src/Entity/AuditTrail.php index 17e0f1517..313136ae5 100644 --- a/api/src/Entity/AuditTrail.php +++ b/api/src/Entity/AuditTrail.php @@ -33,7 +33,8 @@ * "get"={"path"="/admin/audit_trails"}, * "post"={"path"="/admin/audit_trails"} * }, - * attributes={"order"={"creationDate": "DESC"}}) + * attributes={"order"={"creationDate": "DESC"}} + * ) * * @ORM\Entity(repositoryClass=AuditTrailRepository::class) * diff --git a/api/src/Entity/CollectionEntity.php b/api/src/Entity/CollectionEntity.php index add253305..c34e1a7eb 100644 --- a/api/src/Entity/CollectionEntity.php +++ b/api/src/Entity/CollectionEntity.php @@ -36,7 +36,8 @@ * collectionOperations={ * "get"={"path"="/admin/collections"}, * "post"={"path"="/admin/collections"} - * }) + * } + * ) * * @ORM\Entity(repositoryClass=CollectionEntityRepository::class) * diff --git a/api/src/Entity/Contract.php b/api/src/Entity/Contract.php index 5184262c4..f32d81283 100644 --- a/api/src/Entity/Contract.php +++ b/api/src/Entity/Contract.php @@ -33,7 +33,8 @@ * collectionOperations={ * "get"={"path"="/admin/contracts"}, * "post"={"path"="/admin/contracts"} - * }) + * } + * ) * * @ORM\Entity(repositoryClass=ContractRepository::class) * diff --git a/api/src/Entity/Cronjob.php b/api/src/Entity/Cronjob.php index c1fce81ff..cc220c227 100644 --- a/api/src/Entity/Cronjob.php +++ b/api/src/Entity/Cronjob.php @@ -31,7 +31,8 @@ * collectionOperations={ * "get"={"path"="/admin/cronjobs"}, * "post"={"path"="/admin/cronjobs"} - * }) + * } + * ) * * @ORM\Entity(repositoryClass=CronjobRepository::class) * diff --git a/api/src/Entity/DashboardCard.php b/api/src/Entity/DashboardCard.php index 777cb6c4e..8e78148ce 100644 --- a/api/src/Entity/DashboardCard.php +++ b/api/src/Entity/DashboardCard.php @@ -27,7 +27,8 @@ * collectionOperations={ * "get"={"path"="/admin/dashboardCards"}, * "post"={"path"="/admin/dashboardCards"} - * }) + * } + * ) * * @ORM\Entity(repositoryClass=DashboardCardRepository::class) * diff --git a/api/src/Entity/Endpoint.php b/api/src/Entity/Endpoint.php index a125fd3a3..1eb7c2772 100644 --- a/api/src/Entity/Endpoint.php +++ b/api/src/Entity/Endpoint.php @@ -37,7 +37,8 @@ * collectionOperations={ * "get"={"path"="/admin/endpoints"}, * "post"={"path"="/admin/endpoints"} - * }) + * } + * ) * * @ORM\Entity(repositoryClass="App\Repository\EndpointRepository") * diff --git a/api/src/Entity/Entity.php b/api/src/Entity/Entity.php index 12b470e79..2185064a3 100644 --- a/api/src/Entity/Entity.php +++ b/api/src/Entity/Entity.php @@ -51,7 +51,8 @@ * "description"="Deletes all objects that belong to this schema" * } * }, - * }) + * } + * ) * * @ORM\Entity(repositoryClass="App\Repository\EntityRepository") * diff --git a/api/src/Entity/Mapping.php b/api/src/Entity/Mapping.php index 98cd88254..5e7a2a952 100644 --- a/api/src/Entity/Mapping.php +++ b/api/src/Entity/Mapping.php @@ -31,7 +31,7 @@ * collectionOperations={ * "get"={"path"="/admin/mappings"}, * "post"={"path"="/admin/mappings"} - * }) + * } * ) * * @ORM\Entity(repositoryClass=MappingRepository::class) diff --git a/api/src/Entity/ObjectEntity.php b/api/src/Entity/ObjectEntity.php index fb6eb4e43..f659dde1a 100644 --- a/api/src/Entity/ObjectEntity.php +++ b/api/src/Entity/ObjectEntity.php @@ -19,7 +19,7 @@ use function Symfony\Component\Translation\t; /** - * An (data) object that resides within the datalayer of the gateway. + * A (data) object that resides within the datalayer of the gateway. * * @category Entity * diff --git a/api/src/Entity/Organization.php b/api/src/Entity/Organization.php index 3483a3ef4..7043caac4 100644 --- a/api/src/Entity/Organization.php +++ b/api/src/Entity/Organization.php @@ -35,7 +35,8 @@ * collectionOperations={ * "get"={"path"="/admin/organizations"}, * "post"={"path"="/admin/organizations"} - * }) + * } + * ) * * @ORM\HasLifecycleCallbacks * diff --git a/api/src/Entity/Property.php b/api/src/Entity/Property.php index f50b8da07..eaeadb576 100644 --- a/api/src/Entity/Property.php +++ b/api/src/Entity/Property.php @@ -28,7 +28,7 @@ * collectionOperations={ * "get"={"path"="/admin/properties"}, * "post"={"path"="/admin/properties"} - * }) + * } * ) * * @ORM\Entity(repositoryClass=PropertyRepository::class) diff --git a/api/src/Entity/Purpose.php b/api/src/Entity/Purpose.php index 23c26c033..71af172b7 100644 --- a/api/src/Entity/Purpose.php +++ b/api/src/Entity/Purpose.php @@ -28,7 +28,8 @@ * collectionOperations={ * "get"={"path"="/admin/purposes"}, * "post"={"path"="/admin/purposes"} - * }) + * } + * ) * * @ORM\Entity(repositoryClass=PurposeRepository::class) * diff --git a/api/src/Entity/SecurityGroup.php b/api/src/Entity/SecurityGroup.php index 4d0e72249..0203a7cab 100644 --- a/api/src/Entity/SecurityGroup.php +++ b/api/src/Entity/SecurityGroup.php @@ -35,7 +35,7 @@ * collectionOperations={ * "get"={"path"="/admin/user_groups"}, * "post"={"path"="/admin/user_groups"} - * }) + * } * ) * * @ORM\HasLifecycleCallbacks diff --git a/api/src/Entity/Synchronization.php b/api/src/Entity/Synchronization.php index 678594050..6fc019997 100644 --- a/api/src/Entity/Synchronization.php +++ b/api/src/Entity/Synchronization.php @@ -32,7 +32,8 @@ * collectionOperations={ * "get"={"path"="/admin/synchronizations"}, * "post"={"path"="/admin/synchronizations"} - * }) + * } + * ) * * @ORM\Entity(repositoryClass=SynchronizationRepository::class) * diff --git a/api/src/Entity/Template.php b/api/src/Entity/Template.php index c50bd2c35..4fa1edd19 100644 --- a/api/src/Entity/Template.php +++ b/api/src/Entity/Template.php @@ -31,7 +31,8 @@ * collectionOperations={ * "get"={"path"="/admin/templates"}, * "post"={"path"="/admin/templates"} - * })) + * } + * ) * * @ORM\Entity(repositoryClass=TemplateRepository::class) * diff --git a/api/src/Entity/Translation.php b/api/src/Entity/Translation.php index c5f7561a0..731ade771 100644 --- a/api/src/Entity/Translation.php +++ b/api/src/Entity/Translation.php @@ -35,7 +35,8 @@ * "path"="/admin/table_names" * }, * "post"={"path"="/admin/translations"} - * }) + * } + * ) * * @ORM\Entity(repositoryClass=TranslationRepository::class) * diff --git a/api/src/Entity/Unread.php b/api/src/Entity/Unread.php index 836f1a822..2f96e9a2b 100644 --- a/api/src/Entity/Unread.php +++ b/api/src/Entity/Unread.php @@ -30,7 +30,7 @@ * collectionOperations={ * "get"={"path"="/admin/unreads"}, * "post"={"path"="/admin/unreads"} - * }) + * } * ) * * @ORM\Entity(repositoryClass="App\Repository\UnreadRepository") diff --git a/api/src/Entity/User.php b/api/src/Entity/User.php index c576217e1..7d2797e63 100644 --- a/api/src/Entity/User.php +++ b/api/src/Entity/User.php @@ -36,7 +36,8 @@ * collectionOperations={ * "get"={"path"="/admin/users"}, * "post"={"path"="/admin/users"} - * }) + * } + * ) * * @ORM\HasLifecycleCallbacks * diff --git a/api/src/Entity/Value.php b/api/src/Entity/Value.php index 91154a87d..89a6c4cae 100644 --- a/api/src/Entity/Value.php +++ b/api/src/Entity/Value.php @@ -40,7 +40,8 @@ * collectionOperations={ * "get"={"path"="/admin/values"}, * "post"={"path"="/admin/values"} - * }) + * } + * ) * * @ORM\Entity(repositoryClass="App\Repository\ValueRepository") * From 8906eb2770ffbb4fec9de71d6e1ddab7b1464d46 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Tue, 16 Jan 2024 14:46:20 +0100 Subject: [PATCH 58/68] Hotfix for BRK too large objects This hotfix should be reverted ASAP and superceded by a possibility to overrule maxDepth per attribute. --- api/src/Entity/ObjectEntity.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/api/src/Entity/ObjectEntity.php b/api/src/Entity/ObjectEntity.php index f659dde1a..dc83993f0 100644 --- a/api/src/Entity/ObjectEntity.php +++ b/api/src/Entity/ObjectEntity.php @@ -1184,6 +1184,12 @@ public function toArray(array $configuration = []): array $config['maxDepth'] = $attribute->getObject()->getMaxDepth() + $config['level']; } $config['level'] = $config['level'] + 1; + + //TODO: This is a very hacky solution that has to be changed back ASAP + if ($attribute->getObject() === $this->getEntity()) { + $config['maxDepth'] = $config['level']; + } + $objectToArray = $object->toArray($config); // Check if we want an embedded array From aaf6dda8317419aed621461026ebbf90f0367c5b Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Tue, 16 Jan 2024 15:37:25 +0100 Subject: [PATCH 59/68] Also run this on multiple --- api/src/Entity/ObjectEntity.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/api/src/Entity/ObjectEntity.php b/api/src/Entity/ObjectEntity.php index dc83993f0..8f9095279 100644 --- a/api/src/Entity/ObjectEntity.php +++ b/api/src/Entity/ObjectEntity.php @@ -1257,6 +1257,11 @@ public function toArray(array $configuration = []): array $config['maxDepth'] = $attribute->getObject()->getMaxDepth() + $config['level']; } $config['level'] = $config['level'] + 1; + + //TODO: This is a very hacky solution that has to be changed back ASAP + if ($attribute->getObject() === $this->getEntity()) { + $config['maxDepth'] = $config['level']; + } $objectToArray = $object->toArray($config); // Check if we want an embedded array From def809e80101e9565a037eba153f31dcbc69a397 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Mon, 22 Jan 2024 13:00:09 +0100 Subject: [PATCH 60/68] Comment out some deprecated code to fix old sync --- api/src/Repository/ActionRepository.php | 2 ++ api/src/Service/EavService.php | 3 ++- api/src/Service/FunctionService.php | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/api/src/Repository/ActionRepository.php b/api/src/Repository/ActionRepository.php index 2a883d429..7d7e5746a 100644 --- a/api/src/Repository/ActionRepository.php +++ b/api/src/Repository/ActionRepository.php @@ -26,6 +26,8 @@ public function __construct(ManagerRegistry $registry) */ public function findByListens(string $listen): array { + // Todo: maybe add actions to MongoDB as well, so we can use better queries for this: + // Todo: %$listen% will sometimes find actions it shouldn't find, example: action listening to test.test.123 will be found if action test.test is thrown. $query = $this->createQueryBuilder('a') ->andWhere('a.listens LIKE :listen') ->setParameter('listen', "%$listen%") diff --git a/api/src/Service/EavService.php b/api/src/Service/EavService.php index c5e68a4a1..1a5dad5a9 100644 --- a/api/src/Service/EavService.php +++ b/api/src/Service/EavService.php @@ -1291,7 +1291,8 @@ public function handleDelete(ObjectEntity $object, ArrayCollection $maxDepth = n } // Remove this object from cache - $this->functionService->removeResultFromCache($object); + //todo: deprecated function +// $this->functionService->removeResultFromCache($object); $this->em->remove($object); $this->em->flush(); diff --git a/api/src/Service/FunctionService.php b/api/src/Service/FunctionService.php index 4ac569b10..d36564fc2 100644 --- a/api/src/Service/FunctionService.php +++ b/api/src/Service/FunctionService.php @@ -196,6 +196,7 @@ public function isResource($url) * @throws InvalidArgumentException * * @return bool + * @deprecated */ public function removeResultFromCache(ObjectEntity $objectEntity, ?SymfonyStyle $io = null): bool { @@ -225,6 +226,7 @@ public function removeResultFromCache(ObjectEntity $objectEntity, ?SymfonyStyle * @throws InvalidArgumentException * * @return void + * @deprecated */ private function removeParentResultsFromCache(ObjectEntity $objectEntity, ?SymfonyStyle $io) { @@ -248,6 +250,7 @@ private function removeParentResultsFromCache(ObjectEntity $objectEntity, ?Symfo * @throws InvalidArgumentException * * @return void + * @deprecated */ private function removeChildResultsFromCache(ObjectEntity $objectEntity, ?SymfonyStyle $io) { From de2382e25b78a4fee6726bd90214b2feb6b163a5 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Tue, 23 Jan 2024 11:40:54 +0100 Subject: [PATCH 61/68] No longer use url for sourceId in notifications / syncs when not needed --- api/src/Service/SynchronizationService.php | 93 ++++++++++++---------- 1 file changed, 52 insertions(+), 41 deletions(-) diff --git a/api/src/Service/SynchronizationService.php b/api/src/Service/SynchronizationService.php index b70a62ee0..4cfbd8048 100644 --- a/api/src/Service/SynchronizationService.php +++ b/api/src/Service/SynchronizationService.php @@ -14,6 +14,7 @@ use App\Exception\GatewayException; use CommonGateway\CoreBundle\Service\CallService; use CommonGateway\CoreBundle\Service\FileSystemHandleService; +use CommonGateway\CoreBundle\Service\GatewayResourceService; use CommonGateway\CoreBundle\Service\MappingService; use DateInterval; use DateTime; @@ -23,6 +24,7 @@ use Monolog\Logger; use Psr\Cache\CacheException; use Psr\Cache\InvalidArgumentException; +use Ramsey\Uuid\Uuid; use Safe\Exceptions\UrlException; use Symfony\Component\Console\Helper\TableSeparator; use Symfony\Component\Console\Style\SymfonyStyle; @@ -60,6 +62,7 @@ class SynchronizationService private SymfonyStyle $io; private Environment $twig; private MappingService $mappingService; + private GatewayResourceService $resourceService; private ActionEvent $event; private EventDispatcherInterface $eventDispatcher; @@ -83,6 +86,7 @@ class SynchronizationService * @param EventDispatcherInterface $eventDispatcher * @param MappingService $mappingService * @param FileSystemHandleService $fileSystemService + * @param GatewayResourceService $resourceService */ public function __construct( CallService $callService, @@ -98,7 +102,8 @@ public function __construct( Environment $twig, EventDispatcherInterface $eventDispatcher, MappingService $mappingService, - FileSystemHandleService $fileSystemService + FileSystemHandleService $fileSystemService, + GatewayResourceService $resourceService ) { $this->callService = $callService; $this->entityManager = $entityManager; @@ -119,6 +124,7 @@ public function __construct( $this->logger = new Logger('installation'); $this->mappingService = $mappingService; $this->fileSystemService = $fileSystemService; + $this->resourceService = $resourceService; } /** @@ -670,13 +676,15 @@ public function getSingleFromSource(Synchronization $synchronization): ?array $url = \Safe\parse_url($synchronization->getSource()->getLocation()); + $endpoint = str_contains('http', $synchronization->getSourceId()) === true ? $synchronization->getEndpoint() : $synchronization->getEndpoint().'/'.$synchronization->getSourceId(); + if ($url['scheme'] === 'http' || $url['scheme'] === 'https') { // Get object form source with callservice try { $this->logger->info("getSingleFromSource with Synchronization->sourceId = {$synchronization->getSourceId()}"); $response = $this->callService->call( $callServiceConfig['source'], - $synchronization->getEndpoint() ?? $callServiceConfig['endpoint'], + isset($this->configuration['location']) === true ? $callServiceConfig['endpoint'] : $endpoint, $callServiceConfig['method'] ?? 'GET', [ 'body' => '', @@ -694,7 +702,7 @@ public function getSingleFromSource(Synchronization $synchronization): ?array $result = $this->callService->decodeResponse($callServiceConfig['source'], $response); } elseif ($url['scheme'] === 'ftp') { // This only works if a file data equals a single Object(Entity). Or if the mapping on the Source or Synchronization results in data for just a single Object. - $result = $this->fileSystemService->call($synchronization->getSource(), $synchronization->getEndpoint() ?? $callServiceConfig['endpoint']); + $result = $this->fileSystemService->call($synchronization->getSource(), isset($this->configuration['location']) === true ? $callServiceConfig['endpoint'] : $endpoint); } $dot = new Dot($result); // The place where we can find the id field when looping through the list of objects, from $result root, by object (dot notation) @@ -711,15 +719,20 @@ public function getSingleFromSource(Synchronization $synchronization): ?array /** * Finds a synchronization object if it exists for the current object in the source, or creates one if it doesn't exist. * - * @param Source $source The source that is requested - * @param Entity $entity The entity that is requested - * @param string $sourceId The id of the object in the source + * @param Source $source The source that is requested + * @param Entity $entity The entity that is requested + * @param string $sourceId The id of the object in the source + * @param string|null $endpoint The endpoint of the synchronization. * * @return Synchronization|null A synchronization object related to the object in the source */ - public function findSyncBySource(Source $source, Entity $entity, string $sourceId): ?Synchronization + public function findSyncBySource(Source $source, Entity $entity, string $sourceId, ?string $endpoint = null): ?Synchronization { - $synchronization = $this->entityManager->getRepository('App:Synchronization')->findOneBy(['gateway' => $source, 'entity' => $entity, 'sourceId' => $sourceId]); + $criteria = ['gateway' => $source, 'entity' => $entity, 'sourceId' => $sourceId]; + if (empty($endpoint) === false) { + $criteria['endpoint'] = $endpoint; + } + $synchronization = $this->entityManager->getRepository('App:Synchronization')->findOneBy($criteria); if ($synchronization instanceof Synchronization) { if (isset($this->io)) { @@ -731,6 +744,7 @@ public function findSyncBySource(Source $source, Entity $entity, string $sourceI } $synchronization = new Synchronization($source, $entity); + $synchronization->setEndpoint($endpoint); $synchronization->setSourceId($sourceId); $this->entityManager->persist($synchronization); // We flush later @@ -1326,15 +1340,18 @@ private function syncToSource(Synchronization $synchronization, bool $existsInSo // $objectArray = $this->objectEntityService->checkGetObjectExceptions($data, $object, [], ['all' => true], 'application/ld+json'); // todo: maybe move this to foreach in getAllFromSource() (nice to have) - $callServiceConfig = $this->getCallServiceConfig($synchronization->getSource(), null, $objectArray); + $callServiceConfig = $this->getCallServiceConfig($synchronization->getSource(), $existsInSource ? $synchronization->getSourceId() : null, $objectArray); $objectArray = $this->mapOutput($objectArray); + $normalEndpoint = $synchronization->getEndpoint().($existsInSource ? '/'.$synchronization->getSourceId() : ''); + $endpoint = str_contains('http', $synchronization->getSourceId()) === true ? $synchronization->getEndpoint() : $normalEndpoint; + $objectString = $this->getObjectString($objectArray); try { $result = $this->callService->call( $callServiceConfig['source'], - $synchronization->getEndpoint() ?? $callServiceConfig['endpoint'], + isset($this->configuration['location']) === true ? $callServiceConfig['endpoint'] : $endpoint, $callServiceConfig['method'] ?? ($existsInSource ? 'PUT' : 'POST'), [ 'body' => $objectString, @@ -1469,41 +1486,35 @@ private function syncThroughComparing(Synchronization $synchronization): Synchro */ public function aquireObject(string $url, Entity $entity): ?ObjectEntity { - // 1. Get the domain from the url - $parse = \Safe\parse_url($url); - $location = $parse['scheme'].'://'.$parse['host']; - - // 2.c Try to establish a source for the domain - $source = $this->entityManager->getRepository('App:Gateway')->findOneBy(['location'=>$location]); - - // 2.b The source might be on a path e.g. /v1 so if whe cant find a source let try to cycle - if ($source instanceof Source === false && isset($parse['path']) === true) { - foreach (explode('/', $parse['path']) as $pathPart) { - if ($pathPart !== '') { - $location = $location.'/'.$pathPart; - } - $source = $this->entityManager->getRepository('App:Gateway')->findOneBy(['location'=>$location]); - if ($source !== null) { - break; - } - } - } - if ($source instanceof Source === false) { - return null; - } - - // 3 If we have a source we can establish an endpoint. - $endpoint = str_replace($location, '', $url); - - // 4 Create sync - $synchronization = new Synchronization($source, $entity); - $synchronization->setSourceId($url); - $synchronization->setEndpoint($endpoint); + $source = $this->resourceService->findSourceForUrl($url, 'conduction-nl/commonground-gateway', $endpoint); + $sourceId = $this->getSourceId($endpoint, $url); - $this->entityManager->persist($synchronization); + $synchronization = $this->findSyncBySource($source, $entity, $sourceId, $endpoint); $this->synchronize($synchronization); return $synchronization->getObject(); } + + /** + * A function best used after resourceService->findSourceForUrl and/or before $this->findSyncBySource. + * This function will get the uuid / int id from the end of an endpoint. This is the sourceId for a Synchronization. + * + * @param string $endpoint The endpoint to get the SourceId from. + * @param string|null $url The url used as back-up for SourceId if no proper SourceId can be found. + * + * @return string|null The sourceId, will be equal to $url if end part of the endpoint isn't an uuid or integer. + */ + public function getSourceId(string &$endpoint, ?string $url = null): ?string + { + $explodedEndpoint = explode('/', $endpoint); + $sourceId = end($explodedEndpoint); + if (Uuid::isValid($sourceId) === true || is_int((int) $sourceId) === true) { + $endpoint = str_replace("/$sourceId", '', $endpoint); + } else { + $sourceId = $url; + } + + return $sourceId; + } } From 1ffc507873927d15cf0bb065b1bb532ba2d325e4 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Tue, 23 Jan 2024 11:52:48 +0100 Subject: [PATCH 62/68] Small fix & code cleanup for setting Synchronization Endpoint --- api/src/Service/SynchronizationService.php | 23 ++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/api/src/Service/SynchronizationService.php b/api/src/Service/SynchronizationService.php index 4cfbd8048..cc1fb43e0 100644 --- a/api/src/Service/SynchronizationService.php +++ b/api/src/Service/SynchronizationService.php @@ -309,7 +309,7 @@ private function loopThroughCollectionResults(array $results, array $config): ar array_key_exists('object', $this->configuration['apiSource']['location']) && $result = $dot->get($this->configuration['apiSource']['location']['object'], $result); // Lets grab the sync object, if we don't find an existing one, this will create a new one: - $synchronization = $this->findSyncBySource($config['source'], $config['entity'], $id); + $synchronization = $this->findSyncBySource($config['source'], $config['entity'], $id, $this->configuration['location'] ?? null); // todo: Another search function for sync object. If no sync object is found, look for matching properties... // todo: ...in $result and an ObjectEntity in db. And then create sync for an ObjectEntity if we find one this way. (nice to have) // Other option to find a sync object, currently not used: @@ -676,7 +676,13 @@ public function getSingleFromSource(Synchronization $synchronization): ?array $url = \Safe\parse_url($synchronization->getSource()->getLocation()); - $endpoint = str_contains('http', $synchronization->getSourceId()) === true ? $synchronization->getEndpoint() : $synchronization->getEndpoint().'/'.$synchronization->getSourceId(); + if (isset($this->configuration['location']) === true) { + $endpoint = $this->configuration['location']; + $synchronization->setEndpoint($endpoint); + } else { + $normalEndpoint = $synchronization->getEndpoint().'/'.$synchronization->getSourceId(); + $endpoint = str_contains('http', $synchronization->getSourceId()) === true ? $synchronization->getEndpoint() : $normalEndpoint; + } if ($url['scheme'] === 'http' || $url['scheme'] === 'https') { // Get object form source with callservice @@ -684,7 +690,7 @@ public function getSingleFromSource(Synchronization $synchronization): ?array $this->logger->info("getSingleFromSource with Synchronization->sourceId = {$synchronization->getSourceId()}"); $response = $this->callService->call( $callServiceConfig['source'], - isset($this->configuration['location']) === true ? $callServiceConfig['endpoint'] : $endpoint, + $endpoint, $callServiceConfig['method'] ?? 'GET', [ 'body' => '', @@ -1343,15 +1349,20 @@ private function syncToSource(Synchronization $synchronization, bool $existsInSo $callServiceConfig = $this->getCallServiceConfig($synchronization->getSource(), $existsInSource ? $synchronization->getSourceId() : null, $objectArray); $objectArray = $this->mapOutput($objectArray); - $normalEndpoint = $synchronization->getEndpoint().($existsInSource ? '/'.$synchronization->getSourceId() : ''); - $endpoint = str_contains('http', $synchronization->getSourceId()) === true ? $synchronization->getEndpoint() : $normalEndpoint; + if (isset($this->configuration['location']) === true) { + $endpoint = $this->configuration['location']; + $synchronization->setEndpoint($endpoint); + } else { + $normalEndpoint = $synchronization->getEndpoint().($existsInSource ? '/'.$synchronization->getSourceId() : ''); + $endpoint = str_contains('http', $synchronization->getSourceId()) === true ? $synchronization->getEndpoint() : $normalEndpoint; + } $objectString = $this->getObjectString($objectArray); try { $result = $this->callService->call( $callServiceConfig['source'], - isset($this->configuration['location']) === true ? $callServiceConfig['endpoint'] : $endpoint, + $endpoint, $callServiceConfig['method'] ?? ($existsInSource ? 'PUT' : 'POST'), [ 'body' => $objectString, From a1e183c455823412c8852c25314cc71198dd2877 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Tue, 23 Jan 2024 12:30:51 +0100 Subject: [PATCH 63/68] Remove else statements and 1 line if statements --- api/src/Service/SynchronizationService.php | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/api/src/Service/SynchronizationService.php b/api/src/Service/SynchronizationService.php index cc1fb43e0..481f56ede 100644 --- a/api/src/Service/SynchronizationService.php +++ b/api/src/Service/SynchronizationService.php @@ -676,12 +676,13 @@ public function getSingleFromSource(Synchronization $synchronization): ?array $url = \Safe\parse_url($synchronization->getSource()->getLocation()); + $endpoint = $synchronization->getEndpoint().'/'.$synchronization->getSourceId(); + if (str_contains('http', $synchronization->getSourceId()) === true) { + $endpoint = $synchronization->getEndpoint(); + } if (isset($this->configuration['location']) === true) { $endpoint = $this->configuration['location']; $synchronization->setEndpoint($endpoint); - } else { - $normalEndpoint = $synchronization->getEndpoint().'/'.$synchronization->getSourceId(); - $endpoint = str_contains('http', $synchronization->getSourceId()) === true ? $synchronization->getEndpoint() : $normalEndpoint; } if ($url['scheme'] === 'http' || $url['scheme'] === 'https') { @@ -1349,12 +1350,16 @@ private function syncToSource(Synchronization $synchronization, bool $existsInSo $callServiceConfig = $this->getCallServiceConfig($synchronization->getSource(), $existsInSource ? $synchronization->getSourceId() : null, $objectArray); $objectArray = $this->mapOutput($objectArray); + $endpoint = $synchronization->getEndpoint(); + if ($existsInSource === true) { + $endpoint = $endpoint.'/'.$synchronization->getSourceId(); + } + if (str_contains('http', $synchronization->getSourceId()) === true) { + $endpoint = $synchronization->getEndpoint(); + } if (isset($this->configuration['location']) === true) { $endpoint = $this->configuration['location']; $synchronization->setEndpoint($endpoint); - } else { - $normalEndpoint = $synchronization->getEndpoint().($existsInSource ? '/'.$synchronization->getSourceId() : ''); - $endpoint = str_contains('http', $synchronization->getSourceId()) === true ? $synchronization->getEndpoint() : $normalEndpoint; } $objectString = $this->getObjectString($objectArray); From 5a9d88e9bb8793d5a5011a5f5cd3b4b0561f5bb2 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Fri, 26 Jan 2024 14:12:47 +0100 Subject: [PATCH 64/68] Check for an APIKEY_USER when using a api-key auth --- api/src/Security/ApiKeyAuthenticator.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/api/src/Security/ApiKeyAuthenticator.php b/api/src/Security/ApiKeyAuthenticator.php index c7af2224d..74690f81e 100644 --- a/api/src/Security/ApiKeyAuthenticator.php +++ b/api/src/Security/ApiKeyAuthenticator.php @@ -153,6 +153,13 @@ public function authenticate(Request $request): PassportInterface try { $user = $application->getOrganization()->getUsers()[0]; + + $users = array_filter($application->getOrganization()->getUsers(), function (User $user) { + return $user->getName() === 'APIKEY_USER'; + }); + if (empty($users[0]) === false) { + $user = $users[0]; + } } catch (\Exception $exception) { throw new AuthenticationException('An invalid User is configured for this ApiKey'); } From 815bc4d80f89729b3a6c9954745e8a9f82864393 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Fri, 26 Jan 2024 14:52:45 +0100 Subject: [PATCH 65/68] Create default APIKEY_USER during initialize command --- api/src/Command/InitializationCommand.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/api/src/Command/InitializationCommand.php b/api/src/Command/InitializationCommand.php index 6d4ac71ba..f3245572c 100644 --- a/api/src/Command/InitializationCommand.php +++ b/api/src/Command/InitializationCommand.php @@ -249,7 +249,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int // Handling users $io->section('Looking for an user'); if (!$user = $this->entityManager->getRepository('App:User')->findOneBy([])) { - $io->info('No User found, creating a new one'); + $io->info('No User found, creating a default and APIKEY one'); + $user = new User(); $user->setName('Default User'); $user->setReference('https://docs.commongateway.nl/user/default.user.json'); @@ -261,6 +262,18 @@ protected function execute(InputInterface $input, OutputInterface $output): int $user->setOrganization($organization); $this->entityManager->persist($user); + + $apikeyUser = new User(); + $apikeyUser->setName('APIKEY_USER'); + $apikeyUser->setReference('https://docs.commongateway.nl/user/default.apikey.user.json'); + $apikeyUser->setDescription('Created during auto configuration'); + $apikeyUser->setEmail('apikey@test.com'); + $apikeyUser->setPassword($this->hasher->hashPassword($apikeyUser, '!ChangeMe!')); + $apikeyUser->addSecurityGroup($securityGroupAdmin); + $apikeyUser->addApplication($application); + $apikeyUser->setOrganization($organization); + + $this->entityManager->persist($apikeyUser); } else { $io->info('User found, continuing....'); } From 52fcc6659bc0815a6ebef844c85cfe83112523a6 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Fri, 26 Jan 2024 15:26:03 +0100 Subject: [PATCH 66/68] User collection->filter instead of array_filter --- api/src/Security/ApiKeyAuthenticator.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/api/src/Security/ApiKeyAuthenticator.php b/api/src/Security/ApiKeyAuthenticator.php index 74690f81e..4bf905852 100644 --- a/api/src/Security/ApiKeyAuthenticator.php +++ b/api/src/Security/ApiKeyAuthenticator.php @@ -152,20 +152,22 @@ public function authenticate(Request $request): PassportInterface } try { - $user = $application->getOrganization()->getUsers()[0]; + $user = $application->getOrganization()->getUsers()->first(); - $users = array_filter($application->getOrganization()->getUsers(), function (User $user) { + $userCollection = $application->getOrganization()->getUsers(); + $users = $userCollection->filter(function (User $user) { return $user->getName() === 'APIKEY_USER'; }); - if (empty($users[0]) === false) { - $user = $users[0]; + + if (count($users) > 0) { + $user = $users->first(); } } catch (\Exception $exception) { - throw new AuthenticationException('An invalid User is configured for this ApiKey'); + throw new AuthenticationException('An invalid User (or no user) is configured for this ApiKey'); } if ($user instanceof User === false) { - throw new AuthenticationException('An invalid User is configured for this ApiKey'); + throw new AuthenticationException('An invalid User (or no user) is configured for this ApiKey'); } // Set apiKey Application id in session From dc8138d5e79ea1de08f0db5cfc1653b7283952d3 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Fri, 26 Jan 2024 16:24:28 +0100 Subject: [PATCH 67/68] Added array values function for twig --- api/src/Twig/MappingExtension.php | 1 + api/src/Twig/MappingRuntime.php | 33 +++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/api/src/Twig/MappingExtension.php b/api/src/Twig/MappingExtension.php index d2df0d88d..6308c6c99 100644 --- a/api/src/Twig/MappingExtension.php +++ b/api/src/Twig/MappingExtension.php @@ -12,6 +12,7 @@ public function getFunctions() return [ new TwigFunction('map', [MappingRuntime::class, 'map']), new TwigFunction('dotToObject', [MappingRuntime::class, 'dotToArray']), + new TwigFunction('arrayValues', [MappingRuntime::class, 'arrayValues']), ]; } } diff --git a/api/src/Twig/MappingRuntime.php b/api/src/Twig/MappingRuntime.php index 850832aa2..87b385b24 100644 --- a/api/src/Twig/MappingRuntime.php +++ b/api/src/Twig/MappingRuntime.php @@ -18,6 +18,20 @@ public function __construct(MappingService $mappingService, EntityManagerInterfa $this->mappingService = $mappingService; } + /** + * Uses CoreBundle MappingService to map data. + * If $list is set to true you could use the key 'listInput' in the $data array to pass along the list of items to map. + * This makes it possible to also pass along other key+value pairs to use in mapping besides just one array of items to map. + * + * @param string $mappingString The reference of a Mapping object. + * @param array $data The data to map. Or one list of items to map. + * @param bool $list False by default, if set to true mapping will be done for each item in the $data array. + * + * @return array The mapped result. + * + * @throws \Twig\Error\LoaderError + * @throws \Twig\Error\SyntaxError + */ public function map(string $mappingString, array $data, bool $list = false): array { $mapping = $this->entityManager->getRepository('App:Mapping')->findOneBy(['reference' => $mappingString]); @@ -27,10 +41,29 @@ public function map(string $mappingString, array $data, bool $list = false): arr return $value; } + /** + * Turns given array into an Adbar\Dot (dot notation). + * + * @param array $array The array to turn into a dot array. + * + * @return array The dot aray. + */ public function dotToArray(array $array): array { $dotArray = new Dot($array, true); return $dotArray->all(); } + + /** + * Makes it possible to use the php function array_values in twig. + * + * @param array $array The array to use array_values on. + * + * @return array The updated array. + */ + public function arrayValues(array $array): array + { + return array_values($array); + } } From ab35dd51757b9eefa641c6a6ce04a91a96215561 Mon Sep 17 00:00:00 2001 From: Wilco Louwerse Date: Fri, 26 Jan 2024 16:59:03 +0100 Subject: [PATCH 68/68] Fix test endpoint for mapping --- api/src/Controller/ConvenienceController.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/api/src/Controller/ConvenienceController.php b/api/src/Controller/ConvenienceController.php index 32f7f46c6..f4d642310 100755 --- a/api/src/Controller/ConvenienceController.php +++ b/api/src/Controller/ConvenienceController.php @@ -40,7 +40,6 @@ class ConvenienceController extends AbstractController private ActionSubscriber $actionSubscriber; private ObjectEntityService $objectEntityService; private MappingService $mappingService; - private Environment $twig; public function __construct( EntityManagerInterface $entityManager, @@ -50,7 +49,7 @@ public function __construct( HandlerService $handlerService, ActionSubscriber $actionSubscriber, ObjectEntityService $objectEntityService, - Environment $twig + MappingService $mappingService ) { $this->entityManager = $entityManager; $this->serializer = $serializer; @@ -60,8 +59,7 @@ public function __construct( $this->handlerService = $handlerService; $this->actionSubscriber = $actionSubscriber; $this->objectEntityService = $objectEntityService; - $this->twig = $twig; - $this->mappingService = new MappingService($twig); + $this->mappingService = $mappingService; } /**