From 55d0a9ab87a762fcc22f473712524c78a2d2be1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Fri, 26 Aug 2022 21:35:17 +0200 Subject: [PATCH] misc improvements - stronger types, exception refactor --- Dockerfile | 6 --- src/Contracts/ManagesDatabaseUsers.php | 5 +++ src/Contracts/SyncMaster.php | 2 + src/Contracts/Tenant.php | 12 ++--- .../TenantCouldNotBeIdentifiedException.php | 45 ++++++++++++++++++- src/Contracts/TenantDatabaseManager.php | 16 ++----- src/Contracts/TenantWithDatabase.php | 2 +- src/Database/Concerns/HasInternalKeys.php | 16 +++---- src/Database/Concerns/TenantRun.php | 6 ++- src/Database/DatabaseManager.php | 2 + src/Database/Models/ImpersonationToken.php | 4 +- src/Database/Models/Tenant.php | 2 +- src/Database/Models/TenantPivot.php | 4 +- src/Database/ParentModelScope.php | 2 +- src/Database/TenantCollection.php | 3 +- src/DatabaseConfig.php | 7 +-- .../TenantCouldNotBeIdentifiedById.php | 28 ------------ ...enantCouldNotBeIdentifiedByIdException.php | 18 ++++++++ ...antCouldNotBeIdentifiedByPathException.php | 21 +++------ ...dNotBeIdentifiedByRequestDataException.php | 21 +++------ ...tCouldNotBeIdentifiedOnDomainException.php | 21 +++------ .../TenantDatabaseAlreadyExistsException.php | 14 +++--- .../TenantDatabaseDoesNotExistException.php | 2 +- ...nantDatabaseUserAlreadyExistsException.php | 14 +++--- src/Features/CrossDomainRedirect.php | 2 +- src/Features/UniversalRoutes.php | 7 +-- src/Features/UserImpersonation.php | 27 +++++------ .../Contracts/CachedTenantResolver.php | 9 ++-- src/Resolvers/DomainTenantResolver.php | 17 +++---- src/Resolvers/PathTenantResolver.php | 11 ++--- src/Resolvers/RequestDataTenantResolver.php | 9 ++-- src/Tenancy.php | 14 +++--- .../MicrosoftSQLDatabaseManager.php | 9 ++-- tests/TenantModelTest.php | 10 +++-- 34 files changed, 179 insertions(+), 209 deletions(-) delete mode 100644 src/Exceptions/TenantCouldNotBeIdentifiedById.php create mode 100644 src/Exceptions/TenantCouldNotBeIdentifiedByIdException.php diff --git a/Dockerfile b/Dockerfile index b1d76ea8..0ced8009 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,12 +5,6 @@ ARG PHP_VERSION=8.1 WORKDIR /var/www/html -LABEL org.opencontainers.image.source=https://github.com/stancl/tenancy \ - org.opencontainers.image.vendor="Samuel Ć tancl" \ - org.opencontainers.image.licenses="MIT" \ - org.opencontainers.image.title="PHP ${PHP_VERSION} with modules for laravel support" \ - org.opencontainers.image.description="PHP ${PHP_VERSION} with a set of php/os packages suitable for running Laravel apps" - # our default timezone and langauge ENV TZ=Europe/London ENV LANG=en_GB.UTF-8 diff --git a/src/Contracts/ManagesDatabaseUsers.php b/src/Contracts/ManagesDatabaseUsers.php index 6de28a80..d4da1c3c 100644 --- a/src/Contracts/ManagesDatabaseUsers.php +++ b/src/Contracts/ManagesDatabaseUsers.php @@ -6,11 +6,16 @@ namespace Stancl\Tenancy\Contracts; use Stancl\Tenancy\DatabaseConfig; +// todo possibly move to Database namespace, along with other classes + interface ManagesDatabaseUsers extends TenantDatabaseManager { + /** Create a database user. */ public function createUser(DatabaseConfig $databaseConfig): bool; + /** Delete a database user. */ public function deleteUser(DatabaseConfig $databaseConfig): bool; + /** Does a database user exist? */ public function userExists(string $username): bool; } diff --git a/src/Contracts/SyncMaster.php b/src/Contracts/SyncMaster.php index e1b34149..28fafa91 100644 --- a/src/Contracts/SyncMaster.php +++ b/src/Contracts/SyncMaster.php @@ -7,6 +7,8 @@ namespace Stancl\Tenancy\Contracts; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Relations\BelongsToMany; +// todo move all resource syncing-related things to a separate namespace? + /** * @property-read Tenant[]|Collection $tenants */ diff --git a/src/Contracts/Tenant.php b/src/Contracts/Tenant.php index 770e862e..c01cd7d5 100644 --- a/src/Contracts/Tenant.php +++ b/src/Contracts/Tenant.php @@ -4,11 +4,11 @@ declare(strict_types=1); namespace Stancl\Tenancy\Contracts; +use Closure; + /** * @see \Stancl\Tenancy\Database\Models\Tenant * - * @method __call(string $method, array $parameters) IDE support. This will be a model. - * @method static __callStatic(string $method, array $parameters) IDE support. This will be a model. * @mixin \Illuminate\Database\Eloquent\Model */ interface Tenant @@ -17,14 +17,14 @@ interface Tenant public function getTenantKeyName(): string; /** Get the value of the key used for identifying the tenant. */ - public function getTenantKey(); + public function getTenantKey(): int|string; /** Get the value of an internal key. */ - public function getInternal(string $key); + public function getInternal(string $key): mixed; /** Set the value of an internal key. */ - public function setInternal(string $key, $value); + public function setInternal(string $key, mixed $value): static; /** Run a callback in this tenant's environment. */ - public function run(callable $callback); + public function run(Closure $callback): mixed; } diff --git a/src/Contracts/TenantCouldNotBeIdentifiedException.php b/src/Contracts/TenantCouldNotBeIdentifiedException.php index ac767e5e..2f301c37 100644 --- a/src/Contracts/TenantCouldNotBeIdentifiedException.php +++ b/src/Contracts/TenantCouldNotBeIdentifiedException.php @@ -5,7 +5,50 @@ declare(strict_types=1); namespace Stancl\Tenancy\Contracts; use Exception; +use Facade\IgnitionContracts\BaseSolution; +use Facade\IgnitionContracts\ProvidesSolution; +use Facade\IgnitionContracts\Solution; -abstract class TenantCouldNotBeIdentifiedException extends Exception +abstract class TenantCouldNotBeIdentifiedException extends Exception implements ProvidesSolution { + /** Default solution title. */ + protected string $solutionTitle = 'Tenant could not be identified'; + + /** Default solution description. */ + protected string $solutionDescription = 'Are you sure this tenant exists?'; + + /** Set the message. */ + protected function tenantCouldNotBeIdentified(string $how): static + { + $this->message = "Tenant could not be identified " . $how; + + return $this; + } + + /** Set the solution title. */ + protected function title(string $solutionTitle): static + { + $this->solutionTitle = $solutionTitle; + + return $this; + } + + /** Set the solution description. */ + protected function description(string $solutionDescription): static + { + $this->solutionDescription = $solutionDescription; + + return $this; + } + + /** Get the Ignition description. */ + public function getSolution(): Solution + { + return BaseSolution::create($this->solutionTitle) + ->setSolutionDescription($this->solutionDescription) + ->setDocumentationLinks([ + 'Tenants' => 'https://tenancyforlaravel.com/docs/v3/tenants', + 'Tenant Identification' => 'https://tenancyforlaravel.com/docs/v3/tenant-identification', + ]); + } } diff --git a/src/Contracts/TenantDatabaseManager.php b/src/Contracts/TenantDatabaseManager.php index 92801d75..deaecb33 100644 --- a/src/Contracts/TenantDatabaseManager.php +++ b/src/Contracts/TenantDatabaseManager.php @@ -8,24 +8,16 @@ use Stancl\Tenancy\Exceptions\NoConnectionSetException; interface TenantDatabaseManager { - /** - * Create a database. - */ + /** Create a database. */ public function createDatabase(TenantWithDatabase $tenant): bool; - /** - * Delete a database. - */ + /** Delete a database. */ public function deleteDatabase(TenantWithDatabase $tenant): bool; - /** - * Does a database exist. - */ + /** Does a database exist? */ public function databaseExists(string $name): bool; - /** - * Make a DB connection config array. - */ + /** Construct a DB connection config array. */ public function makeConnectionConfig(array $baseConfig, string $databaseName): array; /** diff --git a/src/Contracts/TenantWithDatabase.php b/src/Contracts/TenantWithDatabase.php index c3f51628..98268502 100644 --- a/src/Contracts/TenantWithDatabase.php +++ b/src/Contracts/TenantWithDatabase.php @@ -11,5 +11,5 @@ interface TenantWithDatabase extends Tenant public function database(): DatabaseConfig; /** Get an internal key. */ - public function getInternal(string $key); + public function getInternal(string $key): mixed; } diff --git a/src/Database/Concerns/HasInternalKeys.php b/src/Database/Concerns/HasInternalKeys.php index b4e175c3..ea70d6ed 100644 --- a/src/Database/Concerns/HasInternalKeys.php +++ b/src/Database/Concerns/HasInternalKeys.php @@ -6,26 +6,20 @@ namespace Stancl\Tenancy\Database\Concerns; trait HasInternalKeys { - /** - * Get the internal prefix. - */ + /** Get the internal prefix. */ public static function internalPrefix(): string { return 'tenancy_'; } - /** - * Get an internal key. - */ - public function getInternal(string $key) + /** Get an internal key. */ + public function getInternal(string $key): mixed { return $this->getAttribute(static::internalPrefix() . $key); } - /** - * Set internal key. - */ - public function setInternal(string $key, $value) + /** Set internal key. */ + public function setInternal(string $key, mixed $value): static { $this->setAttribute(static::internalPrefix() . $key, $value); diff --git a/src/Database/Concerns/TenantRun.php b/src/Database/Concerns/TenantRun.php index 29bbedac..01d5b9d8 100644 --- a/src/Database/Concerns/TenantRun.php +++ b/src/Database/Concerns/TenantRun.php @@ -4,15 +4,17 @@ declare(strict_types=1); namespace Stancl\Tenancy\Database\Concerns; +use Closure; use Stancl\Tenancy\Contracts\Tenant; trait TenantRun { /** * Run a callback in this tenant's context. - * Atomic, safely reverts to previous context. + * + * This method is atomic and safely reverts to the previous context. */ - public function run(callable $callback) + public function run(Closure $callback): mixed { /** @var Tenant $this */ $originalTenant = tenant(); diff --git a/src/Database/DatabaseManager.php b/src/Database/DatabaseManager.php index 6242ffa9..eb807696 100644 --- a/src/Database/DatabaseManager.php +++ b/src/Database/DatabaseManager.php @@ -14,6 +14,8 @@ use Stancl\Tenancy\Exceptions\DatabaseManagerNotRegisteredException; use Stancl\Tenancy\Exceptions\TenantDatabaseAlreadyExistsException; use Stancl\Tenancy\Exceptions\TenantDatabaseUserAlreadyExistsException; +// todo move to Database namespace + /** * @internal Class is subject to breaking changes in minor and patch versions. */ diff --git a/src/Database/Models/ImpersonationToken.php b/src/Database/Models/ImpersonationToken.php index 43c536fb..d73b436b 100644 --- a/src/Database/Models/ImpersonationToken.php +++ b/src/Database/Models/ImpersonationToken.php @@ -35,10 +35,8 @@ class ImpersonationToken extends Model 'created_at', ]; - public static function boot() + public static function booted() { - parent::boot(); - static::creating(function ($model) { $model->created_at = $model->created_at ?? $model->freshTimestamp(); $model->token = $model->token ?? Str::random(128); diff --git a/src/Database/Models/Tenant.php b/src/Database/Models/Tenant.php index f88297be..4518e7b7 100644 --- a/src/Database/Models/Tenant.php +++ b/src/Database/Models/Tenant.php @@ -39,7 +39,7 @@ class Tenant extends Model implements Contracts\Tenant return 'id'; } - public function getTenantKey() + public function getTenantKey(): int|string { return $this->getAttribute($this->getTenantKeyName()); } diff --git a/src/Database/Models/TenantPivot.php b/src/Database/Models/TenantPivot.php index f745b45a..5c0d6a37 100644 --- a/src/Database/Models/TenantPivot.php +++ b/src/Database/Models/TenantPivot.php @@ -9,10 +9,8 @@ use Stancl\Tenancy\Contracts\Syncable; class TenantPivot extends Pivot { - public static function boot() + public static function booted() { - parent::boot(); - static::saved(function (self $pivot) { $parent = $pivot->pivotParent; diff --git a/src/Database/ParentModelScope.php b/src/Database/ParentModelScope.php index 387b1d19..78f5de20 100644 --- a/src/Database/ParentModelScope.php +++ b/src/Database/ParentModelScope.php @@ -10,7 +10,7 @@ use Illuminate\Database\Eloquent\Scope; class ParentModelScope implements Scope { - public function apply(Builder $builder, Model $model) + public function apply(Builder $builder, Model $model): void { if (! tenancy()->initialized) { return; diff --git a/src/Database/TenantCollection.php b/src/Database/TenantCollection.php index ba3a8fab..45a137fe 100644 --- a/src/Database/TenantCollection.php +++ b/src/Database/TenantCollection.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Stancl\Tenancy\Database; +use Closure; use Illuminate\Database\Eloquent\Collection; use Stancl\Tenancy\Contracts\Tenant; @@ -16,7 +17,7 @@ use Stancl\Tenancy\Contracts\Tenant; */ class TenantCollection extends Collection { - public function runForEach(callable $callable): self + public function runForEach(Closure $callable): self { tenancy()->runForMultiple($this->items, $callable); diff --git a/src/DatabaseConfig.php b/src/DatabaseConfig.php index b3195960..60e70ce5 100644 --- a/src/DatabaseConfig.php +++ b/src/DatabaseConfig.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Stancl\Tenancy; +use Closure; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; @@ -48,17 +49,17 @@ class DatabaseConfig $this->tenant = $tenant; } - public static function generateDatabaseNamesUsing(callable $databaseNameGenerator): void + public static function generateDatabaseNamesUsing(Closure $databaseNameGenerator): void { static::$databaseNameGenerator = $databaseNameGenerator; } - public static function generateUsernamesUsing(callable $usernameGenerator): void + public static function generateUsernamesUsing(Closure $usernameGenerator): void { static::$usernameGenerator = $usernameGenerator; } - public static function generatePasswordsUsing(callable $passwordGenerator): void + public static function generatePasswordsUsing(Closure $passwordGenerator): void { static::$passwordGenerator = $passwordGenerator; } diff --git a/src/Exceptions/TenantCouldNotBeIdentifiedById.php b/src/Exceptions/TenantCouldNotBeIdentifiedById.php deleted file mode 100644 index 5c2e562c..00000000 --- a/src/Exceptions/TenantCouldNotBeIdentifiedById.php +++ /dev/null @@ -1,28 +0,0 @@ -setSolutionDescription('Are you sure the ID is correct and the tenant exists?') - ->setDocumentationLinks([ - 'Initializing Tenants' => 'https://tenancyforlaravel.com/docs/v3/tenants', - ]); - } -} diff --git a/src/Exceptions/TenantCouldNotBeIdentifiedByIdException.php b/src/Exceptions/TenantCouldNotBeIdentifiedByIdException.php new file mode 100644 index 00000000..13b83daf --- /dev/null +++ b/src/Exceptions/TenantCouldNotBeIdentifiedByIdException.php @@ -0,0 +1,18 @@ +tenantCouldNotBeIdentified("by tenant id: $tenant_id") + ->title('Tenant could not be identified with that ID') + ->description('Are you sure the ID is correct and the tenant exists?'); + } +} diff --git a/src/Exceptions/TenantCouldNotBeIdentifiedByPathException.php b/src/Exceptions/TenantCouldNotBeIdentifiedByPathException.php index 896c9323..5a494d90 100644 --- a/src/Exceptions/TenantCouldNotBeIdentifiedByPathException.php +++ b/src/Exceptions/TenantCouldNotBeIdentifiedByPathException.php @@ -4,24 +4,15 @@ declare(strict_types=1); namespace Stancl\Tenancy\Exceptions; -use Facade\IgnitionContracts\BaseSolution; -use Facade\IgnitionContracts\ProvidesSolution; -use Facade\IgnitionContracts\Solution; use Stancl\Tenancy\Contracts\TenantCouldNotBeIdentifiedException; -class TenantCouldNotBeIdentifiedByPathException extends TenantCouldNotBeIdentifiedException implements ProvidesSolution +class TenantCouldNotBeIdentifiedByPathException extends TenantCouldNotBeIdentifiedException { - public function __construct($tenant_id) + public function __construct(int|string $tenant_id) { - parent::__construct("Tenant could not be identified on path with tenant_id: $tenant_id"); - } - - public function getSolution(): Solution - { - return BaseSolution::create('Tenant could not be identified on this path') - ->setSolutionDescription('Did you forget to create a tenant for this path?') - ->setDocumentationLinks([ - 'Creating Tenants' => 'https://tenancyforlaravel.com/docs/v3/tenants/', - ]); + $this + ->tenantCouldNotBeIdentified("on path with tenant id: $tenant_id") + ->title('Tenant could not be identified on this path') + ->description('Did you forget to create a tenant for this path?'); } } diff --git a/src/Exceptions/TenantCouldNotBeIdentifiedByRequestDataException.php b/src/Exceptions/TenantCouldNotBeIdentifiedByRequestDataException.php index f0447d96..1f1c98a1 100644 --- a/src/Exceptions/TenantCouldNotBeIdentifiedByRequestDataException.php +++ b/src/Exceptions/TenantCouldNotBeIdentifiedByRequestDataException.php @@ -4,24 +4,15 @@ declare(strict_types=1); namespace Stancl\Tenancy\Exceptions; -use Facade\IgnitionContracts\BaseSolution; -use Facade\IgnitionContracts\ProvidesSolution; -use Facade\IgnitionContracts\Solution; use Stancl\Tenancy\Contracts\TenantCouldNotBeIdentifiedException; -class TenantCouldNotBeIdentifiedByRequestDataException extends TenantCouldNotBeIdentifiedException implements ProvidesSolution +class TenantCouldNotBeIdentifiedByRequestDataException extends TenantCouldNotBeIdentifiedException { - public function __construct($tenant_id) + public function __construct(mixed $payload) { - parent::__construct("Tenant could not be identified by request data with payload: $tenant_id"); - } - - public function getSolution(): Solution - { - return BaseSolution::create('Tenant could not be identified with this request data') - ->setSolutionDescription('Did you forget to create a tenant with this id?') - ->setDocumentationLinks([ - 'Creating Tenants' => 'https://tenancyforlaravel.com/docs/v3/tenants/', - ]); + $this + ->tenantCouldNotBeIdentified("by request data with payload: $payload") + ->title('Tenant could not be identified using this request data') + ->description('Did you forget to create a tenant with this id?'); } } diff --git a/src/Exceptions/TenantCouldNotBeIdentifiedOnDomainException.php b/src/Exceptions/TenantCouldNotBeIdentifiedOnDomainException.php index 66bc1db8..0421fe1b 100644 --- a/src/Exceptions/TenantCouldNotBeIdentifiedOnDomainException.php +++ b/src/Exceptions/TenantCouldNotBeIdentifiedOnDomainException.php @@ -4,24 +4,15 @@ declare(strict_types=1); namespace Stancl\Tenancy\Exceptions; -use Facade\IgnitionContracts\BaseSolution; -use Facade\IgnitionContracts\ProvidesSolution; -use Facade\IgnitionContracts\Solution; use Stancl\Tenancy\Contracts\TenantCouldNotBeIdentifiedException; -class TenantCouldNotBeIdentifiedOnDomainException extends TenantCouldNotBeIdentifiedException implements ProvidesSolution +class TenantCouldNotBeIdentifiedOnDomainException extends TenantCouldNotBeIdentifiedException { - public function __construct($domain) + public function __construct(string $domain) { - parent::__construct("Tenant could not be identified on domain $domain"); - } - - public function getSolution(): Solution - { - return BaseSolution::create('Tenant could not be identified on this domain') - ->setSolutionDescription('Did you forget to create a tenant for this domain?') - ->setDocumentationLinks([ - 'Creating Tenants' => 'https://tenancyforlaravel.com/docs/v3/tenants/', - ]); + $this + ->tenantCouldNotBeIdentified("on domain $domain") + ->title('Tenant could not be identified on this domain') + ->description('Did you forget to create a tenant for this domain?'); } } diff --git a/src/Exceptions/TenantDatabaseAlreadyExistsException.php b/src/Exceptions/TenantDatabaseAlreadyExistsException.php index 4c08b66e..ce648625 100644 --- a/src/Exceptions/TenantDatabaseAlreadyExistsException.php +++ b/src/Exceptions/TenantDatabaseAlreadyExistsException.php @@ -8,18 +8,14 @@ use Stancl\Tenancy\Contracts\TenantCannotBeCreatedException; class TenantDatabaseAlreadyExistsException extends TenantCannotBeCreatedException { - /** @var string */ - protected $database; + public function __construct( + protected string $database, + ) { + parent::__construct(); + } public function reason(): string { return "Database {$this->database} already exists."; } - - public function __construct(string $database) - { - $this->database = $database; - - parent::__construct(); - } } diff --git a/src/Exceptions/TenantDatabaseDoesNotExistException.php b/src/Exceptions/TenantDatabaseDoesNotExistException.php index 2f6df0f9..9aa5c3c9 100644 --- a/src/Exceptions/TenantDatabaseDoesNotExistException.php +++ b/src/Exceptions/TenantDatabaseDoesNotExistException.php @@ -8,7 +8,7 @@ use Exception; class TenantDatabaseDoesNotExistException extends Exception { - public function __construct($database) + public function __construct(string $database) { parent::__construct("Database $database does not exist."); } diff --git a/src/Exceptions/TenantDatabaseUserAlreadyExistsException.php b/src/Exceptions/TenantDatabaseUserAlreadyExistsException.php index f84e39ec..00efe2b2 100644 --- a/src/Exceptions/TenantDatabaseUserAlreadyExistsException.php +++ b/src/Exceptions/TenantDatabaseUserAlreadyExistsException.php @@ -8,18 +8,14 @@ use Stancl\Tenancy\Contracts\TenantCannotBeCreatedException; class TenantDatabaseUserAlreadyExistsException extends TenantCannotBeCreatedException { - /** @var string */ - protected $user; + public function __construct( + protected string $user, + ) { + parent::__construct(); + } public function reason(): string { return "Database user {$this->user} already exists."; } - - public function __construct(string $user) - { - parent::__construct(); - - $this->user = $user; - } } diff --git a/src/Features/CrossDomainRedirect.php b/src/Features/CrossDomainRedirect.php index 6ebd15fc..0b6d7682 100644 --- a/src/Features/CrossDomainRedirect.php +++ b/src/Features/CrossDomainRedirect.php @@ -15,7 +15,7 @@ class CrossDomainRedirect implements Feature RedirectResponse::macro('domain', function (string $domain) { /** @var RedirectResponse $this */ - // replace first occurance of hostname fragment with $domain + // Replace first occurrence of the hostname fragment with $domain $url = $this->getTargetUrl(); $hostname = parse_url($url, PHP_URL_HOST); $position = strpos($url, $hostname); diff --git a/src/Features/UniversalRoutes.php b/src/Features/UniversalRoutes.php index c73a5304..e327b5d3 100644 --- a/src/Features/UniversalRoutes.php +++ b/src/Features/UniversalRoutes.php @@ -13,9 +13,10 @@ use Stancl\Tenancy\Tenancy; class UniversalRoutes implements Feature { - public static $middlewareGroup = 'universal'; + public static string $middlewareGroup = 'universal'; - public static $identificationMiddlewares = [ + // todo docblock + public static array $identificationMiddlewares = [ Middleware\InitializeTenancyByDomain::class, Middleware\InitializeTenancyBySubdomain::class, ]; @@ -39,7 +40,7 @@ class UniversalRoutes implements Feature } } - public static function routeHasMiddleware(Route $route, $middleware): bool + public static function routeHasMiddleware(Route $route, string $middleware): bool { if (in_array($middleware, $route->middleware(), true)) { return true; diff --git a/src/Features/UserImpersonation.php b/src/Features/UserImpersonation.php index f96465ff..021b44fc 100644 --- a/src/Features/UserImpersonation.php +++ b/src/Features/UserImpersonation.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace Stancl\Tenancy\Features; -use Carbon\Carbon; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Facades\Auth; use Stancl\Tenancy\Contracts\Feature; @@ -14,7 +13,8 @@ use Stancl\Tenancy\Tenancy; class UserImpersonation implements Feature { - public static $ttl = 60; // seconds + /** The lifespan of impersonation tokens (in seconds). */ + public static int $ttl = 60; public function bootstrap(Tenancy $tenancy): void { @@ -28,25 +28,20 @@ class UserImpersonation implements Feature }); } - /** - * Impersonate a user and get an HTTP redirect response. - * - * @param string|ImpersonationToken $token - * @param int $ttl - */ - public static function makeResponse($token, int $ttl = null): RedirectResponse + /** Impersonate a user and get an HTTP redirect response. */ + public static function makeResponse(string|ImpersonationToken $token, int $ttl = null): RedirectResponse { $token = $token instanceof ImpersonationToken ? $token : ImpersonationToken::findOrFail($token); + $ttl ??= static::$ttl; - if (((string) $token->tenant_id) !== ((string) tenant()->getTenantKey())) { - abort(403); - } + $tokenExpired = $token->created_at->diffInSeconds(now()) > $ttl; - $ttl = $ttl ?? static::$ttl; + abort_if($tokenExpired, 403); - if ($token->created_at->diffInSeconds(Carbon::now()) > $ttl) { - abort(403); - } + $tokenTenantId = (string) $token->tenant_id; + $currentTenantId = (string) tenant()->getTenantKey(); + + abort_unless($tokenTenantId === $currentTenantId, 403); Auth::guard($token->auth_guard)->loginUsingId($token->user_id); diff --git a/src/Resolvers/Contracts/CachedTenantResolver.php b/src/Resolvers/Contracts/CachedTenantResolver.php index e84f1fb1..a3e3daeb 100644 --- a/src/Resolvers/Contracts/CachedTenantResolver.php +++ b/src/Resolvers/Contracts/CachedTenantResolver.php @@ -11,14 +11,11 @@ use Stancl\Tenancy\Contracts\TenantResolver; abstract class CachedTenantResolver implements TenantResolver { - /** @var bool */ - public static $shouldCache = false; + public static bool $shouldCache = false; // todo docblocks for these - /** @var int */ - public static $cacheTTL = 3600; // seconds + public static int $cacheTTL = 3600; // seconds - /** @var string|null */ - public static $cacheStore = null; // default + public static string|null $cacheStore = null; // default /** @var Repository */ protected $cache; diff --git a/src/Resolvers/DomainTenantResolver.php b/src/Resolvers/DomainTenantResolver.php index 13eeb448..44ec4fdc 100644 --- a/src/Resolvers/DomainTenantResolver.php +++ b/src/Resolvers/DomainTenantResolver.php @@ -11,21 +11,14 @@ use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedOnDomainException; class DomainTenantResolver extends Contracts\CachedTenantResolver { - /** - * The model representing the domain that the tenant was identified on. - * - * @var Domain - */ - public static $currentDomain; + /** The model representing the domain that the tenant was identified on. */ + public static Domain $currentDomain; // todo |null? - /** @var bool */ - public static $shouldCache = false; + public static bool $shouldCache = false; - /** @var int */ - public static $cacheTTL = 3600; // seconds + public static int $cacheTTL = 3600; // seconds - /** @var string|null */ - public static $cacheStore = null; // default + public static string|null $cacheStore = null; // default public function resolveWithoutCache(...$args): Tenant { diff --git a/src/Resolvers/PathTenantResolver.php b/src/Resolvers/PathTenantResolver.php index 0b79626f..36a05a83 100644 --- a/src/Resolvers/PathTenantResolver.php +++ b/src/Resolvers/PathTenantResolver.php @@ -10,16 +10,13 @@ use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedByPathException; class PathTenantResolver extends Contracts\CachedTenantResolver { - public static $tenantParameterName = 'tenant'; + public static string $tenantParameterName = 'tenant'; - /** @var bool */ - public static $shouldCache = false; + public static bool $shouldCache = false; - /** @var int */ - public static $cacheTTL = 3600; // seconds + public static int $cacheTTL = 3600; // seconds - /** @var string|null */ - public static $cacheStore = null; // default + public static string|null $cacheStore = null; // default public function resolveWithoutCache(...$args): Tenant { diff --git a/src/Resolvers/RequestDataTenantResolver.php b/src/Resolvers/RequestDataTenantResolver.php index d9f2ebac..65d4ce38 100644 --- a/src/Resolvers/RequestDataTenantResolver.php +++ b/src/Resolvers/RequestDataTenantResolver.php @@ -9,14 +9,11 @@ use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedByRequestDataException; class RequestDataTenantResolver extends Contracts\CachedTenantResolver { - /** @var bool */ - public static $shouldCache = false; + public static bool $shouldCache = false; - /** @var int */ - public static $cacheTTL = 3600; // seconds + public static int $cacheTTL = 3600; // seconds - /** @var string|null */ - public static $cacheStore = null; // default + public static string|null $cacheStore = null; // default public function resolveWithoutCache(...$args): Tenant { diff --git a/src/Tenancy.php b/src/Tenancy.php index ba151f22..c0f655c2 100644 --- a/src/Tenancy.php +++ b/src/Tenancy.php @@ -11,7 +11,7 @@ use Illuminate\Support\Traits\Macroable; use Stancl\Tenancy\Concerns\Debuggable; use Stancl\Tenancy\Contracts\TenancyBootstrapper; use Stancl\Tenancy\Contracts\Tenant; -use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedById; +use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedByIdException; class Tenancy { @@ -38,7 +38,7 @@ class Tenancy $tenant = $this->find($tenantId); if (! $tenant) { - throw new TenantCouldNotBeIdentifiedById($tenantId); + throw new TenantCouldNotBeIdentifiedByIdException($tenantId); } } @@ -62,17 +62,17 @@ class Tenancy public function end(): void { - event(new Events\EndingTenancy($this)); - if (! $this->initialized) { return; } - event(new Events\TenancyEnded($this)); + event(new Events\EndingTenancy($this)); + + $this->tenant = null; $this->initialized = false; - $this->tenant = null; + event(new Events\TenancyEnded($this)); } /** @return TenancyBootstrapper[] */ @@ -131,7 +131,7 @@ class Tenancy * * @param Tenant[]|\Traversable|string[]|null $tenants */ - public function runForMultiple($tenants, callable $callback): void + public function runForMultiple($tenants, Closure $callback): void { // Convert null to all tenants $tenants = is_null($tenants) ? $this->model()->cursor() : $tenants; diff --git a/src/TenantDatabaseManagers/MicrosoftSQLDatabaseManager.php b/src/TenantDatabaseManagers/MicrosoftSQLDatabaseManager.php index 0bc34623..044c35b6 100644 --- a/src/TenantDatabaseManagers/MicrosoftSQLDatabaseManager.php +++ b/src/TenantDatabaseManagers/MicrosoftSQLDatabaseManager.php @@ -2,6 +2,8 @@ declare(strict_types=1); +// todo likely move all of these classes to Database\ + namespace Stancl\Tenancy\TenantDatabaseManagers; use Illuminate\Database\Connection; @@ -12,10 +14,9 @@ use Stancl\Tenancy\Exceptions\NoConnectionSetException; class MicrosoftSQLDatabaseManager implements TenantDatabaseManager { - /** @var string */ - protected $connection; + protected string $connection; // todo docblock, in all of these classes - protected function database(): Connection + protected function database(): Connection // todo consider abstracting this method & setConnection() into a base class { if ($this->connection === null) { throw new NoConnectionSetException(static::class); @@ -33,7 +34,7 @@ class MicrosoftSQLDatabaseManager implements TenantDatabaseManager { $database = $tenant->database()->getName(); $charset = $this->database()->getConfig('charset'); - $collation = $this->database()->getConfig('collation'); + $collation = $this->database()->getConfig('collation'); // todo check why these are not used return $this->database()->statement("CREATE DATABASE [{$database}]"); } diff --git a/tests/TenantModelTest.php b/tests/TenantModelTest.php index d50c9b6b..b4fd38f6 100644 --- a/tests/TenantModelTest.php +++ b/tests/TenantModelTest.php @@ -157,23 +157,25 @@ class AnotherTenant extends Model implements Contracts\Tenant return 'id'; } - public function getTenantKey() + public function getTenantKey(): int|string { return $this->getAttribute('id'); } - public function run(callable $callback) + public function run(Closure $callback): mixed { $callback(); } - public function getInternal(string $key) + public function getInternal(string $key): mixed { return $this->$key; } - public function setInternal(string $key, $value) + public function setInternal(string $key, mixed $value): static { $this->$key = $value; + + return $this; } }