Browse Source

V8-4096 add messenger and review file exchange between apis

Olivier Massot 2 years ago
parent
commit
1892966184

+ 1 - 0
composer.json

@@ -45,6 +45,7 @@
         "symfony/mailer": "6.2.*",
         "symfony/mailer": "6.2.*",
         "symfony/mercure": "^0.6.1",
         "symfony/mercure": "^0.6.1",
         "symfony/mercure-bundle": "^0.3.4",
         "symfony/mercure-bundle": "^0.3.4",
+        "symfony/messenger": "6.2.*",
         "symfony/monolog-bundle": "^3.7",
         "symfony/monolog-bundle": "^3.7",
         "symfony/polyfill-intl-icu": "^1.21",
         "symfony/polyfill-intl-icu": "^1.21",
         "symfony/polyfill-intl-messageformatter": "^1.24",
         "symfony/polyfill-intl-messageformatter": "^1.24",

+ 5 - 0
readme.md

@@ -14,6 +14,11 @@ Pour consommer les signaux messengers, lancer :
 
 
     php bin/console messenger:consume async
     php bin/console messenger:consume async
 
 
+En cas d'erreur lié à l'absence de la table messenger_messages, lancer d'abord :
+
+    php bin/console messenger:setup-transports
+
+
 > Voir: <https://symfony.com/doc/5.4/messenger.html#consuming-messages-running-the-worker>
 > Voir: <https://symfony.com/doc/5.4/messenger.html#consuming-messages-running-the-worker>
 
 
 ## Chercher les erreurs avec phpstan
 ## Chercher les erreurs avec phpstan

+ 8 - 7
src/DataPersister/Export/LicenceCmf/ExportRequestDataPersister.php

@@ -33,7 +33,7 @@ class ExportRequestDataPersister implements ContextAwareDataPersisterInterface
      * @throws Exception
      * @throws Exception
      * @noinspection PhpParameterNameChangedDuringInheritanceInspection
      * @noinspection PhpParameterNameChangedDuringInheritanceInspection
      */
      */
-    public function persist($exportRequest, array $context = []): File
+    public function persist($exportRequest, array $context = []): ExportRequest
     {
     {
         /** @var Access $access */
         /** @var Access $access */
         $access = $this->security->getUser();
         $access = $this->security->getUser();
@@ -46,14 +46,15 @@ class ExportRequestDataPersister implements ContextAwareDataPersisterInterface
         $exportRequest->setFileId($file->getId());
         $exportRequest->setFileId($file->getId());
 
 
         if (!$exportRequest->isAsync()) {
         if (!$exportRequest->isAsync()) {
-            return $exporter->export($exportRequest);
+            $exporter->export($exportRequest);
+        } else {
+            // Send the export request to Messenger (@see App\Message\Handler\ExportHandler)
+            $this->messageBus->dispatch(
+                new Export($exportRequest)
+            );
         }
         }
 
 
-        // Send the export request to Messenger (@see App\Message\Handler\ExportHandler)
-        $this->messageBus->dispatch(
-            new Export($exportRequest)
-        );
-        return $file;
+        return $exportRequest;
     }
     }
 
 
     public function remove($data, array $context = []): void
     public function remove($data, array $context = []): void

+ 29 - 1
src/DataProvider/Core/DownloadRequestDataProvider.php

@@ -19,6 +19,15 @@ use Symfony\Bundle\SecurityBundle\Security;
  */
  */
 final class DownloadRequestDataProvider implements ItemDataProviderInterface, RestrictedDataProviderInterface
 final class DownloadRequestDataProvider implements ItemDataProviderInterface, RestrictedDataProviderInterface
 {
 {
+    // Internal ips allowed to access private files without being authenticated
+    const INTERNAL_IPS = [
+        '/^127\.0\.0\.[0-1]$/',
+        '/^localhost$/',
+        '/^10\.8\.0\.\d{1,3}$/', // VPN
+        '/^141\.94\.117\.[33-61]$/',   // internal public ips
+        '/^172\.20\.\d{1,3}\.\d{1,3}$/',  // docker
+    ];
+
     public function __construct(
     public function __construct(
         private FileRepository $fileRepository,
         private FileRepository $fileRepository,
         private FileManager $fileManager,
         private FileManager $fileManager,
@@ -31,6 +40,21 @@ final class DownloadRequestDataProvider implements ItemDataProviderInterface, Re
         return DownloadRequest::class === $resourceClass;
         return DownloadRequest::class === $resourceClass;
     }
     }
 
 
+    /**
+     * Returns true if the client Ip is allowed to access restricted content without auth
+     *
+     * @param string $clientIp
+     * @return bool
+     */
+    private function isInternalIp(string $clientIp): bool
+    {
+        foreach (self::INTERNAL_IPS as $ipRule) {
+            if (preg_match($ipRule, $clientIp)) {
+                return true;
+            }
+        }
+        return false;
+    }
     public function getItem(string $resourceClass, $id, string $operationName = null, array $context = []): Response | RedirectResponse
     public function getItem(string $resourceClass, $id, string $operationName = null, array $context = []): Response | RedirectResponse
     {
     {
         $file = $this->fileRepository->find($id);
         $file = $this->fileRepository->find($id);
@@ -41,8 +65,12 @@ final class DownloadRequestDataProvider implements ItemDataProviderInterface, Re
             throw new \RuntimeException("File " . $id . " has " . $file->getStatus() . " status; abort.");
             throw new \RuntimeException("File " . $id . " has " . $file->getStatus() . " status; abort.");
         }
         }
 
 
+        // This is a request from an authorized IP
+        $clientIp = $_SERVER['REMOTE_ADDR'];
+        $internalIp = $this->isInternalIp($clientIp);
+
         // Read the file
         // Read the file
-        $token = $this->security->getToken();
+        $token = $internalIp ? null : $this->security->getToken();
         $content = $this->fileManager->read($file, $token);
         $content = $this->fileManager->read($file, $token);
 
 
         // Build the response and attach the file to it
         // Build the response and attach the file to it

+ 3 - 1
src/Entity/Core/File.php

@@ -553,9 +553,11 @@ class File
     /**
     /**
      * @param string|null $host
      * @param string|null $host
      */
      */
-    public function setHost(?string $host): void
+    public function setHost(?string $host): self
     {
     {
         $this->host = $host;
         $this->host = $host;
+
+        return $this;
     }
     }
 
 
     public function getOrganizationLogos(): Collection
     public function getOrganizationLogos(): Collection

+ 6 - 9
src/Service/ApiLegacy/ApiLegacyRequestService.php

@@ -36,13 +36,6 @@ class ApiLegacyRequestService extends ApiRequestService
     ): ResponseInterface {
     ): ResponseInterface {
 
 
         $token = $this->security->getToken();
         $token = $this->security->getToken();
-        if ($token === null || $token instanceof NullToken || $token->getUser() === null) {
-            throw new HttpException(500, 'Request error : Invalid security token');
-        }
-
-        if (!array_key_exists('BEARER', $_REQUEST)) {
-            throw new HttpException(500, 'Request error : missing BEARER');
-        }
 
 
         $headers = [
         $headers = [
             'Accept' => '*/*',
             'Accept' => '*/*',
@@ -51,6 +44,7 @@ class ApiLegacyRequestService extends ApiRequestService
             'Content-Type' => 'application/ld+json',
             'Content-Type' => 'application/ld+json',
         ];
         ];
 
 
+        $jwt = null;
         if ($token instanceof SwitchUserToken) {
         if ($token instanceof SwitchUserToken) {
             $originalUser = $token->getOriginalToken()->getUser();
             $originalUser = $token->getOriginalToken()->getUser();
             if ($originalUser === null) {
             if ($originalUser === null) {
@@ -60,12 +54,15 @@ class ApiLegacyRequestService extends ApiRequestService
             $jwt = $this->jwtManager->create($originalUser->getPerson());
             $jwt = $this->jwtManager->create($originalUser->getPerson());
             $headers['x-accessid'] = $originalUser->getId();
             $headers['x-accessid'] = $originalUser->getId();
             $headers['x-switch-access'] = $token->getUser()->getId();
             $headers['x-switch-access'] = $token->getUser()->getId();
-        } else {
+        }
+        elseif ($token !== null && !($token instanceof NullToken) && $token->getUser() !== null) {
             $jwt = $this->jwtManager->create($token->getUser()->getPerson());
             $jwt = $this->jwtManager->create($token->getUser()->getPerson());
             $headers['x-accessid'] = $token->getUser()->getId();
             $headers['x-accessid'] = $token->getUser()->getId();
         }
         }
 
 
-        $headers['authorization'] = 'BEARER ' . $jwt;
+        if ($jwt !== null) {
+            $headers['authorization'] = 'BEARER ' . $jwt;
+        }
         $options['headers'] = array_merge($options['headers'] ?? [], $headers);
         $options['headers'] = array_merge($options['headers'] ?? [], $headers);
 
 
         return parent::request($method, $url, $parameters, $options);
         return parent::request($method, $url, $parameters, $options);

+ 1 - 4
src/Service/File/Storage/ApiLegacyStorage.php

@@ -40,10 +40,7 @@ class ApiLegacyStorage implements FileStorageInterface
      */
      */
     public function read(File $file): string
     public function read(File $file): string
     {
     {
-        $url = 'api/files/' . $file->getId() .'/download';
-        if ($this->originalAccessId !== null) {
-            $url = 'api/' . $this->originalAccessId . '/'. $this->asAccessId . '/files/' . $file->getId() .'/download';
-        }
+        $url = '_internal/secure/files/' . $file->getId();
         return $this->apiLegacyRequestService->getContent($url);
         return $this->apiLegacyRequestService->getContent($url);
     }
     }
 }
 }