From d20056b8044fce6cc486bd6f11e81cf781616696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Sat, 1 Aug 2020 15:07:10 +0200 Subject: [PATCH] Fix subdomain middleware --- .../InitializeTenancyBySubdomain.php | 35 ++++++++++--------- tests/SubdomainTest.php | 32 +++++++++++++++-- 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/src/Middleware/InitializeTenancyBySubdomain.php b/src/Middleware/InitializeTenancyBySubdomain.php index 413ae85d..a5c6cee2 100644 --- a/src/Middleware/InitializeTenancyBySubdomain.php +++ b/src/Middleware/InitializeTenancyBySubdomain.php @@ -5,15 +5,13 @@ declare(strict_types=1); namespace Stancl\Tenancy\Middleware; use Closure; +use Exception; use Illuminate\Http\Response; use Illuminate\Support\Str; use Stancl\Tenancy\Exceptions\NotASubdomainException; class InitializeTenancyBySubdomain extends InitializeTenancyByDomain { - /** @var callable|null */ - public static $onInvalidSubdomain; - /** * The index of the subdomain fragment in the hostname * split by `.`. 0 for first fragment, 1 if you prefix @@ -37,19 +35,27 @@ class InitializeTenancyBySubdomain extends InitializeTenancyByDomain { $subdomain = $this->makeSubdomain($request->getHost()); - // If a non-string, like a Response instance was returned - // from makeSubdomain() - due to NotASubDomainException - // being thrown, we abort by returning the value now. - if (! is_string($subdomain)) { + if (is_object($subdomain) && $subdomain instanceof Exception) { + $onFail = static::$onFail ?? function ($e) { + throw $e; + }; + + return $onFail($subdomain, $request, $next); + } + + // If a Response instance was returned, we return it immediately. + if (is_object($subdomain) && $subdomain instanceof Response) { return $subdomain; } return $this->initializeTenancy( - $request, $next, $subdomain + $request, + $next, + $subdomain ); } - /** @return string|Response|mixed */ + /** @return string|Response|Exception|mixed */ protected function makeSubdomain(string $hostname) { $parts = explode('.', $hostname); @@ -58,15 +64,12 @@ class InitializeTenancyBySubdomain extends InitializeTenancyByDomain $isIpAddress = count(array_filter($parts, 'is_numeric')) === count($parts); // If we're on localhost or an IP address, then we're not visiting a subdomain. + $isACentralDomain = in_array($hostname, config('tenancy.central_domains'), true); $notADomain = $isLocalhost || $isIpAddress; - $thirdPartyDomain = ! Str::endsWith($hostname, config('tenancy.central_domains')); + $thirdPartyDomain = !Str::endsWith($hostname, config('tenancy.central_domains')); - if ($notADomain || $thirdPartyDomain) { - $handle = static::$onInvalidSubdomain ?? function ($e) { - throw $e; - }; - - return $handle(new NotASubdomainException($hostname)); + if ($isACentralDomain || $notADomain || $thirdPartyDomain) { + return new NotASubdomainException($hostname); } return $parts[static::$subdomainIndex]; diff --git a/tests/SubdomainTest.php b/tests/SubdomainTest.php index 1ee1a20b..17fbc1b3 100644 --- a/tests/SubdomainTest.php +++ b/tests/SubdomainTest.php @@ -17,7 +17,7 @@ class SubdomainTest extends TestCase parent::setUp(); // Global state cleanup after some tests - InitializeTenancyBySubdomain::$onInvalidSubdomain = null; + InitializeTenancyBySubdomain::$onFail = null; Route::group([ 'middleware' => InitializeTenancyBySubdomain::class, @@ -88,8 +88,12 @@ class SubdomainTest extends TestCase { // in this case, we need to return a response instance // since a string would be treated as the subdomain - InitializeTenancyBySubdomain::$onInvalidSubdomain = function () { - return response('foo custom invalid subdomain handler'); + InitializeTenancyBySubdomain::$onFail = function ($e) { + if ($e instanceof NotASubdomainException) { + return response('foo custom invalid subdomain handler'); + } + + throw $e; }; $this @@ -120,6 +124,28 @@ class SubdomainTest extends TestCase ->withoutExceptionHandling() ->get('http://foo.localhost/foo/abc/xyz'); } + + /** @test */ + public function central_domain_is_not_a_subdomain() + { + config(['tenancy.central_domains' => [ + 'localhost', + ]]); + + $tenant = SubdomainTenant::create([ + 'id' => 'acme', + ]); + + $tenant->domains()->create([ + 'domain' => 'acme', + ]); + + $this->expectException(NotASubdomainException::class); + + $this + ->withoutExceptionHandling() + ->get('http://localhost/foo/abc/xyz'); + } } class SubdomainTenant extends Models\Tenant