1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2025-12-13 02:24:03 +00:00

Refactor early identification (#47)

* Make universal route logic part of tbe early ID trait

* Add requstHasTenant to prevent access MW, add todo@samuel

* Delete PathIdentificationManager, move the used methods appropriately

* Correct and refactor code related to the deleted PathIdentificationManager class

* Add docblock

* Fix code style (php-cs-fixer)

* refactor globalStackMiddleware()

* remove todos [ci skip]

* refactor routeMiddleware()

* revert bool assertions

* revert more changes

---------

Co-authored-by: PHP CS Fixer <phpcsfixer@example.com>
Co-authored-by: Samuel Štancl <samuel@archte.ch>
This commit is contained in:
lukinovec 2024-04-22 11:30:58 +02:00 committed by GitHub
parent b70cd0e531
commit 4e51cdbacb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 95 additions and 256 deletions

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -1,26 +0,0 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Concerns;
use Illuminate\Http\Request;
/**
* Identification middleware has to implement this in order to make universal routes work with it,.
*/
interface UsableWithUniversalRoutes
{
/**
* 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.
*/
public function requestHasTenant(Request $request): bool;
}