diff --git a/assets/TenancyServiceProvider.stub.php b/assets/TenancyServiceProvider.stub.php index 5964514e..006cb190 100644 --- a/assets/TenancyServiceProvider.stub.php +++ b/assets/TenancyServiceProvider.stub.php @@ -193,12 +193,11 @@ class TenancyServiceProvider extends ServiceProvider * return RouteFacade::get('/livewire/livewire.js', $handle)->middleware(['universal']); * }); */ - if (InitializeTenancyByRequestData::inGlobalStack()) { TenancyUrlGenerator::$prefixRouteNames = false; } - if (InitializeTenancyByPath::inGlobalStack()) { + if (tenancy()->globalStackHasMiddleware(config('tenancy.identification.path_identification_middleware'))) { TenancyUrlGenerator::$prefixRouteNames = true; /** @var CloneRoutesAsTenant $cloneRoutes */ diff --git a/assets/config.php b/assets/config.php index 4547bed6..c146b61a 100644 --- a/assets/config.php +++ b/assets/config.php @@ -87,7 +87,17 @@ return [ Middleware\InitializeTenancyByDomainOrSubdomain::class, ], - // todo@lukas docblock + /** + * Identification middleware tenancy recognizes as path identification middleware. + * + * This is used during determining whether whether a path identification is used + * during operations specific to path identification, e.g. forgetting the tenant parameter in ForgetTenantParameter. + * + * If you're using a custom path identification middleware, add it here. + * + * @see \Stancl\Tenancy\Actions\CloneRoutesAsTenant + * @see \Stancl\Tenancy\Listeners\ForgetTenantParameter + */ 'path_identification_middleware' => [ Middleware\InitializeTenancyByPath::class, ], diff --git a/assets/routes.php b/assets/routes.php index d616b665..22fc2b38 100644 --- a/assets/routes.php +++ b/assets/routes.php @@ -3,14 +3,14 @@ declare(strict_types=1); use Illuminate\Support\Facades\Route; -use Stancl\Tenancy\PathIdentificationManager; use Stancl\Tenancy\Controllers\TenantAssetController; +use Stancl\Tenancy\Resolvers\PathTenantResolver; Route::get('/tenancy/assets/{path?}', TenantAssetController::class) ->where('path', '(.*)') ->name('stancl.tenancy.asset'); -Route::prefix('/{' . PathIdentificationManager::getTenantParameterName() . '}/')->get('/tenancy/assets/{path?}', TenantAssetController::class) +Route::prefix('/{' . PathTenantResolver::tenantParameterName() . '}/')->get('/tenancy/assets/{path?}', TenantAssetController::class) ->where('path', '(.*)') ->middleware('tenant') - ->name(PathIdentificationManager::getTenantRouteNamePrefix() . 'stancl.tenancy.asset'); + ->name(PathTenantResolver::tenantRouteNamePrefix() . 'stancl.tenancy.asset'); diff --git a/src/Actions/CloneRoutesAsTenant.php b/src/Actions/CloneRoutesAsTenant.php index 08cb3966..10069e19 100644 --- a/src/Actions/CloneRoutesAsTenant.php +++ b/src/Actions/CloneRoutesAsTenant.php @@ -10,7 +10,7 @@ use Illuminate\Routing\Router; use Illuminate\Support\Collection; use Illuminate\Support\Str; use Stancl\Tenancy\Enums\RouteMode; -use Stancl\Tenancy\PathIdentificationManager; +use Stancl\Tenancy\Resolvers\PathTenantResolver; /** * The CloneRoutesAsTenant action clones @@ -75,7 +75,7 @@ class CloneRoutesAsTenant protected function getRoutesToClone(): Collection { - $tenantParameterName = PathIdentificationManager::getTenantParameterName(); + $tenantParameterName = PathTenantResolver::tenantParameterName(); /** * Clone all routes that: @@ -95,9 +95,10 @@ class CloneRoutesAsTenant return false; } - $routeHasPathIdentificationMiddleware = PathIdentificationManager::pathIdentificationOnRoute($route); - $pathIdentificationMiddlewareInGlobalStack = PathIdentificationManager::pathIdentificationInGlobalStack(); + $pathIdentificationMiddleware = config('tenancy.identification.path_identification_middleware'); + $routeHasPathIdentificationMiddleware = tenancy()->routeHasMiddleware($route, $pathIdentificationMiddleware); $routeHasNonPathIdentificationMiddleware = tenancy()->routeHasIdentificationMiddleware($route) && ! $routeHasPathIdentificationMiddleware; + $pathIdentificationMiddlewareInGlobalStack = tenancy()->globalStackHasMiddleware($pathIdentificationMiddleware); /** * The route should get cloned if: @@ -163,7 +164,7 @@ class CloneRoutesAsTenant ->filter(fn (string $middleware) => ! in_array($middleware, ['universal', 'clone'])) ->toArray(); - $tenantRouteNamePrefix = PathIdentificationManager::getTenantRouteNamePrefix(); + $tenantRouteNamePrefix = PathTenantResolver::tenantRouteNamePrefix(); // Make sure the route name has the tenant route name prefix $newRouteNamePrefix = $route->getName() @@ -173,7 +174,7 @@ class CloneRoutesAsTenant return $action ->put('as', $newRouteNamePrefix) ->put('middleware', $newRouteMiddleware) - ->put('prefix', '/{' . PathIdentificationManager::getTenantParameterName() . '}/' . $route->getPrefix()); + ->put('prefix', '/{' . PathTenantResolver::tenantParameterName() . '}/' . $route->getPrefix()); })->toArray(); /** @var Route $newRoute */ diff --git a/src/Concerns/DealsWithRouteContexts.php b/src/Concerns/DealsWithRouteContexts.php index 75b56fbe..4e89b21f 100644 --- a/src/Concerns/DealsWithRouteContexts.php +++ b/src/Concerns/DealsWithRouteContexts.php @@ -14,6 +14,9 @@ use Illuminate\Support\Arr; use Illuminate\Support\Facades\Route as RouteFacade; use Stancl\Tenancy\Enums\RouteMode; +/** + * @mixin \Stancl\Tenancy\Tenancy + */ trait DealsWithRouteContexts { /** @@ -107,46 +110,14 @@ trait DealsWithRouteContexts } /** - * Check if the passed route has the passed middleware - * three layers deep – explained in the annotation of getRouteMiddleware(). + * Checks whether any of the passed middleware are present in the route's middleware stack. */ - public static function routeHasMiddleware(Route $route, string $middleware): bool + public static function routeHasMiddleware(Route $route, string|array $middlewares): bool { - return in_array($middleware, static::getRouteMiddleware($route)); - } + $routeMiddleware = static::getRouteMiddleware($route); - public function routeIdentificationMiddleware(Route $route): string|null - { - foreach (static::getRouteMiddleware($route) as $routeMiddleware) { - if (in_array($routeMiddleware, static::middleware())) { - return $routeMiddleware; - } - } - - return null; - } - - public static function kernelIdentificationMiddleware(): string|null - { - /** @var Kernel $kernel */ - $kernel = app(Kernel::class); - - foreach (static::middleware() as $identificationMiddleware) { - if ($kernel->hasMiddleware($identificationMiddleware)) { - return $identificationMiddleware; - } - } - - return null; - } - - /** - * Check if a route has identification middleware. - */ - public static function routeHasIdentificationMiddleware(Route $route): bool - { - foreach (static::getRouteMiddleware($route) as $middleware) { - if (in_array($middleware, static::middleware())) { + foreach (Arr::wrap($middlewares) as $middleware) { + if (in_array($middleware, $routeMiddleware)) { return true; } } @@ -155,22 +126,34 @@ trait DealsWithRouteContexts } /** - * Check if route uses kernel identification (identification middleare is in the global stack and the route doesn't have route-level identification middleware). + * Check if a route has identification middleware. */ - public static function routeUsesKernelIdentification(Route $route): bool + public static function routeHasIdentificationMiddleware(Route $route): bool { - return ! static::routeHasIdentificationMiddleware($route) && static::kernelIdentificationMiddleware(); + return static::routeHasMiddleware($route, static::middleware()); } /** - * Check if a route uses domain identification. + * Check if route uses kernel identification (identification middleware is in the global stack and the route doesn't have route-level identification middleware). */ - public static function routeHasDomainIdentificationMiddleware(Route $route): bool + public static function routeUsesKernelIdentification(Route $route): bool { - $routeMiddleware = static::getRouteMiddleware($route); + return ! static::routeHasIdentificationMiddleware($route) && + tenancy()->globalStackHasMiddleware(static::middleware()); + } - foreach (config('tenancy.identification.domain_identification_middleware') as $middleware) { - if (in_array($middleware, $routeMiddleware)) { + /** + * Checks whether any of the passed middleware are present in the global middleware stack. + * + * @param class-string|array $identificationMiddleware + */ + public static function globalStackHasMiddleware(string|array $identificationMiddleware = []): bool + { + /** @var Kernel $kernel */ + $kernel = app(Kernel::class); + + foreach (Arr::wrap($identificationMiddleware) as $middleware) { + if ($kernel->hasMiddleware($middleware)) { return true; } } diff --git a/src/Concerns/UsableWithEarlyIdentification.php b/src/Concerns/UsableWithEarlyIdentification.php index 4b3cc8a2..2e2b01e7 100644 --- a/src/Concerns/UsableWithEarlyIdentification.php +++ b/src/Concerns/UsableWithEarlyIdentification.php @@ -4,12 +4,10 @@ declare(strict_types=1); namespace Stancl\Tenancy\Concerns; -use Illuminate\Contracts\Http\Kernel; use Illuminate\Http\Request; use Illuminate\Routing\Route; use Stancl\Tenancy\Enums\Context; use Stancl\Tenancy\Enums\RouteMode; -use Stancl\Tenancy\Exceptions\MiddlewareNotUsableWithUniversalRoutesException; use Stancl\Tenancy\Middleware\IdentificationMiddleware; use Stancl\Tenancy\Middleware\PreventAccessFromUnwantedDomains; @@ -27,6 +25,19 @@ use Stancl\Tenancy\Middleware\PreventAccessFromUnwantedDomains; */ trait UsableWithEarlyIdentification { + /** + * Determine if the tenant is present in the incoming request. + * + * Because universal routes can be in any context (central/tenant), + * we use this to determine the context. We can't just check for + * the route's middleware to determine the route's context. + * + * For example, route '/foo' has the 'universal' and InitializeTenancyByRequestData middleware. + * When visiting the route, we should determine the context by the presence of the tenant payload. + * The context is tenant if the tenant parameter is present (e.g. '?tenant=foo'), otherwise the context is central. + */ + abstract public function requestHasTenant(Request $request): bool; + /** * Skip middleware if the route is universal and uses path identification or if the route is universal and the context should be central. * Universal routes using path identification should get cloned using CloneRoutesAsTenant. @@ -36,9 +47,6 @@ trait UsableWithEarlyIdentification protected function shouldBeSkipped(Route $route): bool { if (tenancy()->routeIsUniversal($route) && $this instanceof IdentificationMiddleware) { - /** @phpstan-ignore-next-line */ - throw_unless($this instanceof UsableWithUniversalRoutes, MiddlewareNotUsableWithUniversalRoutesException::class); - return $this->determineUniversalRouteContextFromRequest(request()) === Context::CENTRAL; } @@ -51,10 +59,11 @@ trait UsableWithEarlyIdentification // Now that we're sure the MW isn't used in the global MW stack, we determine whether to skip it if ($this instanceof PreventAccessFromUnwantedDomains) { // Skip access prevention if the route directly uses a non-domain identification middleware - return tenancy()->routeHasIdentificationMiddleware($route) && ! tenancy()->routeHasDomainIdentificationMiddleware($route); + return tenancy()->routeHasIdentificationMiddleware($route) && + ! tenancy()->routeHasMiddleware($route, config('tenancy.identification.domain_identification_middleware')); } - return $this->shouldIdentificationMiddlewareBeSkipped($route); + return $this->shouldSkipIdentificationMiddleware($route); } protected function determineUniversalRouteContextFromRequest(Request $request): Context @@ -66,7 +75,6 @@ trait UsableWithEarlyIdentification $globalIdentificationUsed = ! tenancy()->routeHasIdentificationMiddleware($route) && static::inGlobalStack(); $routeLevelIdentificationUsed = tenancy()->routeHasMiddleware($route, static::class); - /** @var UsableWithUniversalRoutes $this */ if (($globalIdentificationUsed || $routeLevelIdentificationUsed) && $this->requestHasTenant($request)) { return Context::TENANT; } @@ -74,9 +82,9 @@ trait UsableWithEarlyIdentification return Context::CENTRAL; } - protected function shouldIdentificationMiddlewareBeSkipped(Route $route): bool + protected function shouldSkipIdentificationMiddleware(Route $route): bool { - if (! static::inGlobalStack()) { + if (! tenancy()->globalStackHasMiddleware(static::class)) { return false; } @@ -109,6 +117,6 @@ trait UsableWithEarlyIdentification public static function inGlobalStack(): bool { - return app(Kernel::class)->hasMiddleware(static::class); + return tenancy()->globalStackHasMiddleware(static::class); } } diff --git a/src/Concerns/UsableWithUniversalRoutes.php b/src/Concerns/UsableWithUniversalRoutes.php deleted file mode 100644 index fca6ca1b..00000000 --- a/src/Concerns/UsableWithUniversalRoutes.php +++ /dev/null @@ -1,26 +0,0 @@ -routeHasIdentificationMiddleware($event->route); + $pathIdentificationInGlobalStack = tenancy()->globalStackHasMiddleware(config('tenancy.identification.path_identification_middleware')); + $kernelPathIdentificationUsed = $pathIdentificationInGlobalStack && ! tenancy()->routeHasIdentificationMiddleware($event->route); $routeMode = tenancy()->getRouteMode($event->route); - $routeModeIsTenantOrUniversal = $routeMode === RouteMode::TENANT || ($routeMode === RouteMode::UNIVERSAL && $event->route->hasParameter(PathIdentificationManager::getTenantParameterName())); + $routeModeIsTenantOrUniversal = $routeMode === RouteMode::TENANT || ($routeMode === RouteMode::UNIVERSAL && $event->route->hasParameter(PathTenantResolver::tenantParameterName())); if ($kernelPathIdentificationUsed && $routeModeIsTenantOrUniversal) { - $event->route->forgetParameter(PathIdentificationManager::getTenantParameterName()); + $event->route->forgetParameter(PathTenantResolver::tenantParameterName()); } } } diff --git a/src/Middleware/InitializeTenancyByDomain.php b/src/Middleware/InitializeTenancyByDomain.php index 4b6f2195..b9e049a4 100644 --- a/src/Middleware/InitializeTenancyByDomain.php +++ b/src/Middleware/InitializeTenancyByDomain.php @@ -7,11 +7,10 @@ namespace Stancl\Tenancy\Middleware; use Closure; use Illuminate\Http\Request; use Stancl\Tenancy\Concerns\UsableWithEarlyIdentification; -use Stancl\Tenancy\Concerns\UsableWithUniversalRoutes; use Stancl\Tenancy\Resolvers\DomainTenantResolver; use Stancl\Tenancy\Tenancy; -class InitializeTenancyByDomain extends IdentificationMiddleware implements UsableWithUniversalRoutes +class InitializeTenancyByDomain extends IdentificationMiddleware { use UsableWithEarlyIdentification; diff --git a/src/Middleware/InitializeTenancyByPath.php b/src/Middleware/InitializeTenancyByPath.php index c7772eff..bcf27dd2 100644 --- a/src/Middleware/InitializeTenancyByPath.php +++ b/src/Middleware/InitializeTenancyByPath.php @@ -7,16 +7,14 @@ namespace Stancl\Tenancy\Middleware; use Closure; use Illuminate\Http\Request; use Stancl\Tenancy\Concerns\UsableWithEarlyIdentification; -use Stancl\Tenancy\Concerns\UsableWithUniversalRoutes; use Stancl\Tenancy\Exceptions\RouteIsMissingTenantParameterException; -use Stancl\Tenancy\PathIdentificationManager; use Stancl\Tenancy\Resolvers\PathTenantResolver; use Stancl\Tenancy\Tenancy; /** * @see Stancl\Tenancy\Listeners\ForgetTenantParameter */ -class InitializeTenancyByPath extends IdentificationMiddleware implements UsableWithUniversalRoutes +class InitializeTenancyByPath extends IdentificationMiddleware { use UsableWithEarlyIdentification; @@ -55,6 +53,6 @@ class InitializeTenancyByPath extends IdentificationMiddleware implements Usable */ public function requestHasTenant(Request $request): bool { - return tenancy()->getRoute($request)->hasParameter(PathIdentificationManager::getTenantParameterName()); + return tenancy()->getRoute($request)->hasParameter(PathTenantResolver::tenantParameterName()); } } diff --git a/src/Middleware/InitializeTenancyByRequestData.php b/src/Middleware/InitializeTenancyByRequestData.php index 09524b44..f36cadfd 100644 --- a/src/Middleware/InitializeTenancyByRequestData.php +++ b/src/Middleware/InitializeTenancyByRequestData.php @@ -7,13 +7,12 @@ namespace Stancl\Tenancy\Middleware; use Closure; use Illuminate\Http\Request; use Stancl\Tenancy\Concerns\UsableWithEarlyIdentification; -use Stancl\Tenancy\Concerns\UsableWithUniversalRoutes; use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedByRequestDataException; use Stancl\Tenancy\Overrides\TenancyUrlGenerator; use Stancl\Tenancy\Resolvers\RequestDataTenantResolver; use Stancl\Tenancy\Tenancy; -class InitializeTenancyByRequestData extends IdentificationMiddleware implements UsableWithUniversalRoutes +class InitializeTenancyByRequestData extends IdentificationMiddleware { use UsableWithEarlyIdentification; diff --git a/src/Middleware/PreventAccessFromUnwantedDomains.php b/src/Middleware/PreventAccessFromUnwantedDomains.php index a54bfba7..05e0dbaa 100644 --- a/src/Middleware/PreventAccessFromUnwantedDomains.php +++ b/src/Middleware/PreventAccessFromUnwantedDomains.php @@ -67,4 +67,10 @@ class PreventAccessFromUnwantedDomains { return in_array($request->getHost(), config('tenancy.identification.central_domains'), true); } + + // todo@samuel + public function requestHasTenant(Request $request): bool + { + return false; + } } diff --git a/src/Overrides/TenancyUrlGenerator.php b/src/Overrides/TenancyUrlGenerator.php index bc3a1d24..af307021 100644 --- a/src/Overrides/TenancyUrlGenerator.php +++ b/src/Overrides/TenancyUrlGenerator.php @@ -6,7 +6,7 @@ namespace Stancl\Tenancy\Overrides; use Illuminate\Routing\UrlGenerator; use Illuminate\Support\Arr; -use Stancl\Tenancy\PathIdentificationManager; +use Stancl\Tenancy\Resolvers\PathTenantResolver; /** * This class is used in place of the default UrlGenerator when UrlGeneratorBootstrapper is enabled. @@ -104,7 +104,7 @@ class TenancyUrlGenerator extends UrlGenerator */ protected function prefixRouteName(string $name): string { - $tenantPrefix = PathIdentificationManager::getTenantRouteNamePrefix(); + $tenantPrefix = PathTenantResolver::tenantRouteNamePrefix(); if (static::$prefixRouteNames && ! str($name)->startsWith($tenantPrefix)) { $name = str($name)->after($tenantPrefix)->prepend($tenantPrefix)->toString(); @@ -118,6 +118,6 @@ class TenancyUrlGenerator extends UrlGenerator */ protected function addTenantParameter(array $parameters): array { - return tenant() && static::$passTenantParameterToRoutes ? array_merge($parameters, [PathIdentificationManager::getTenantParameterName() => tenant()->getTenantKey()]) : $parameters; + return tenant() && static::$passTenantParameterToRoutes ? array_merge($parameters, [PathTenantResolver::tenantParameterName() => tenant()->getTenantKey()]) : $parameters; } } diff --git a/src/PathIdentificationManager.php b/src/PathIdentificationManager.php deleted file mode 100644 index 37f7df69..00000000 --- a/src/PathIdentificationManager.php +++ /dev/null @@ -1,60 +0,0 @@ - tenancy()->routeHasMiddleware($route, $middleware)); - } - - public static function pathIdentificationInGlobalStack(): bool - { - return static::checkPathIdentificationMiddleware(fn ($middleware) => $middleware::inGlobalStack()); - } - - protected static function checkPathIdentificationMiddleware(Closure $closure): bool - { - foreach (static::getPathIdentificationMiddleware() as $middleware) { - if ($closure($middleware)) { - return true; - } - } - - return false; - } - - protected static function getPathIdentificationMiddleware(): array - { - return config('tenancy.identification.path_identification_middleware'); - } -} diff --git a/src/Resolvers/DomainTenantResolver.php b/src/Resolvers/DomainTenantResolver.php index 61920b14..34fd4c4f 100644 --- a/src/Resolvers/DomainTenantResolver.php +++ b/src/Resolvers/DomainTenantResolver.php @@ -4,15 +4,14 @@ declare(strict_types=1); namespace Stancl\Tenancy\Resolvers; -use Stancl\Tenancy\Tenancy; -use Stancl\Tenancy\Contracts\Domain; -use Stancl\Tenancy\Contracts\Tenant; -use Illuminate\Support\Facades\Schema; -use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; -use Stancl\Tenancy\Contracts\SingleDomainTenant; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\Relation; +use Stancl\Tenancy\Contracts\Domain; +use Stancl\Tenancy\Contracts\SingleDomainTenant; +use Stancl\Tenancy\Contracts\Tenant; use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedOnDomainException; +use Stancl\Tenancy\Tenancy; class DomainTenantResolver extends Contracts\CachedTenantResolver { diff --git a/tests/CachedTenantResolverTest.php b/tests/CachedTenantResolverTest.php index c6197655..0ca9553f 100644 --- a/tests/CachedTenantResolverTest.php +++ b/tests/CachedTenantResolverTest.php @@ -286,7 +286,7 @@ function getResolverArgument(string $resolver, string $tenantKey): string|Route // Make the 'tenant' route parameter the tenant key // Setting the parameter on the $route->parameters property is required // Because $route->setParameter() throws an exception when $route->parameters is not set yet - $route->parameters[PathIdentificationManager::getTenantParameterName()] = $tenantKey; + $route->parameters[PathTenantResolver::tenantParameterName()] = $tenantKey; // Return the route instance with the tenant key as the 'tenant' parameter return $route; diff --git a/tests/RouteMiddlewareTest.php b/tests/RouteMiddlewareTest.php index 9d894dc5..ab0a46be 100644 --- a/tests/RouteMiddlewareTest.php +++ b/tests/RouteMiddlewareTest.php @@ -74,7 +74,7 @@ test('domain identification middleware is configurable', function() { config(['tenancy.identification.domain_identification_middleware' => []]); - expect(tenancy()->routeHasDomainIdentificationMiddleware($route))->toBeFalse(); + expect(tenancy()->routeHasMiddleware($route, config('tenancy.identification.domain_identification_middleware')))->toBeFalse(); // Set domain identification middleware list back to default config(['tenancy.identification.domain_identification_middleware' => [ @@ -83,5 +83,5 @@ test('domain identification middleware is configurable', function() { InitializeTenancyByDomainOrSubdomain::class, ]]); - expect(tenancy()->routeHasDomainIdentificationMiddleware($route))->toBeTrue(); + expect(tenancy()->routeHasMiddleware($route, config('tenancy.identification.domain_identification_middleware')))->toBeTrue(); }); diff --git a/tests/UniversalRouteTest.php b/tests/UniversalRouteTest.php index 59df82b7..16c45a1e 100644 --- a/tests/UniversalRouteTest.php +++ b/tests/UniversalRouteTest.php @@ -21,7 +21,6 @@ use Stancl\Tenancy\Middleware\InitializeTenancyByRequestData; use Stancl\Tenancy\Middleware\PreventAccessFromUnwantedDomains; use Stancl\Tenancy\Middleware\InitializeTenancyByDomainOrSubdomain; use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedOnDomainException; -use Stancl\Tenancy\Exceptions\MiddlewareNotUsableWithUniversalRoutesException; use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedByRequestDataException; test('a route can be universal using domain identification', function (array $routeMiddleware, array $globalMiddleware) { @@ -321,29 +320,6 @@ test('tenant resolver methods return the correct names for configured values', f ['tenant_route_name_prefix', 'prefix'] ]); -test('identification middleware works with universal routes only when it implements MiddlewareUsableWithUniversalRoutes', function () { - $tenantKey = Tenant::create()->getTenantKey(); - $routeAction = fn () => tenancy()->initialized ? $tenantKey : 'Tenancy is not initialized.'; - - // Route with the package's request data identification middleware – implements MiddlewareUsableWithUniversalRoutes - RouteFacade::get('/universal-route', $routeAction)->middleware(['universal', InitializeTenancyByRequestData::class]); - - // Routes with custom request data identification middleware – does not implement MiddlewareUsableWithUniversalRoutes - RouteFacade::get('/custom-mw-universal-route', $routeAction)->middleware(['universal', CustomMiddleware::class]); - RouteFacade::get('/custom-mw-tenant-route', $routeAction)->middleware(['tenant', CustomMiddleware::class]); - - // Ensure the custom identification middleware works with non-universal routes - // This is tested here because this is the only test where the custom MW is used - // No exception is thrown for this request since the route uses the TENANT middleware, not the UNIVERSAL middleware - pest()->get('http://localhost/custom-mw-tenant-route?tenant=' . $tenantKey)->assertOk()->assertSee($tenantKey); - - pest()->get('http://localhost/universal-route')->assertOk(); - pest()->get('http://localhost/universal-route?tenant=' . $tenantKey)->assertOk()->assertSee($tenantKey); - - pest()->expectException(MiddlewareNotUsableWithUniversalRoutesException::class); - $this->withoutExceptionHandling()->get('http://localhost/custom-mw-universal-route'); -}); - foreach ([ 'domain identification types' => [PreventAccessFromUnwantedDomains::class, InitializeTenancyByDomain::class], 'subdomain identification types' => [PreventAccessFromUnwantedDomains::class, InitializeTenancyBySubdomain::class], @@ -370,45 +346,6 @@ foreach ([ ]); } -class CustomMiddleware extends IdentificationMiddleware -{ - use UsableWithEarlyIdentification; - - public static string $header = 'X-Tenant'; - public static string $cookie = 'X-Tenant'; - public static string $queryParameter = 'tenant'; - - public function __construct( - protected Tenancy $tenancy, - protected RequestDataTenantResolver $resolver, - ) { - } - - /** @return \Illuminate\Http\Response|mixed */ - public function handle(Request $request, Closure $next): mixed - { - if ($this->shouldBeSkipped(tenancy()->getRoute($request))) { - // Allow accessing central route in kernel identification - return $next($request); - } - - return $this->initializeTenancy($request, $next, $this->getPayload($request)); - } - - protected function getPayload(Request $request): string|array|null - { - if (static::$header && $request->hasHeader(static::$header)) { - return $request->header(static::$header); - } elseif (static::$queryParameter && $request->has(static::$queryParameter)) { - return $request->get(static::$queryParameter); - } elseif (static::$cookie && $request->hasCookie(static::$cookie)) { - return $request->cookie(static::$cookie); - } - - return null; - } -} - class Controller extends BaseController { public function __invoke()