mirror of
https://github.com/archtechx/tenancy.git
synced 2026-08-06 22:14:03 +00:00
Merge branch 'master' into broadcasting-fixes
This commit is contained in:
commit
f20f8016d8
49 changed files with 1586 additions and 210 deletions
|
|
@ -16,6 +16,15 @@ use Stancl\Tenancy\Contracts\Tenant;
|
|||
|
||||
/**
|
||||
* Makes cache tenant-aware by applying a prefix.
|
||||
*
|
||||
* Using this bootstrapper together with DatabaseTenancyBootstrapper
|
||||
* with a database cache store will result in "double scoping". The store will be scoped
|
||||
* by the DB connection (entries will go into the tenant's database) *and* by the prefix.
|
||||
* This is harmless in most cases, but is important to be aware of.
|
||||
*
|
||||
* If you only use database cache stores, consider using DatabaseCacheBootstrapper instead.
|
||||
*
|
||||
* @see Stancl\Tenancy\Bootstrappers\DatabaseCacheBootstrapper
|
||||
*/
|
||||
class CacheTenancyBootstrapper implements TenancyBootstrapper
|
||||
{
|
||||
|
|
|
|||
|
|
@ -21,11 +21,15 @@ use Stancl\Tenancy\TenancyServiceProvider;
|
|||
*
|
||||
* By default, this bootstrapper scopes ALL cache stores that use the database driver. If you only
|
||||
* want to scope SOME stores, set the static $stores property to an array of names of the stores
|
||||
* you want to scope. These stores must use 'database' as their driver.
|
||||
* you want to scope. Those stores must use 'database' as their driver.
|
||||
*
|
||||
* Notably, this bootstrapper sets TenancyServiceProvider::$adjustCacheManagerUsing to a callback
|
||||
* that ensures all affected stores still use the central connection when accessed via global cache
|
||||
* (typicaly the GlobalCache facade or global_cache() helper).
|
||||
* (typically the GlobalCache facade or global_cache() helper). The code in TenancyServiceProvider
|
||||
* that uses `extend()` callbacks to make database stores on the global cache manager use the central
|
||||
* connection only corrects stores scoped by the Database*Tenancy*Bootstrapper. This bootstrapper
|
||||
* also changes the stores' connection in the *config* to 'tenant' which doesn't let that callback
|
||||
* change the connection back to central on the global cache manager.
|
||||
*/
|
||||
class DatabaseCacheBootstrapper implements TenancyBootstrapper
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,14 +5,42 @@ declare(strict_types=1);
|
|||
namespace Stancl\Tenancy\Bootstrappers;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use RuntimeException;
|
||||
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
||||
use Stancl\Tenancy\Contracts\Tenant;
|
||||
use Stancl\Tenancy\Database\Contracts\TenantWithDatabase;
|
||||
use Stancl\Tenancy\Database\DatabaseManager;
|
||||
use Stancl\Tenancy\Database\Exceptions\TenantDatabaseDoesNotExistException;
|
||||
use Throwable;
|
||||
|
||||
class DatabaseTenancyBootstrapper implements TenancyBootstrapper
|
||||
{
|
||||
/**
|
||||
* When true, throw an exception if a tenant gets connected to
|
||||
* another tenant's database or to the central database.
|
||||
*
|
||||
* This case should never come up in well-configured apps where
|
||||
* users cannot set or edit tenant IDs or database names, so this
|
||||
* option is disabled by default.
|
||||
*
|
||||
* However, applications dealing with extremely sensitive data may
|
||||
* choose to enable this runtime check to prevent a bug or misconfiguration
|
||||
* from creating an exploit that would let an attacker access another
|
||||
* tenant's data or data from the central database.
|
||||
*
|
||||
* One way such a scenario might come up is if an application allows
|
||||
* broad tenant attribute updates on a page for updating some fields
|
||||
* on the tenant, without restricting that action to only a limited
|
||||
* set of fields that are safe to edit. An attacker might be able to add
|
||||
* something like ['tenancy_db_name' => '...'] to the request which could
|
||||
* lead to this internal attribute being updated on an existing tenant.
|
||||
*
|
||||
* It's possible that enabling this setting will negate the performance
|
||||
* benefits of cached tenant lookup.
|
||||
*/
|
||||
public static bool $harden = false;
|
||||
|
||||
/** @var DatabaseManager */
|
||||
protected $database;
|
||||
|
||||
|
|
@ -41,10 +69,39 @@ class DatabaseTenancyBootstrapper implements TenancyBootstrapper
|
|||
}
|
||||
|
||||
$this->database->connectToTenant($tenant);
|
||||
|
||||
if (static::$harden) {
|
||||
try {
|
||||
$this->verifyTenantCanUseDatabase($tenant);
|
||||
} catch (Throwable $e) {
|
||||
// Revert connection back to central
|
||||
$this->revert();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function revert(): void
|
||||
{
|
||||
$this->database->reconnectToCentral();
|
||||
}
|
||||
|
||||
protected function verifyTenantCanUseDatabase(Tenant $tenant): void
|
||||
{
|
||||
/** @var \Stancl\Tenancy\Database\Models\Tenant&TenantWithDatabase $tenant */
|
||||
$tenantDbName = $tenant->database()->getName();
|
||||
|
||||
// Check that no other tenant uses this tenant's database
|
||||
if ($tenant::where($tenant->getTenantKeyName(), '!=', $tenant->getTenantKey())
|
||||
->where($tenant::getDataColumn() . '->' . $tenant->internalPrefix() . 'db_name', $tenantDbName)
|
||||
->exists()) {
|
||||
throw new RuntimeException('Tenant cannot use a database of another tenant.');
|
||||
}
|
||||
|
||||
if (Schema::hasTable($tenant->getTable())) {
|
||||
// Throw if the current database/schema has the tenants table (i.e. it's not central)
|
||||
throw new RuntimeException('Tenant cannot use the central database.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use Stancl\Tenancy\Resolvers\PathTenantResolver;
|
|||
/**
|
||||
* Makes the app use TenancyUrlGenerator (instead of Illuminate\Routing\UrlGenerator) which:
|
||||
* - prefixes route names with the tenant route name prefix (PathTenantResolver::tenantRouteNamePrefix() by default)
|
||||
* - passes the tenant parameter to the link generated by route() and temporarySignedRoute() (PathTenantResolver::tenantParameterName() by default).
|
||||
* - passes the tenant parameter (PathTenantResolver::tenantParameterName() by default) to the link generated by the affected methods like route() and temporarySignedRoute().
|
||||
*
|
||||
* Used with path and query string identification.
|
||||
*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue