mirror of
https://github.com/archtechx/tenancy.git
synced 2026-08-06 05:54:03 +00:00
### Parameter validation In `statement()` calls of `TenantDatabaseManager`s, use parameter binding when possible. When that's not possible, validate the parameters using `validateParameter()` or `validatePassword()`. Passwords use a less strict allowlist than other parameters (e.g. DB names), since passwords tend to use more special characters but we can afford to be more restrictive in the generic `validateParameter()`. In `SQLiteDatabaseManager`, names of file-based databases are validated (in `createDatabase`, `deleteDatabase` and `databaseExists`) using a similar allowlist to the (non-password) parameters in other DB managers, with an additional character: `.` (this addition is necessary since file-based SQLite databases end with `.sqlite`). ### DatabaseTenancyBootstrapper - harden() and the lost test file While checking for more places that could use validation, I realized that it's possible to update tenant's db_name to the central DB or the DB of another tenant. Added the `DatabaseTenancyBootstrapper::$harden` property -- setting it to true prevents tenants from connecting to the wrong databases (`RuntimeException` is thrown after connecting to the wrong database). Also, the DatabaseTenancyBootstrapper test file was ignored while running tests because it lacked the `Test` suffix. Added the suffix and fixed the broken `DATABASE_URL` test in the file. ### SQLiteDatabaseManager - respect static $path property in makeConnectionConfig() SQLiteDatabaseManager had a bug in `makeConnectionConfig`: the method didn't respect the static `$path` property, it used `database_path()` instead. Added a regression test for that. Also recognizing in-memory SQLite databases (using `isInMemory()`) is more strict now so that simply having a db_name with `_tenancy_inmemory_` somewhere in the name doesn't make a file-based database considered in-memory. ### MySQLDatabaseManager - charset and collation defaulting Creating databases with `null` charsets and collations resulted in a `QueryException`, since null isn't a valid charset/collation. To solve that, in the `CREATE DATABASE` statement in MySQLDatabaseManager, only add charset/collation to the statement if they are not null. MySQL defaults to the server's charset and collation, so it's safe to not pass any charset/collation in the `CREATE DATABASE` statement and let MySQL choose. Also, if e.g. collation is non-null and charset is null, MySQL will use a charset compatible with the used collation, and this works both ways. --------- Co-authored-by: Samuel Stancl <samuel@archte.ch> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
51 lines
1.6 KiB
PHP
51 lines
1.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Stancl\Tenancy\Database\TenantDatabaseManagers;
|
|
|
|
use Stancl\Tenancy\Database\Contracts\TenantWithDatabase;
|
|
|
|
class MySQLDatabaseManager extends TenantDatabaseManager
|
|
{
|
|
public function createDatabase(TenantWithDatabase $tenant): bool
|
|
{
|
|
$database = $tenant->database()->getName();
|
|
$charset = $this->connection()->getConfig('charset');
|
|
$collation = $this->connection()->getConfig('collation');
|
|
|
|
$this->validateParameter($database);
|
|
|
|
// MySQL defaults to the server's charset and collation
|
|
// if charset and collation are not specified.
|
|
// If charset is specified but collation is null, MySQL
|
|
// will choose a default collation for the specified charset (and vice versa).
|
|
$statement = "CREATE DATABASE `{$database}`";
|
|
|
|
if ($charset !== null) {
|
|
$this->validateParameter($charset);
|
|
$statement .= " CHARACTER SET `{$charset}`";
|
|
}
|
|
|
|
if ($collation !== null) {
|
|
$this->validateParameter($collation);
|
|
$statement .= " COLLATE `{$collation}`";
|
|
}
|
|
|
|
return $this->connection()->statement($statement);
|
|
}
|
|
|
|
public function deleteDatabase(TenantWithDatabase $tenant): bool
|
|
{
|
|
$database = $tenant->database()->getName();
|
|
|
|
$this->validateParameter($database);
|
|
|
|
return $this->connection()->statement("DROP DATABASE `{$database}`");
|
|
}
|
|
|
|
public function databaseExists(string $name): bool
|
|
{
|
|
return (bool) $this->connection()->select('SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = ?', [$name]);
|
|
}
|
|
}
|