1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2026-02-05 12:54:05 +00:00

Merge branch 'may25' into cloning-refactor

This commit is contained in:
Samuel Štancl 2025-06-23 11:39:42 +02:00
commit 3799f08c7c
12 changed files with 189 additions and 82 deletions

View file

@ -22,6 +22,23 @@ class CreateUserWithRLSPolicies extends Command
protected $description = "Creates RLS policies for all tables related to the tenant table. Also creates the RLS user if it doesn't exist yet";
/**
* Force, rather than just enable, the created RLS policies.
*
* By default, table owners bypass RLS policies. When this is enabled,
* they also need the BYPASSRLS permission. If your setup lets you create
* a user with BYPASSRLS, you may prefer leaving this on for additional
* safety. Otherwise, if you can't use BYPASSRLS, you can set this to false
* and depend on the behavior of table owners bypassing RLS automatically.
*
* This setting generally doesn't affect behavior at all with "default"
* setups, however if you have a more custom setup, with additional users
* involved (e.g. central connection user not being the same user that
* creates tables, or the created "RLS user" creating some tables) you
* should take care with how you configure this.
*/
public static bool $forceRls = true;
public function handle(PermissionControlledPostgreSQLSchemaManager $manager): int
{
$username = config('tenancy.rls.user.username');
@ -49,14 +66,9 @@ class CreateUserWithRLSPolicies extends Command
// Enable RLS scoping on the table (without this, queries won't be scoped using RLS)
DB::statement("ALTER TABLE {$table} ENABLE ROW LEVEL SECURITY");
/**
* Force RLS scoping on the table, so that the table owner users
* don't bypass the scoping table owners bypass RLS by default.
*
* E.g. when using a custom implementation where you create tables as the RLS user,
* the queries won't be scoped for the RLS user unless we force the RLS scoping using this query.
*/
DB::statement("ALTER TABLE {$table} FORCE ROW LEVEL SECURITY");
if (static::$forceRls) {
DB::statement("ALTER TABLE {$table} FORCE ROW LEVEL SECURITY");
}
}
/**

View file

@ -5,49 +5,11 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Contracts;
use Exception;
use Spatie\ErrorSolutions\Contracts\BaseSolution;
use Spatie\ErrorSolutions\Contracts\ProvidesSolution;
abstract class TenantCouldNotBeIdentifiedException extends Exception implements ProvidesSolution
abstract class TenantCouldNotBeIdentifiedException extends Exception
{
/** Default solution title. */
protected string $solutionTitle = 'Tenant could not be identified';
/** Default solution description. */
protected string $solutionDescription = 'Are you sure this tenant exists?';
/** Set the message. */
protected function tenantCouldNotBeIdentified(string $how): static
protected function tenantCouldNotBeIdentified(string $how): void
{
$this->message = 'Tenant could not be identified ' . $how;
return $this;
}
/** Set the solution title. */
protected function title(string $solutionTitle): static
{
$this->solutionTitle = $solutionTitle;
return $this;
}
/** Set the solution description. */
protected function description(string $solutionDescription): static
{
$this->solutionDescription = $solutionDescription;
return $this;
}
/** Get the Ignition description. */
public function getSolution(): BaseSolution
{
return BaseSolution::create($this->solutionTitle)
->setSolutionDescription($this->solutionDescription)
->setDocumentationLinks([
'Tenants' => 'https://tenancyforlaravel.com/docs/v3/tenants',
'Tenant Identification' => 'https://tenancyforlaravel.com/docs/v3/tenant-identification',
]);
}
}

View file

@ -10,9 +10,6 @@ class TenantColumnNotWhitelistedException extends TenantCouldNotBeIdentifiedExce
{
public function __construct(int|string $tenant_id)
{
$this
->tenantCouldNotBeIdentified("on path with tenant key: $tenant_id (column not whitelisted)")
->title('Tenant could not be identified on this route because the used column is not whitelisted.')
->description('Please add the column to the list of allowed columns in the PathTenantResolver config.');
$this->tenantCouldNotBeIdentified("on path with tenant key: $tenant_id (column not whitelisted)");
}
}

View file

@ -10,9 +10,6 @@ class TenantCouldNotBeIdentifiedByIdException extends TenantCouldNotBeIdentified
{
public function __construct(int|string $tenant_id)
{
$this
->tenantCouldNotBeIdentified("by tenant key: $tenant_id")
->title('Tenant could not be identified with that key')
->description('Are you sure the key is correct and the tenant exists?');
$this->tenantCouldNotBeIdentified("by tenant key: $tenant_id");
}
}

View file

@ -10,9 +10,6 @@ class TenantCouldNotBeIdentifiedByPathException extends TenantCouldNotBeIdentifi
{
public function __construct(int|string $tenant_id)
{
$this
->tenantCouldNotBeIdentified("on path with tenant key: $tenant_id")
->title('Tenant could not be identified on this path')
->description('Did you forget to create a tenant for this path?');
$this->tenantCouldNotBeIdentified("on path with tenant key: $tenant_id");
}
}

View file

@ -10,9 +10,6 @@ class TenantCouldNotBeIdentifiedByRequestDataException extends TenantCouldNotBeI
{
public function __construct(mixed $payload)
{
$this
->tenantCouldNotBeIdentified("by request data with payload: $payload")
->title('Tenant could not be identified using this request data')
->description('Did you forget to create a tenant with this id?');
$this->tenantCouldNotBeIdentified("by request data with payload: $payload");
}
}

View file

@ -10,9 +10,6 @@ class TenantCouldNotBeIdentifiedOnDomainException extends TenantCouldNotBeIdenti
{
public function __construct(string $domain)
{
$this
->tenantCouldNotBeIdentified("on domain $domain")
->title('Tenant could not be identified on this domain')
->description('Did you forget to create a tenant for this domain?');
$this->tenantCouldNotBeIdentified("on domain $domain");
}
}

View file

@ -30,6 +30,7 @@ if (! function_exists('tenant')) {
return app(Tenant::class);
}
// @phpstan-ignore-next-line nullsafe.neverNull
return app(Tenant::class)?->getAttribute($key);
}
}