mirror of
https://github.com/archtechx/tenancy.git
synced 2026-06-20 22:54:05 +00:00
Added a failing test for determining if a host is a subdomain, then fixed `DomainTenantResolver::isSubdomain()` (similar fix as in #1423) and a related assertion. Previously, while having `tenancy.identification.central_domains` set to e.g. `['site.com']`, the `isSubdomain()` check consider `tenantsite.com` a subdomain because it ends with `site.com`. Now, instead of the `endsWith()` check, the method checks if the passed domain is in the configured central domains. If it is, it returns `false`. Otherwise, loop through all the central domains and check if the passed domain matches any of the central domains prefixed with a dot (e.g. `tenant.site.com` would be considered a subdomain, `tenant.site.com` wouldn't). Because in InitializeTenancyByDomainOrSubdomain, if tenancy fails to initialize using a subdomain (before this PR's changes, e.g. `tenantsite.com` would be considered a subdomain, and `tenantsite` would be used for initializing tenancy), it'll catch the exception and use the whole domain for identification instead, this error will likely never be noticed in real-world usage. So this PR corrects the subdomain detection logic, but the real-world impact of that is negligible. > Note: The subdomain error catching logic in domainOrSubdomain ID MW was added in v4. If we applied this change in v3, it'd fix a real issue where domainOrSubdomain ID MW would just fail at the subdomain initialization, without attempting domain initialization after the failure. --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Samuel Štancl <samuel@archte.ch>
123 lines
3.4 KiB
PHP
123 lines
3.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use Illuminate\Support\Facades\Route;
|
|
use Stancl\Tenancy\Database\Concerns\HasDomains;
|
|
use Stancl\Tenancy\Exceptions\NotASubdomainException;
|
|
use Stancl\Tenancy\Middleware\InitializeTenancyBySubdomain;
|
|
use Stancl\Tenancy\Database\Models;
|
|
use Stancl\Tenancy\Resolvers\DomainTenantResolver;
|
|
use function Stancl\Tenancy\Tests\pest;
|
|
|
|
beforeEach(function () {
|
|
// Global state cleanup after some tests
|
|
InitializeTenancyBySubdomain::$onFail = null;
|
|
|
|
Route::group([
|
|
'middleware' => InitializeTenancyBySubdomain::class,
|
|
], function () {
|
|
Route::get('/foo/{a}/{b}', function ($a, $b) {
|
|
return "$a + $b";
|
|
});
|
|
});
|
|
|
|
config(['tenancy.models.tenant' => SubdomainTenant::class]);
|
|
});
|
|
|
|
test('tenant can be identified by subdomain', function () {
|
|
$tenant = SubdomainTenant::create([
|
|
'id' => 'acme',
|
|
]);
|
|
|
|
$tenant->domains()->create([
|
|
'domain' => 'foo',
|
|
]);
|
|
|
|
expect(tenancy()->initialized)->toBeFalse();
|
|
|
|
pest()
|
|
->get('http://foo.localhost/foo/abc/xyz')
|
|
->assertSee('abc + xyz');
|
|
|
|
expect(tenancy()->initialized)->toBeTrue();
|
|
expect(tenant('id'))->toBe('acme');
|
|
});
|
|
|
|
test('onfail logic can be customized', function () {
|
|
InitializeTenancyBySubdomain::$onFail = function () {
|
|
return response('foo');
|
|
};
|
|
|
|
pest()
|
|
->get('http://foo.localhost/foo/abc/xyz')
|
|
->assertSee('foo');
|
|
});
|
|
|
|
test('archte.ch is not a valid subdomain', function () {
|
|
pest()->expectException(NotASubdomainException::class);
|
|
|
|
// This gets routed to the app, but with a request domain of 'archte.ch'
|
|
$this
|
|
->withoutExceptionHandling()
|
|
->get('http://archte.ch/foo/abc/xyz');
|
|
});
|
|
|
|
test('ip address is not a valid subdomain', function () {
|
|
pest()->expectException(NotASubdomainException::class);
|
|
|
|
$this
|
|
->withoutExceptionHandling()
|
|
->get('http://127.0.0.2/foo/abc/xyz');
|
|
});
|
|
|
|
test('oninvalidsubdomain logic can be customized', function () {
|
|
// in this case, we need to return a response instance
|
|
// since a string would be treated as the subdomain
|
|
InitializeTenancyBySubdomain::$onFail = function ($e) {
|
|
if ($e instanceof NotASubdomainException) {
|
|
return response('foo custom invalid subdomain handler');
|
|
}
|
|
|
|
throw $e;
|
|
};
|
|
|
|
$this
|
|
->withoutExceptionHandling()
|
|
->get('http://127.0.0.2/foo/abc/xyz')
|
|
->assertSee('foo custom invalid subdomain handler');
|
|
});
|
|
|
|
test('we cant use a subdomain that doesnt belong to our central domains', function () {
|
|
config(['tenancy.identification.central_domains' => [
|
|
'127.0.0.1',
|
|
// not 'localhost'
|
|
]]);
|
|
|
|
$tenant = SubdomainTenant::create([
|
|
'id' => 'acme',
|
|
]);
|
|
|
|
$tenant->domains()->create([
|
|
'domain' => 'foo',
|
|
]);
|
|
|
|
pest()->expectException(NotASubdomainException::class);
|
|
|
|
$this
|
|
->withoutExceptionHandling()
|
|
->get('http://foo.localhost/foo/abc/xyz');
|
|
});
|
|
|
|
test('domain resolver correctly determines if string is a subdomain', function() {
|
|
config(['tenancy.identification.central_domains' => ['site.com', 'blog.site.com']]);
|
|
|
|
expect(DomainTenantResolver::isSubdomain('blog.site.com'))->toBeFalse();
|
|
expect(DomainTenantResolver::isSubdomain('tenant.site.com'))->toBeTrue();
|
|
expect(DomainTenantResolver::isSubdomain('tenantsite.com'))->toBeFalse();
|
|
});
|
|
|
|
class SubdomainTenant extends Models\Tenant
|
|
{
|
|
use HasDomains;
|
|
}
|