Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions ProcessMaker/Http/Controllers/Admin/QueuesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,28 @@
use Illuminate\Auth\Access\AuthorizationException;
use ProcessMaker\Events\QueueManagementAccessed;
use ProcessMaker\Http\Controllers\Controller;
use ProcessMaker\Providers\TenantQueueServiceProvider;

class QueuesController extends Controller
{
public function index()
{
if (auth()->user()->is_administrator) {
// Register the Event
QueueManagementAccessed::dispatch();
if (!auth()->user()->is_administrator) {
throw new AuthorizationException();
}

return view('admin.queues.index');
if (config('app.multitenancy')) {
if (!TenantQueueServiceProvider::allowAllTenats()) {
// Its multitenancy and they don't have access to all tenants so
// redirect to the tenant-filtered queue management page.
// Otherwise, show the horizon queue manager.
return redirect()->route('tenant-queue.index');
}
}

throw new AuthorizationException();
// Register the Event
QueueManagementAccessed::dispatch();

return view('admin.queues.index');
}
}
21 changes: 20 additions & 1 deletion ProcessMaker/Http/Controllers/Admin/TenantQueueController.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,20 @@ class TenantQueueController extends Controller
public function __construct()
{
// Check if tenant job tracking is enabled
$enabled = config('queue.tenant_tracking_enabled', false);
$enabled = TenantQueueServiceProvider::enabled();

if (!$enabled) {
if (!app()->runningInConsole()) {
abort(404, 'Tenant queue tracking is disabled');
}
}

// If the route binding has a tenant id, check if the user is allowed to access the tenant queue
if ($id = (int) request()->route('tenantId')) {
if (!TenantQueueServiceProvider::allowAllTenats() && $id !== app('currentTenant')?->id) {
throw new AuthorizationException();
}
}
}

/**
Expand All @@ -52,6 +59,12 @@ public function getTenants(): JsonResponse

$tenantsWithJobs = TenantQueueServiceProvider::getTenantsWithJobs();

if (!TenantQueueServiceProvider::allowAllTenats()) {
$tenantsWithJobs = array_filter($tenantsWithJobs, function ($tenantData) {
return (int) $tenantData['id'] === app('currentTenant')?->id;
});
}

// Enrich with tenant information
$tenants = [];
foreach ($tenantsWithJobs as $tenantData) {
Expand Down Expand Up @@ -118,6 +131,12 @@ public function getOverallStats(): JsonResponse

$tenantsWithJobs = TenantQueueServiceProvider::getTenantsWithJobs();

if (!TenantQueueServiceProvider::allowAllTenats()) {
$tenantsWithJobs = array_filter($tenantsWithJobs, function ($tenantData) {
return (int) $tenantData['id'] === app('currentTenant')?->id;
});
}

$overallStats = [
'total_tenants' => count($tenantsWithJobs),
'total_jobs' => 0,
Expand Down
22 changes: 6 additions & 16 deletions ProcessMaker/Multitenancy/MakeQueueTenantAwareAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,16 @@

namespace ProcessMaker\Multitenancy;

use Illuminate\Queue\Events\JobProcessing;
use Illuminate\Queue\Events\JobRetryRequested;
use Illuminate\Support\Facades\Context;
use Spatie\Multitenancy\Actions\MakeQueueTenantAwareAction as BaseMakeQueueTenantAwareAction;

class MakeQueueTenantAwareAction extends BaseMakeQueueTenantAwareAction
{
/*
* We need to override this method because spatie will throw an error if the tenant is not found.
* However, we want to support non-multitenant instances. If the tenant is not found,
* run the job without a tenant.
*/
protected function bindOrForgetCurrentTenant(JobProcessing|JobRetryRequested $event): void
/**
* We're handling tenant aware queues manually, however, we still need to implement this because for some
* reason the Spatie package calls it in Multitenancy::start(), weather it's a configured action or not.
*/
public function execute() : void
{
$tenantId = Context::get($this->currentTenantContextKey());
if (!$tenantId) {
// No need to do anything. Let the job run without a tenant.
return;
}

parent::bindOrForgetCurrentTenant($event);
// Do nothing
}
}
31 changes: 0 additions & 31 deletions ProcessMaker/Multitenancy/TenantAwareDispatcher.php

This file was deleted.

84 changes: 47 additions & 37 deletions ProcessMaker/Multitenancy/TenantBootstrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,33 @@
*/
class TenantBootstrapper
{
private static $landlordValues = [];

private $encrypter = null;

private $pdo = null;

private $originalValues = null;
private $app = null;

public static $landlordKeysToSave = [
'APP_URL',
'APP_KEY',
'LOG_PATH',
'DB_USERNAME',
'DB_PASSWORD',
'REDIS_PREFIX',
'CACHE_SETTING_PREFIX',
'SCRIPT_MICROSERVICE_CALLBACK',
];

public function bootstrap(Application $app)
{
if (!$this->env('MULTITENANCY')) {
return;
}
$this->app = $app;

// We need to save the original values for running horizon
$this->saveOriginalValues();
self::saveLandlordValues($app);

$tenantData = null;

Expand All @@ -49,26 +62,26 @@ public function bootstrap(Application $app)

return;
}
$this->setTenantEnvironmentVariables($app, $tenantData);

// Set storage path
$app->useStoragePath($app->basePath('storage/tenant_' . $tenantData['id']));

$this->setTenantEnvironmentVariables($tenantData);

// Use tenant's translation files. Doing this here so it's available in cached filesystems.php
$app->useLangPath(resource_path('lang/tenant_' . $tenantData['id']));

$tenantData['original_values'] = $this->getOriginalValue();
$tenantData['original_values'] = self::$landlordValues;
Tenant::setBootstrappedTenant($app, $tenantData);
}

private function setTenantEnvironmentVariables($app, $tenantData)
private function setTenantEnvironmentVariables($tenantData)
{
// Additional configs are set in SwitchTenant.php

$tenantId = $tenantData['id'];
$config = json_decode($tenantData['config'], true);

$this->set('APP_CONFIG_CACHE', $app->basePath('storage/tenant_' . $tenantId . '/config.php'));
// Do not override packages cache path for now. Wait until the License service is updated.
// $this->set('APP_PACKAGES_CACHE', $app->basePath('storage/tenant_' . $tenantId . '/packages.php'));
$this->set('LARAVEL_STORAGE_PATH', $app->basePath('storage/tenant_' . $tenantId));
$this->set('APP_CONFIG_CACHE', $this->app->storagePath('config.php'));
$this->set('APP_URL', $config['app.url']);
$this->set('APP_KEY', $this->decrypt($config['app.key']));
$this->set('DB_DATABASE', $tenantData['database']);
Expand All @@ -83,51 +96,48 @@ private function setTenantEnvironmentVariables($app, $tenantData)
}

$this->set('DB_PASSWORD', $password);
$this->set('REDIS_PREFIX', $this->getOriginalValue('REDIS_PREFIX') . 'tenant-' . $tenantId . ':');
$this->set('LOG_PATH', $app->basePath('storage/tenant_' . $tenantId . '/logs/processmaker.log'));
$this->set('LOG_PATH', $this->app->storagePath('logs/processmaker.log'));
}

private function saveOriginalValues()
public static function saveLandlordValues($app)
{
if ($this->env('ORIGINAL_VALUES')) {
if ($app->has('landlordValues')) {
self::$landlordValues = $app->make('landlordValues');

return;
}
$toSave = [
'APP_URL',
'APP_KEY',
'DB_USERNAME',
'DB_PASSWORD',
'REDIS_PREFIX',
'CACHE_SETTING_PREFIX',
'SCRIPT_MICROSERVICE_CALLBACK',
];
$values = [];
foreach ($toSave as $key) {
$values[$key] = $this->env($key);

foreach (self::$landlordKeysToSave as $key) {
self::$landlordValues[$key] = $_SERVER[$key] ?? '';
}
$this->set('ORIGINAL_VALUES', serialize($values));
}

private function getOriginalValue($key = null)
private function getOriginalValue($key)
{
if (!$this->originalValues) {
$this->originalValues = unserialize($this->env('ORIGINAL_VALUES'));
}
if (!$key) {
return $this->originalValues;
if (!isset(self::$landlordValues[$key])) {
return '';
}

return $this->originalValues[$key];
return self::$landlordValues[$key];
}

private function env($key, $default = null)
{
return Env::get($key, $default);
$value = $_SERVER[$key] ?? $default;
if ($value === 'true') {
$value = true;
} elseif ($value === 'false') {
$value = false;
}

return $value;
}

private function set($key, $value)
{
Env::getRepository()->set($key, $value);
// Env::getRepository() is immutable but will use values from $_SERVER and $_ENV
$_SERVER[$key] = $value;
$_ENV[$key] = $value;
}

private function decrypt($value)
Expand Down
Loading
Loading