diff --git a/api/composer.json b/api/composer.json index fd8c5bc0e..2ed21942b 100644 --- a/api/composer.json +++ b/api/composer.json @@ -1,7 +1,7 @@ { "type": "project", "license": "proprietary", - "minimum-stability": "dev", + "minimum-stability": "stable", "prefer-stable": true, "require": { "php": "^7.4.0 || ^8.1.0 || ^8.2.0", diff --git a/api/src/Controller/LoginController.php b/api/src/Controller/LoginController.php index 25ab685c7..e88dd1b7c 100644 --- a/api/src/Controller/LoginController.php +++ b/api/src/Controller/LoginController.php @@ -48,8 +48,8 @@ public function MeAction(Request $request) 'last_name' => $this->getUser()->getLastName(), 'name' => $this->getUser()->getName(), 'email' => $this->getUser()->getEmail(), - '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 + 'person' => $this->getUser()->getPerson(), // Get person ObjectEntity (->Entity with function = person) by id + 'organization' => $this->getUser()->getOrganization(), // Get organization ObjectEntity (->Entity with function = organization) by id ]; $result = json_encode($result); } else { diff --git a/api/src/Logger/SessionDataProcessor.php b/api/src/Logger/SessionDataProcessor.php index ae9014c48..4ce307ff1 100644 --- a/api/src/Logger/SessionDataProcessor.php +++ b/api/src/Logger/SessionDataProcessor.php @@ -2,6 +2,10 @@ namespace App\Logger; +use App\Event\ActionEvent; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Exception\JsonException; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Session\SessionInterface; @@ -10,30 +14,179 @@ class SessionDataProcessor private SessionInterface $session; private RequestStack $requestStack; - public function __construct(SessionInterface $session, RequestStack $requestStack) + /** + * @var EventDispatcherInterface The event dispatcher. + */ + private EventDispatcherInterface $eventDispatcher; + + /** + * @var EntityManagerInterface The entity manager. + */ + private EntityManagerInterface $entityManager; + + /** + * @param SessionInterface $session + * @param RequestStack $requestStack + * @param EventDispatcherInterface $eventDispatcher + * @param EntityManagerInterface $entityManager + */ + 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 + /** + * Update the context with data from the session and the request stack. + * + * @param array $record The log record to update the context for. + * + * @return array The updated context. + */ + 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') : ''; + $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') === 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') : ''; + + // 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() !== 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. + if (in_array($record['level_name'], ['ERROR', 'CRITICAL', 'ALERT', 'EMERGENCY']) === true) { + $context = $this->addErrorContext($context, $record['level_name']); + } + + return $context; + } + + /** + * Update the context for Source call logs. + * + * @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 updateSourceCallContext(array $context, string $levelName): array + { + 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) { + $context['sourceCall']['callQuery'] = json_encode($context['sourceCall']['callQuery']); + if ($context['sourceCall']['callQuery'] === "[]") { + $context['sourceCall']['callQuery'] = ''; + } + } + + if (isset($context['sourceCall']['callBody']) === true && strlen($context['sourceCall']['callBody']) > $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).'...'; + } + + 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() !== 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() !== null ? $this->requestStack->getMainRequest()->getContentType() : ''; + + // 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 (JsonException $exception) { + $context['body'] = ''; + } + $context['crude_body'] = $this->requestStack->getMainRequest() ? $this->requestStack->getMainRequest()->getContent() : ''; + } + + 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 { - $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() : ''; + if ($this->entityManager->getConnection()->isConnected() === true + && in_array( + $this->entityManager->getConnection()->getDatabase(), + $this->entityManager->getConnection()->getSchemaManager()->listDatabases() + ) === true + && $this->entityManager->getConnection()->getSchemaManager()->tablesExist('action') === true + && in_array($record['level_name'], ['ERROR', 'CRITICAL', 'ALERT', 'EMERGENCY']) === true + ){ + $event = new ActionEvent('commongateway.action.event', $record, 'commongateway.log.create'); + + $this->eventDispatcher->dispatch($event, 'commongateway.action.event'); + + $record = $event->getData(); + } 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); + return $this->dispatchLogCreateAction($record); + } }