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

Fix subdomain middleware

This commit is contained in:
Samuel Štancl 2020-08-01 15:07:10 +02:00
parent 94abd732ae
commit d20056b804
2 changed files with 48 additions and 19 deletions

View file

@ -5,15 +5,13 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Middleware; namespace Stancl\Tenancy\Middleware;
use Closure; use Closure;
use Exception;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Stancl\Tenancy\Exceptions\NotASubdomainException; use Stancl\Tenancy\Exceptions\NotASubdomainException;
class InitializeTenancyBySubdomain extends InitializeTenancyByDomain class InitializeTenancyBySubdomain extends InitializeTenancyByDomain
{ {
/** @var callable|null */
public static $onInvalidSubdomain;
/** /**
* The index of the subdomain fragment in the hostname * The index of the subdomain fragment in the hostname
* split by `.`. 0 for first fragment, 1 if you prefix * split by `.`. 0 for first fragment, 1 if you prefix
@ -37,19 +35,27 @@ class InitializeTenancyBySubdomain extends InitializeTenancyByDomain
{ {
$subdomain = $this->makeSubdomain($request->getHost()); $subdomain = $this->makeSubdomain($request->getHost());
// If a non-string, like a Response instance was returned if (is_object($subdomain) && $subdomain instanceof Exception) {
// from makeSubdomain() - due to NotASubDomainException $onFail = static::$onFail ?? function ($e) {
// being thrown, we abort by returning the value now. throw $e;
if (! is_string($subdomain)) { };
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 $subdomain;
} }
return $this->initializeTenancy( return $this->initializeTenancy(
$request, $next, $subdomain $request,
$next,
$subdomain
); );
} }
/** @return string|Response|mixed */ /** @return string|Response|Exception|mixed */
protected function makeSubdomain(string $hostname) protected function makeSubdomain(string $hostname)
{ {
$parts = explode('.', $hostname); $parts = explode('.', $hostname);
@ -58,15 +64,12 @@ class InitializeTenancyBySubdomain extends InitializeTenancyByDomain
$isIpAddress = count(array_filter($parts, 'is_numeric')) === count($parts); $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. // 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; $notADomain = $isLocalhost || $isIpAddress;
$thirdPartyDomain = ! Str::endsWith($hostname, config('tenancy.central_domains')); $thirdPartyDomain = !Str::endsWith($hostname, config('tenancy.central_domains'));
if ($notADomain || $thirdPartyDomain) { if ($isACentralDomain || $notADomain || $thirdPartyDomain) {
$handle = static::$onInvalidSubdomain ?? function ($e) { return new NotASubdomainException($hostname);
throw $e;
};
return $handle(new NotASubdomainException($hostname));
} }
return $parts[static::$subdomainIndex]; return $parts[static::$subdomainIndex];

View file

@ -17,7 +17,7 @@ class SubdomainTest extends TestCase
parent::setUp(); parent::setUp();
// Global state cleanup after some tests // Global state cleanup after some tests
InitializeTenancyBySubdomain::$onInvalidSubdomain = null; InitializeTenancyBySubdomain::$onFail = null;
Route::group([ Route::group([
'middleware' => InitializeTenancyBySubdomain::class, 'middleware' => InitializeTenancyBySubdomain::class,
@ -88,8 +88,12 @@ class SubdomainTest extends TestCase
{ {
// in this case, we need to return a response instance // in this case, we need to return a response instance
// since a string would be treated as the subdomain // since a string would be treated as the subdomain
InitializeTenancyBySubdomain::$onInvalidSubdomain = function () { InitializeTenancyBySubdomain::$onFail = function ($e) {
return response('foo custom invalid subdomain handler'); if ($e instanceof NotASubdomainException) {
return response('foo custom invalid subdomain handler');
}
throw $e;
}; };
$this $this
@ -120,6 +124,28 @@ class SubdomainTest extends TestCase
->withoutExceptionHandling() ->withoutExceptionHandling()
->get('http://foo.localhost/foo/abc/xyz'); ->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 class SubdomainTenant extends Models\Tenant