From 90cf597c1556df19377644e82fbb4884ccc42d8b Mon Sep 17 00:00:00 2001 From: Rodrigo Date: Tue, 2 Sep 2025 14:49:21 -0400 Subject: [PATCH] laravel echo server to local develop --- .../TenantAwareBroadcastManager.php | 10 ++ .../TenantAwareRedisBroadcaster.php | 36 ++++ routes/channels.php | 166 ++++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 ProcessMaker/Multitenancy/Broadcasting/TenantAwareRedisBroadcaster.php diff --git a/ProcessMaker/Multitenancy/Broadcasting/TenantAwareBroadcastManager.php b/ProcessMaker/Multitenancy/Broadcasting/TenantAwareBroadcastManager.php index c2570bc129..e29b325efc 100644 --- a/ProcessMaker/Multitenancy/Broadcasting/TenantAwareBroadcastManager.php +++ b/ProcessMaker/Multitenancy/Broadcasting/TenantAwareBroadcastManager.php @@ -3,6 +3,7 @@ namespace ProcessMaker\Multitenancy\Broadcasting; use Illuminate\Broadcasting\BroadcastManager; +use Illuminate\Contracts\Redis\Factory as Redis; class TenantAwareBroadcastManager extends BroadcastManager { @@ -18,4 +19,13 @@ public function createPusherDriver($config) { return new TenantAwarePusherBroadcaster($this->pusher($config), $this->tenantId); } + + public function createRedisDriver($config) + { + return new TenantAwareRedisBroadcaster( + $this->app->make(Redis::class), + $config['connection'] ?? 'default', + $this->tenantId + ); + } } diff --git a/ProcessMaker/Multitenancy/Broadcasting/TenantAwareRedisBroadcaster.php b/ProcessMaker/Multitenancy/Broadcasting/TenantAwareRedisBroadcaster.php new file mode 100644 index 0000000000..c4b1f8419e --- /dev/null +++ b/ProcessMaker/Multitenancy/Broadcasting/TenantAwareRedisBroadcaster.php @@ -0,0 +1,36 @@ +tenantId = $tenantId; + } + + protected function formatChannels(array $channels) + { + $channels = array_map(function ($channel) { + $channel = (string) $channel; + if ($this->tenantId) { + // Check if channel starts with "private-" + if (str_starts_with($channel, 'private-')) { + return "private-tenant_{$this->tenantId}." . substr($channel, 8); // Remove "private-" prefix and add tenant before the rest + } + + return "tenant_{$this->tenantId}.{$channel}"; + } + + return $channel; + }, $channels); + + return $channels; + } +} diff --git a/routes/channels.php b/routes/channels.php index eb10a61a18..cc662fe2a1 100644 --- a/routes/channels.php +++ b/routes/channels.php @@ -50,3 +50,169 @@ Broadcast::channel('ProcessMaker.Models.Process.{processId}.Language.{language}', function ($user, $processId, $language) { return true; }); + +// Tenant-aware channel authorizations +Broadcast::channel('tenant_{tenantId}.ProcessMaker.Models.User.{id}', function ($user, $tenantId, $id) { + // Handle anonymous users - they should not have access to user-specific channels + if (isset($user->isAnonymous) && $user->isAnonymous) { + return false; + } + + return (int) $user->id === (int) $id; +}); + +Broadcast::channel('tenant_{tenantId}.ProcessMaker.Models.ProcessRequest.{id}', function ($user, $tenantId, $id) { + if ($id === 'undefined' || $user === 'undefined') { + return false; + } + + // Handle anonymous users - they should not have access to process request channels + if (isset($user->isAnonymous) && $user->isAnonymous) { + return false; + } + + if ($user->is_administrator) { + return true; + } + + $request = ProcessRequest::find($id); + + if (!$request) { + return false; + } + + return $request->user_id === $user->id + || !empty($request->participants()->where('users.id', $user->getKey())->first()) + || $request->process?->manager_id === $user->id; +}); + +Broadcast::channel('tenant_{tenantId}.ProcessMaker.Models.ProcessRequestToken.{id}', function ($user, $tenantId, $id) { + // Handle anonymous users - they should not have access to process request token channels + if (isset($user->isAnonymous) && $user->isAnonymous) { + return false; + } + + if ($user->is_administrator) { + return true; + } + + $token = ProcessRequestToken::find($id); + + if (!$token) { + return false; + } + + return $user->getKey() === $token->user_id; +}); + +Broadcast::channel('tenant_{tenantId}.test.status', function ($user, $tenantId) { + return true; +}); + +Broadcast::channel('tenant_{tenantId}.ProcessMaker.Models.Process.{processId}.Language.{language}', function ($user, $tenantId, $processId, $language) { + return true; +}); + +// Private tenant-aware channel authorizations +Broadcast::channel('private-tenant_{tenantId}.ProcessMaker.Models.User.{id}', function ($user, $tenantId, $id) { + // Handle anonymous users - they should not have access to private channels + if (isset($user->isAnonymous) && $user->isAnonymous) { + return false; + } + + return (int) $user->id === (int) $id; +}); + +Broadcast::channel('private-tenant_{tenantId}.ProcessMaker.Models.ProcessRequest.{id}', function ($user, $tenantId, $id) { + if ($id === 'undefined' || $user === 'undefined') { + return false; + } + + // Handle anonymous users - they should not have access to private channels + if (isset($user->isAnonymous) && $user->isAnonymous) { + return false; + } + + if ($user->is_administrator) { + return true; + } + + $request = ProcessRequest::find($id); + + if (!$request) { + return false; + } + + return $request->user_id === $user->id + || !empty($request->participants()->where('users.id', $user->getKey())->first()) + || $request->process?->manager_id === $user->id; +}); + +Broadcast::channel('private-tenant_{tenantId}.ProcessMaker.Models.ProcessRequestToken.{id}', function ($user, $tenantId, $id) { + // Handle anonymous users - they should not have access to private channels + if (isset($user->isAnonymous) && $user->isAnonymous) { + return false; + } + + if ($user->is_administrator) { + return true; + } + + $token = ProcessRequestToken::find($id); + + if (!$token) { + return false; + } + + return $user->getKey() === $token->user_id; +}); + +Broadcast::channel('private-tenant_{tenantId}.test.status', function ($user, $tenantId) { + return true; +}); + +Broadcast::channel('private-tenant_{tenantId}.ProcessMaker.Models.Process.{processId}.Language.{language}', function ($user, $tenantId, $processId, $language) { + return true; +}); + +// Generic package channel authorizations - allow authenticated users to access package channels +Broadcast::channel('private-tenant_{tenantId}.ProcessMaker.Package.{packageName}.{model}.{id}', function ($user, $tenantId, $packageName, $model, $id) { + // Handle anonymous users - they should not have access to private package channels + if (isset($user->isAnonymous) && $user->isAnonymous) { + return false; + } + + // Allow authenticated users to access package channels + return true; +}); + +Broadcast::channel('tenant_{tenantId}.ProcessMaker.Package.{packageName}.{model}.{id}', function ($user, $tenantId, $packageName, $model, $id) { + // Handle anonymous users - they should not have access to package channels + if (isset($user->isAnonymous) && $user->isAnonymous) { + return false; + } + + // Allow authenticated users to access package channels + return true; +}); + +// Generic package channel authorizations without tenant prefix +Broadcast::channel('private-ProcessMaker.Package.{packageName}.{model}.{id}', function ($user, $packageName, $model, $id) { + // Handle anonymous users - they should not have access to private package channels + if (isset($user->isAnonymous) && $user->isAnonymous) { + return false; + } + + // Allow authenticated users to access package channels + return true; +}); + +Broadcast::channel('ProcessMaker.Package.{packageName}.{model}.{id}', function ($user, $packageName, $model, $id) { + // Handle anonymous users - they should not have access to package channels + if (isset($user->isAnonymous) && $user->isAnonymous) { + return false; + } + + // Allow authenticated users to access package channels + return true; +});