From cbd3850a8fd72a4085959e779d27a94393892d54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Tue, 15 Oct 2019 20:23:56 +0200 Subject: [PATCH] [2.1.0] Initialize tenancy before executing controller constructors (#169) * Update message about migrations in Install * wip * Apply fixes from StyleCI * string instead of array * Fix globalUrl binding * Simplify if condition in TenantRouteServiceProvider * Apply fixes from StyleCI * Improve PreventAccessFromTenantDomains - look into middleware subgroups * Fix typo * gatherMiddleware() instead of middleware() * Fix tests * Apply fixes from StyleCI * Update install command * Apply fixes from StyleCI * Add the PreventAccess MW to tenant routes by default --- src/Commands/Install.php | 17 ++++++++------ src/Middleware/InitializeTenancy.php | 14 ++++++++---- .../PreventAccessFromTenantDomains.php | 22 ++++++++++++++++++- src/TenancyServiceProvider.php | 21 ++++++++++++++---- src/TenantRouteServiceProvider.php | 3 +-- src/routes.php | 8 ++++--- tests/CommandsTest.php | 2 +- tests/Etc/modifiedHttpKernel.stub | 1 + 8 files changed, 66 insertions(+), 22 deletions(-) diff --git a/src/Commands/Install.php b/src/Commands/Install.php index 0d3bd834..8217b904 100644 --- a/src/Commands/Install.php +++ b/src/Commands/Install.php @@ -47,6 +47,9 @@ class Install extends Command $newKernel = str_replace("'web' => [", "'web' => [ \Stancl\Tenancy\Middleware\PreventAccessFromTenantDomains::class,", $newKernel); + $newKernel = str_replace("'api' => [", "'api' => [ + \Stancl\Tenancy\Middleware\PreventAccessFromTenantDomains::class,", $newKernel); + file_put_contents(app_path('Http/Kernel.php'), $newKernel); $this->info('✔️ Set middleware priority'); @@ -59,9 +62,9 @@ class Install extends Command | Tenant Routes |-------------------------------------------------------------------------- | -| Here is where you can register tenant routes for your application. These -| routes are loaded by the TenantRouteServiceProvider within a group -| which contains the \"InitializeTenancy\" middleware. Good luck! +| Here you can register the tenant routes for your application. +| These routes are loaded by the TenantRouteServiceProvider +| with the tenancy and web middleware groups. Good luck! | */ @@ -73,11 +76,11 @@ Route::get('/app', function () { $this->info('✔️ Created routes/tenant.php'); $this->line(''); - $this->line("This package lets you store data about tenants either in Redis or in a relational database like MySQL. If you're going to use the database storage, you need to create a tenants table."); - if ($this->confirm('Do you want to publish the default database migrations?', true)) { + $this->line('This package lets you store data about tenants either in Redis or in a relational database like MySQL. To store data about tenants in a relational database, you need a few database tables.'); + if ($this->confirm('Do you wish to publish the migrations that create these tables?', true)) { $this->callSilent('vendor:publish', [ - '--provider' => 'Stancl\Tenancy\TenancyServiceProvider', - '--tag' => 'migrations', + '--provider' => 'Stancl\Tenancy\TenancyServiceProvider', + '--tag' => 'migrations', ]); $this->info('✔️ Created migrations. Remember to run [php artisan migrate]!'); } diff --git a/src/Middleware/InitializeTenancy.php b/src/Middleware/InitializeTenancy.php index 3bc383ba..ff6fe2e4 100644 --- a/src/Middleware/InitializeTenancy.php +++ b/src/Middleware/InitializeTenancy.php @@ -28,10 +28,16 @@ class InitializeTenancy */ public function handle($request, Closure $next) { - try { - tenancy()->init($request->getHost()); - } catch (TenantCouldNotBeIdentifiedException $e) { - ($this->onFail)($e); + if (tenancy()->initialized) { + return $next($request); + } + + if (! in_array($request->getHost(), config('tenancy.exempt_domains', []), true)) { + try { + tenancy()->init($request->getHost()); + } catch (TenantCouldNotBeIdentifiedException $e) { + ($this->onFail)($e); + } } return $next($request); diff --git a/src/Middleware/PreventAccessFromTenantDomains.php b/src/Middleware/PreventAccessFromTenantDomains.php index ab822175..fb797d4c 100644 --- a/src/Middleware/PreventAccessFromTenantDomains.php +++ b/src/Middleware/PreventAccessFromTenantDomains.php @@ -5,6 +5,8 @@ declare(strict_types=1); namespace Stancl\Tenancy\Middleware; use Closure; +use Illuminate\Routing\Route; +use Illuminate\Support\Facades\Route as Router; /** * Prevent access from tenant domains to central routes and vice versa. @@ -25,7 +27,7 @@ class PreventAccessFromTenantDomains $isExemptDomain = in_array($request->getHost(), config('tenancy.exempt_domains')); $isTenantDomain = ! $isExemptDomain; - $isTenantRoute = in_array('tenancy', $request->route()->middleware()); + $isTenantRoute = $this->isTenantRoute($request->route()); if ($isTenantDomain && ! $isTenantRoute) { // accessing web routes from tenant domains return redirect(config('tenancy.home_url')); @@ -37,4 +39,22 @@ class PreventAccessFromTenantDomains return $next($request); } + + public function isTenantRoute(Route $route): bool + { + if (in_array('tenancy', $route->middleware(), true)) { + return true; + } + + // Loop one level deep and check if the route's middleware + // groups have a `tenancy` middleware group inside them + $middlewareGroups = Router::getMiddlewareGroups(); + foreach ($route->gatherMiddleware() as $middleware) { + if (isset($middlewareGroups[$middleware]) && in_array('tenancy', $middlewareGroups[$middleware], true)) { + return true; + } + } + + return false; + } } diff --git a/src/TenancyServiceProvider.php b/src/TenancyServiceProvider.php index 7f8e86ad..9687fed6 100644 --- a/src/TenancyServiceProvider.php +++ b/src/TenancyServiceProvider.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Stancl\Tenancy; use Illuminate\Cache\CacheManager; +use Illuminate\Contracts\Http\Kernel; use Illuminate\Support\Facades\Route; use Illuminate\Support\ServiceProvider; use Stancl\Tenancy\TenancyBootstrappers\FilesystemTenancyBootstrapper; @@ -77,15 +78,27 @@ class TenancyServiceProvider extends ServiceProvider __DIR__ . '/../assets/migrations/' => database_path('migrations'), ], 'migrations'); - $this->loadRoutesFrom(__DIR__ . '/routes.php'); + $this->app->make(Kernel::class)->prependMiddleware(Middleware\InitializeTenancy::class); + /* + * Since tenancy is initialized in the global middleware stack, this + * middleware group acts mostly as a 'flag' for the PreventAccess + * middleware to decide whether the request should be aborted. + */ Route::middlewareGroup('tenancy', [ - \Stancl\Tenancy\Middleware\InitializeTenancy::class, + /* Prevent access from tenant domains to central routes and vice versa. */ + Middleware\PreventAccessFromTenantDomains::class, ]); + $this->loadRoutesFrom(__DIR__ . '/routes.php'); + $this->app->singleton('globalUrl', function ($app) { - $instance = clone $app['url']; - $instance->setAssetRoot($app[FilesystemTenancyBootstrapper::class]->originalPaths['asset_url']); + if ($app->bound(FilesystemTenancyBootstrapper::class)) { + $instance = clone $app['url']; + $instance->setAssetRoot($app[FilesystemTenancyBootstrapper::class]->originalPaths['asset_url']); + } else { + $instance = $app['url']; + } return $instance; }); diff --git a/src/TenantRouteServiceProvider.php b/src/TenantRouteServiceProvider.php index 16b7f8c2..6b3aca3d 100644 --- a/src/TenantRouteServiceProvider.php +++ b/src/TenantRouteServiceProvider.php @@ -11,8 +11,7 @@ class TenantRouteServiceProvider extends RouteServiceProvider { public function map() { - if (! in_array(request()->getHost(), $this->app['config']['tenancy.exempt_domains'] ?? []) - && file_exists(base_path('routes/tenant.php'))) { + if (file_exists(base_path('routes/tenant.php'))) { Route::middleware(['web', 'tenancy']) ->namespace($this->app['config']['tenancy.tenant_route_namespace'] ?? 'App\Http\Controllers') ->group(base_path('routes/tenant.php')); diff --git a/src/routes.php b/src/routes.php index 69f51e70..7f1a5310 100644 --- a/src/routes.php +++ b/src/routes.php @@ -2,6 +2,8 @@ declare(strict_types=1); -Route::get('/tenancy/assets/{path}', 'Stancl\Tenancy\Controllers\TenantAssetsController@asset') - ->where('path', '(.*)') - ->name('stancl.tenancy.asset'); +Route::middleware(['tenancy'])->group(function () { + Route::get('/tenancy/assets/{path}', 'Stancl\Tenancy\Controllers\TenantAssetsController@asset') + ->where('path', '(.*)') + ->name('stancl.tenancy.asset'); +}); diff --git a/tests/CommandsTest.php b/tests/CommandsTest.php index 4cf8a92c..071f929c 100644 --- a/tests/CommandsTest.php +++ b/tests/CommandsTest.php @@ -130,7 +130,7 @@ class CommandsTest extends TestCase file_put_contents(app_path('Http/Kernel.php'), file_get_contents(__DIR__ . '/Etc/defaultHttpKernel.stub')); $this->artisan('tenancy:install') - ->expectsQuestion('Do you want to publish the default database migrations?', 'yes'); + ->expectsQuestion('Do you wish to publish the migrations that create these tables?', 'yes'); $this->assertFileExists(base_path('routes/tenant.php')); $this->assertFileExists(base_path('config/tenancy.php')); $this->assertFileExists(database_path('migrations/2019_09_15_000010_create_tenants_table.php')); diff --git a/tests/Etc/modifiedHttpKernel.stub b/tests/Etc/modifiedHttpKernel.stub index 86cf535c..0de76bbd 100644 --- a/tests/Etc/modifiedHttpKernel.stub +++ b/tests/Etc/modifiedHttpKernel.stub @@ -39,6 +39,7 @@ class Kernel extends HttpKernel ], 'api' => [ + \Stancl\Tenancy\Middleware\PreventAccessFromTenantDomains::class, 'throttle:60,1', 'bindings', ],