diff --git a/application/Espo/Core/Console/Commands/AuthTokenCheck.php b/application/Espo/Core/Console/Commands/AuthTokenCheck.php index a24da10679..38a9e3ae60 100644 --- a/application/Espo/Core/Console/Commands/AuthTokenCheck.php +++ b/application/Espo/Core/Console/Commands/AuthTokenCheck.php @@ -47,9 +47,12 @@ class AuthTokenCheck implements Command public function run(Params $params, IO $io): void { - $token = $params->getArgument(0); + $io->setExitStatus(1); - if (empty($token)) { + $token = $params->getArgument(0); + $userId = $params->getArgument(1); + + if (!$token) { return; } @@ -67,14 +70,16 @@ class AuthTokenCheck implements Command return; } - $userId = $authToken->getUserId(); + if ($userId && $authToken->getUserId() !== $userId) { + return; + } $user = $this->entityManager - ->getRDBRepository(User::ENTITY_TYPE) + ->getRDBRepositoryByClass(User::class) ->select(Attribute::ID) ->where([ - Attribute::ID => $userId, - 'isActive' => true, + Attribute::ID => $authToken->getUserId(), + User::ATTR_IS_ACTIVE => true, ]) ->findOne(); @@ -83,5 +88,6 @@ class AuthTokenCheck implements Command } $io->write($user->getId()); + $io->setExitStatus(0); } } diff --git a/application/Espo/Core/WebSocket/Pusher.php b/application/Espo/Core/WebSocket/Pusher.php index ec1c1b0f22..d681869964 100644 --- a/application/Espo/Core/WebSocket/Pusher.php +++ b/application/Espo/Core/WebSocket/Pusher.php @@ -37,6 +37,7 @@ use Ratchet\Wamp\Topic; use Ratchet\Wamp\WampConnection; use Ratchet\Wamp\WampServerInterface; +use React\ChildProcess\Process; use Symfony\Component\Process\PhpExecutableFinder; use Exception; @@ -122,30 +123,29 @@ class Pusher implements WampServerInterface if ($checkCommand) { $checkCommand = $checkCommand . ' 2>&1'; - $checkResult = exec($checkCommand, $o, $exitCode); + $process = new Process($checkCommand); + $process->start(); - if ($checkResult !== 'true' || $exitCode !== 0) { - if ($this->isDebugMode) { - $this->log("$connectionId: check access failed for topic $topicId for user $userId"); + $process->on('exit', function ($exitCode) use ($topic, $connectionId, $topicId, $userId) { + if ($exitCode !== 0) { + if ($this->isDebugMode) { + $this->log("$connectionId: check access failed for topic $topicId for user $userId"); + } + + return; } - return; - } + if ($this->isDebugMode) { + $this->log("$connectionId: check access succeed for topic $topicId for user $userId"); + } - if ($this->isDebugMode) { - $this->log("$connectionId: check access succeed for topic $topicId for user $userId"); - } + $this->addTopicForUser($connectionId, $topic, $userId); + }); + + return; } - if (!in_array($topicId, $this->connectionIdTopicIdListMap[$connectionId])) { - if ($this->isDebugMode) { - $this->log("$connectionId: add topic $topicId for user $userId"); - } - - $this->connectionIdTopicIdListMap[$connectionId][] = $topicId; - } - - $this->topicHash[$topicId] = $topic; + $this->addTopicForUser($connectionId, $topic, $userId); } /** @@ -389,37 +389,26 @@ class Pusher implements WampServerInterface $authToken = preg_replace('/[^a-zA-Z0-9\-]+/', '', $params['authToken']); $userId = preg_replace('/[^a-zA-Z0-9\-]+/', '', $params['userId']); - $result = $this->getUserIdByAuthToken($authToken); - - if (empty($result)) { + if (!$authToken || !$userId) { $this->closeConnection($conn); return; } - if ($result !== $userId) { - $this->closeConnection($conn); + $command = "$this->phpExecutablePath command.php AuthTokenCheck $authToken $userId 2>&1"; - return; - } + $process = new Process($command); + $process->start(); - $this->subscribeUser($conn, $userId); - } + $process->on('exit', function ($exitCode) use ($conn, $userId) { + if ($exitCode !== 0) { + $this->closeConnection($conn); - /** - * @param string $authToken - * @return string - */ - private function getUserIdByAuthToken($authToken) - { - /** @var string|null|false $result */ - $result = shell_exec($this->phpExecutablePath . " command.php AuthTokenCheck " . $authToken); + return; + } - if ($result === null || $result === false) { - return ''; - } - - return $result; + $this->subscribeUser($conn, $userId); + }); } /** @@ -564,4 +553,23 @@ class Pusher implements WampServerInterface { echo "[" . date('Y-m-d H:i:s') . "] " . $msg . "\n"; } + + private function addTopicForUser(string $connectionId, Topic $topic, string $userId): void + { + $topicId = $topic->getId(); + + if (!$topicId) { + return; + } + + if (!in_array($topicId, $this->connectionIdTopicIdListMap[$connectionId])) { + if ($this->isDebugMode) { + $this->log("$connectionId: add topic $topicId for user $userId"); + } + + $this->connectionIdTopicIdListMap[$connectionId][] = $topicId; + } + + $this->topicHash[$topicId] = $topic; + } } diff --git a/composer.json b/composer.json index 7961ee6b58..299240ba78 100644 --- a/composer.json +++ b/composer.json @@ -55,7 +55,8 @@ "league/oauth2-client": "^2.8", "symfony/mailer": "^6", "league/html-to-markdown": "^5.1", - "psr/clock": "^1.0" + "psr/clock": "^1.0", + "react/child-process": "^0.6.6" }, "require-dev": { "phpunit/phpunit": "^11.5", diff --git a/composer.lock b/composer.lock index 010f155915..e7ea44b9ee 100644 --- a/composer.lock +++ b/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": "6d5fe3b3c8776218dbb0b02e45ff4cde", + "content-hash": "520a5b72c40d1805a85b374259d7d63a", "packages": [ { "name": "async-aws/core", @@ -5328,6 +5328,81 @@ ], "time": "2022-11-30T15:59:55+00:00" }, + { + "name": "react/child-process", + "version": "v0.6.6", + "source": { + "type": "git", + "url": "https://github.com/reactphp/child-process.git", + "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/child-process/zipball/1721e2b93d89b745664353b9cfc8f155ba8a6159", + "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/event-loop": "^1.2", + "react/stream": "^1.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/socket": "^1.16", + "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\ChildProcess\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Event-driven library for executing child processes with ReactPHP.", + "keywords": [ + "event-driven", + "process", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/child-process/issues", + "source": "https://github.com/reactphp/child-process/tree/v0.6.6" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-01-01T16:37:48+00:00" + }, { "name": "react/dns", "version": "v1.13.0",