1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2026-05-06 18:04:03 +00:00

Validate in-memory db names, move SQLite-specific methods to the SQLiteManager

This commit is contained in:
lukinovec 2026-05-01 14:09:56 +02:00
parent 7363318f6e
commit 48b4837905
2 changed files with 49 additions and 35 deletions

View file

@ -28,16 +28,6 @@ trait ValidatesDatabaseParameters
return 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-'; return 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-';
} }
/**
* Characters allowed in filenames (SQLite databases).
*
* Includes dots to support file extensions (e.g. '.sqlite').
*/
protected static function allowedFilenameCharacters(): string
{
return 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.';
}
/** /**
* Characters allowed in database user passwords. * Characters allowed in database user passwords.
* *
@ -62,7 +52,7 @@ trait ValidatesDatabaseParameters
* *
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
protected function validateParameter(string|array|null $parameters, string|null $allowedCharacters = null): void protected static function validateParameter(string|array|null $parameters, string|null $allowedCharacters = null): void
{ {
$allowedCharacters ??= static::allowedParameterCharacters(); $allowedCharacters ??= static::allowedParameterCharacters();
@ -95,28 +85,8 @@ trait ValidatesDatabaseParameters
* *
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
protected function validatePassword(string|null $password): void protected static function validatePassword(string|null $password): void
{ {
$this->validateParameter($password, static::allowedPasswordCharacters()); static::validateParameter($password, allowedCharacters: static::allowedPasswordCharacters());
}
/**
* Ensure filename only contains allowed characters (static::allowedFilenameCharacters())
* and is not a directory name before used in file paths (e.g. SQLite database names).
*
* @throws InvalidArgumentException
* @see Stancl\Tenancy\Database\TenantDatabaseManagers\SQLiteDatabaseManager
*/
protected function validateFilename(string $filename): void
{
$this->validateParameter($filename, static::allowedFilenameCharacters());
if ($filename === '') {
throw new InvalidArgumentException('Filename cannot be empty.');
}
if (is_dir($filename)) {
throw new InvalidArgumentException("Filename ('{$filename}') cannot be a directory.");
}
} }
} }

View file

@ -11,6 +11,7 @@ use Stancl\Tenancy\Database\Concerns\ValidatesDatabaseParameters;
use Stancl\Tenancy\Database\Contracts\TenantDatabaseManager; use Stancl\Tenancy\Database\Contracts\TenantDatabaseManager;
use Stancl\Tenancy\Database\Contracts\TenantWithDatabase; use Stancl\Tenancy\Database\Contracts\TenantWithDatabase;
use Throwable; use Throwable;
use InvalidArgumentException;
class SQLiteDatabaseManager implements TenantDatabaseManager class SQLiteDatabaseManager implements TenantDatabaseManager
{ {
@ -60,6 +61,16 @@ class SQLiteDatabaseManager implements TenantDatabaseManager
*/ */
public static Closure|null $closeInMemoryConnectionUsing = null; public static Closure|null $closeInMemoryConnectionUsing = null;
/**
* Characters allowed in database names.
*
* Includes dots to support file extensions (e.g. '.sqlite').
*/
protected static function allowedDatabaseNameCharacters(): string
{
return 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.';
}
public function createDatabase(TenantWithDatabase $tenant): bool public function createDatabase(TenantWithDatabase $tenant): bool
{ {
/** @var TenantWithDatabase&Model $tenant */ /** @var TenantWithDatabase&Model $tenant */
@ -136,6 +147,8 @@ class SQLiteDatabaseManager implements TenantDatabaseManager
(static::$persistInMemoryConnectionUsing)(new PDO($dsn), $dsn); (static::$persistInMemoryConnectionUsing)(new PDO($dsn), $dsn);
} }
} else { } else {
$this->validateDatabaseName($databaseName);
$baseConfig['database'] = database_path($databaseName); $baseConfig['database'] = database_path($databaseName);
} }
@ -148,13 +161,44 @@ class SQLiteDatabaseManager implements TenantDatabaseManager
return rtrim(static::$path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name; return rtrim(static::$path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name;
} }
$this->validateFilename($name); $this->validateDatabaseName($name);
return database_path($name); return database_path($name);
} }
public static function isInMemory(string $name): bool public static function isInMemory(string $name): bool
{ {
return $name === ':memory:' || str_starts_with($name, 'file:_tenancy_inmemory_'); if ($name === ':memory:') {
return true;
}
if (str_starts_with($name, 'file:_tenancy_inmemory_') &&
str_ends_with($name, '?mode=memory&cache=shared')) {
// Named in-memory DBs are formatted like 'file:_tenancy_inmemory_tenant123?mode=memory&cache=shared'
static::validateDatabaseName($name, ':?=&');
return true;
}
return false;
}
/**
* Ensure database name only contains allowed characters
* (static::allowedDatabaseNameCharacters() + $extraAllowedCharacters) and is not a directory name.
*
* @throws InvalidArgumentException
*/
protected static function validateDatabaseName(string $name, string $extraAllowedCharacters = ''): void
{
static::validateParameter($name, static::allowedDatabaseNameCharacters() . $extraAllowedCharacters);
if ($name === '') {
throw new InvalidArgumentException('Database name cannot be empty.');
}
if (is_dir($name)) {
throw new InvalidArgumentException("Database name cannot be a directory.");
}
} }
} }