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

Use database transactions for creating users & granting permissions

This commit is contained in:
Samuel Štancl 2020-04-30 22:22:18 +02:00
parent 70e6679678
commit 65ebc043dc
12 changed files with 66 additions and 48 deletions

View file

@ -8,7 +8,7 @@ use Stancl\Tenancy\DatabaseConfig;
interface ManagesDatabaseUsers interface ManagesDatabaseUsers
{ {
public function createUser(DatabaseConfig $databaseConfig): void; public function createUser(DatabaseConfig $databaseConfig): bool;
public function deleteUser(DatabaseConfig $databaseConfig): void; public function deleteUser(DatabaseConfig $databaseConfig): bool;
} }

View file

@ -4,6 +4,8 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Contracts; namespace Stancl\Tenancy\Contracts;
use Stancl\Tenancy\Tenant;
interface TenantDatabaseManager interface TenantDatabaseManager
{ {
/** /**
@ -15,19 +17,13 @@ interface TenantDatabaseManager
/** /**
* Create a database. * Create a database.
*
* @param string $name Name of the database.
* @return bool
*/ */
public function createDatabase(string $name): bool; public function createDatabase(Tenant $tenant): bool;
/** /**
* Delete a database. * Delete a database.
*
* @param string $name Name of the database.
* @return bool
*/ */
public function deleteDatabase(string $name): bool; public function deleteDatabase(Tenant $tenant): bool;
/** /**
* Does a database exist. * Does a database exist.

View file

@ -153,11 +153,7 @@ class DatabaseManager
protected function createDatabaseSynchronously(Tenant $tenant, array $afterCreating) protected function createDatabaseSynchronously(Tenant $tenant, array $afterCreating)
{ {
$manager = $tenant->database()->manager(); $manager = $tenant->database()->manager();
$manager->createDatabase($tenant->database()->getName()); $manager->createDatabase($tenant);
if ($manager instanceof ManagesDatabaseUsers) {
$manager->createUser($tenant->database());
}
foreach ($afterCreating as $item) { foreach ($afterCreating as $item) {
if (is_object($item) && ! $item instanceof Closure) { if (is_object($item) && ! $item instanceof Closure) {
@ -183,10 +179,7 @@ class DatabaseManager
if ($this->app['config']['tenancy.queue_database_deletion'] ?? false) { if ($this->app['config']['tenancy.queue_database_deletion'] ?? false) {
QueuedTenantDatabaseDeleter::dispatch($manager, $tenant); QueuedTenantDatabaseDeleter::dispatch($manager, $tenant);
} else { } else {
$manager->deleteDatabase($database); $manager->deleteDatabase($tenant);
if ($manager instanceof ManagesDatabaseUsers) {
$manager->deleteUser($tenant->database());
}
} }
$this->tenancy->event('database.deleted', $database, $tenant); $this->tenancy->event('database.deleted', $database, $tenant);

View file

@ -36,10 +36,6 @@ class QueuedTenantDatabaseCreator implements ShouldQueue
*/ */
public function handle() public function handle()
{ {
$this->databaseManager->createDatabase($this->databaseName); $this->databaseManager->createDatabase($this->tenant);
if ($this->databaseManager instanceof ManagesDatabaseUsers) {
$this->databaseManager->createUser($this->tenant->database());
}
} }
} }

View file

@ -36,9 +36,6 @@ class QueuedTenantDatabaseDeleter implements ShouldQueue
*/ */
public function handle() public function handle()
{ {
$this->databaseManager->deleteDatabase($this->tenant->database()->getName()); $this->databaseManager->deleteDatabase($this->tenant);
if ($this->databaseManager instanceof ManagesDatabaseUsers) {
$this->databaseManager->deleteUser($this->tenant->database());
}
} }
} }

View file

@ -9,6 +9,7 @@ use Illuminate\Database\Connection;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Stancl\Tenancy\Contracts\Future\CanSetConnection; use Stancl\Tenancy\Contracts\Future\CanSetConnection;
use Stancl\Tenancy\Contracts\TenantDatabaseManager; use Stancl\Tenancy\Contracts\TenantDatabaseManager;
use Stancl\Tenancy\Tenant;
class MySQLDatabaseManager implements TenantDatabaseManager, CanSetConnection class MySQLDatabaseManager implements TenantDatabaseManager, CanSetConnection
{ {
@ -35,17 +36,18 @@ class MySQLDatabaseManager implements TenantDatabaseManager, CanSetConnection
$this->connection = $connection; $this->connection = $connection;
} }
public function createDatabase(string $name): bool public function createDatabase(Tenant $tenant): bool
{ {
$database = $tenant->database()->getName();
$charset = $this->database()->getConfig('charset'); $charset = $this->database()->getConfig('charset');
$collation = $this->database()->getConfig('collation'); $collation = $this->database()->getConfig('collation');
return $this->database()->statement("CREATE DATABASE `$name` CHARACTER SET `$charset` COLLATE `$collation`"); return $this->database()->statement("CREATE DATABASE `{$database}` CHARACTER SET `$charset` COLLATE `$collation`");
} }
public function deleteDatabase(string $name): bool public function deleteDatabase(Tenant $tenant): bool
{ {
return $this->database()->statement("DROP DATABASE `$name`"); return $this->database()->statement("DROP DATABASE `{$tenant->database()->getName()}`");
} }
public function databaseExists(string $name): bool public function databaseExists(string $name): bool

View file

@ -6,16 +6,19 @@ namespace Stancl\Tenancy\TenantDatabaseManagers;
use Stancl\Tenancy\Contracts\ManagesDatabaseUsers; use Stancl\Tenancy\Contracts\ManagesDatabaseUsers;
use Stancl\Tenancy\DatabaseConfig; use Stancl\Tenancy\DatabaseConfig;
use Stancl\Tenancy\Traits\CreatesDatabaseUsers;
class PermissionControlledMySQLDatabaseManager extends MySQLDatabaseManager implements ManagesDatabaseUsers class PermissionControlledMySQLDatabaseManager extends MySQLDatabaseManager implements ManagesDatabaseUsers
{ {
use CreatesDatabaseUsers;
public static $grants = [ public static $grants = [
'ALTER', 'ALTER ROUTINE', 'CREATE', 'CREATE ROUTINE', 'CREATE TEMPORARY TABLES', 'CREATE VIEW', 'ALTER', 'ALTER ROUTINE', 'CREATE', 'CREATE ROUTINE', 'CREATE TEMPORARY TABLES', 'CREATE VIEW',
'DELETE', 'DROP', 'EVENT', 'EXECUTE', 'INDEX', 'INSERT', 'LOCK TABLES', 'REFERENCES', 'SELECT', 'DELETE', 'DROP', 'EVENT', 'EXECUTE', 'INDEX', 'INSERT', 'LOCK TABLES', 'REFERENCES', 'SELECT',
'SHOW VIEW', 'TRIGGER', 'UPDATE', 'SHOW VIEW', 'TRIGGER', 'UPDATE',
]; ];
public function createUser(DatabaseConfig $databaseConfig): void public function createUser(DatabaseConfig $databaseConfig): bool
{ {
$database = $databaseConfig->getName(); $database = $databaseConfig->getName();
$username = $databaseConfig->getUsername(); $username = $databaseConfig->getUsername();
@ -32,7 +35,7 @@ class PermissionControlledMySQLDatabaseManager extends MySQLDatabaseManager impl
$grantQuery = "GRANT $grants ON $database.* TO `$username`@`$hostname` IDENTIFIED BY '$password'"; $grantQuery = "GRANT $grants ON $database.* TO `$username`@`$hostname` IDENTIFIED BY '$password'";
} }
$this->database()->statement($grantQuery); return $this->database()->statement($grantQuery);
} }
protected function isVersion8(): bool protected function isVersion8(): bool
@ -42,8 +45,8 @@ class PermissionControlledMySQLDatabaseManager extends MySQLDatabaseManager impl
return version_compare($version, '8.0.0') >= 0; return version_compare($version, '8.0.0') >= 0;
} }
public function deleteUser(DatabaseConfig $databaseConfig): void public function deleteUser(DatabaseConfig $databaseConfig): bool
{ {
$this->database()->statement("DROP USER IF EXISTS '{$databaseConfig->username}'"); return $this->database()->statement("DROP USER IF EXISTS '{$databaseConfig->username}'");
} }
} }

View file

@ -9,6 +9,7 @@ use Illuminate\Database\Connection;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Stancl\Tenancy\Contracts\Future\CanSetConnection; use Stancl\Tenancy\Contracts\Future\CanSetConnection;
use Stancl\Tenancy\Contracts\TenantDatabaseManager; use Stancl\Tenancy\Contracts\TenantDatabaseManager;
use Stancl\Tenancy\Tenant;
class PostgreSQLDatabaseManager implements TenantDatabaseManager, CanSetConnection class PostgreSQLDatabaseManager implements TenantDatabaseManager, CanSetConnection
{ {
@ -35,14 +36,14 @@ class PostgreSQLDatabaseManager implements TenantDatabaseManager, CanSetConnecti
$this->connection = $connection; $this->connection = $connection;
} }
public function createDatabase(string $name): bool public function createDatabase(Tenant $tenant): bool
{ {
return $this->database()->statement("CREATE DATABASE \"$name\" WITH TEMPLATE=template0"); return $this->database()->statement("CREATE DATABASE \"{$tenant->database()->getName()}\" WITH TEMPLATE=template0");
} }
public function deleteDatabase(string $name): bool public function deleteDatabase(Tenant $tenant): bool
{ {
return $this->database()->statement("DROP DATABASE \"$name\""); return $this->database()->statement("DROP DATABASE \"{$tenant->database()->getName()}\"");
} }
public function databaseExists(string $name): bool public function databaseExists(string $name): bool

View file

@ -9,6 +9,7 @@ use Illuminate\Database\Connection;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Stancl\Tenancy\Contracts\Future\CanSetConnection; use Stancl\Tenancy\Contracts\Future\CanSetConnection;
use Stancl\Tenancy\Contracts\TenantDatabaseManager; use Stancl\Tenancy\Contracts\TenantDatabaseManager;
use Stancl\Tenancy\Tenant;
class PostgreSQLSchemaManager implements TenantDatabaseManager, CanSetConnection class PostgreSQLSchemaManager implements TenantDatabaseManager, CanSetConnection
{ {
@ -35,14 +36,14 @@ class PostgreSQLSchemaManager implements TenantDatabaseManager, CanSetConnection
$this->connection = $connection; $this->connection = $connection;
} }
public function createDatabase(string $name): bool public function createDatabase(Tenant $tenant): bool
{ {
return $this->database()->statement("CREATE SCHEMA \"$name\""); return $this->database()->statement("CREATE SCHEMA \"{$tenant->database()->getName()}\"");
} }
public function deleteDatabase(string $name): bool public function deleteDatabase(Tenant $tenant): bool
{ {
return $this->database()->statement("DROP SCHEMA \"$name\""); return $this->database()->statement("DROP SCHEMA \"{$tenant->database()->getName()}\"");
} }
public function databaseExists(string $name): bool public function databaseExists(string $name): bool

View file

@ -6,6 +6,7 @@ namespace Stancl\Tenancy\TenantDatabaseManagers;
use Stancl\Tenancy\Contracts\ModifiesDatabaseNameForConnection; use Stancl\Tenancy\Contracts\ModifiesDatabaseNameForConnection;
use Stancl\Tenancy\Contracts\TenantDatabaseManager; use Stancl\Tenancy\Contracts\TenantDatabaseManager;
use Stancl\Tenancy\Tenant;
class SQLiteDatabaseManager implements TenantDatabaseManager, ModifiesDatabaseNameForConnection class SQLiteDatabaseManager implements TenantDatabaseManager, ModifiesDatabaseNameForConnection
{ {
@ -14,19 +15,19 @@ class SQLiteDatabaseManager implements TenantDatabaseManager, ModifiesDatabaseNa
return 'database'; return 'database';
} }
public function createDatabase(string $name): bool public function createDatabase(Tenant $tenant): bool
{ {
try { try {
return fclose(fopen(database_path($name), 'w')); return fclose(fopen(database_path($tenant->database()->getName()), 'w'));
} catch (\Throwable $th) { } catch (\Throwable $th) {
return false; return false;
} }
} }
public function deleteDatabase(string $name): bool public function deleteDatabase(Tenant $tenant): bool
{ {
try { try {
return unlink(database_path($name)); return unlink(database_path($tenant->database()->getName()));
} catch (\Throwable $th) { } catch (\Throwable $th) {
return false; return false;
} }

View file

@ -0,0 +1,26 @@
<?php
namespace Stancl\Tenancy\Traits;
use Stancl\Tenancy\Tenant;
trait CreatesDatabaseUsers
{
public function createDatabase(Tenant $tenant): bool
{
return $this->database()->transaction(function () use ($tenant) {
parent::createDatabase($tenant);
return $this->createUser($tenant->database());
});
}
public function deleteDatabase(Tenant $tenant): bool
{
return $this->database()->transaction(function () use ($tenant) {
parent::deleteDatabase($tenant);
return $this->deleteUser($tenant->database());
});
}
}

View file

@ -9,6 +9,7 @@ use Stancl\Tenancy\Jobs\QueuedTenantDatabaseCreator;
use Stancl\Tenancy\Jobs\QueuedTenantDatabaseDeleter; use Stancl\Tenancy\Jobs\QueuedTenantDatabaseDeleter;
use Stancl\Tenancy\Tenant; use Stancl\Tenancy\Tenant;
use Stancl\Tenancy\TenantDatabaseManagers\MySQLDatabaseManager; use Stancl\Tenancy\TenantDatabaseManagers\MySQLDatabaseManager;
use Stancl\Tenancy\TenantDatabaseManagers\PermissionControlledMySQLDatabaseManager;
use Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLDatabaseManager; use Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLDatabaseManager;
use Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLSchemaManager; use Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLSchemaManager;
use Stancl\Tenancy\TenantDatabaseManagers\SQLiteDatabaseManager; use Stancl\Tenancy\TenantDatabaseManagers\SQLiteDatabaseManager;
@ -77,6 +78,7 @@ class TenantDatabaseManagerTest extends TestCase
{ {
return [ return [
['mysql', MySQLDatabaseManager::class], ['mysql', MySQLDatabaseManager::class],
['mysql', PermissionControlledMySQLDatabaseManager::class],
['sqlite', SQLiteDatabaseManager::class], ['sqlite', SQLiteDatabaseManager::class],
['pgsql', PostgreSQLDatabaseManager::class], ['pgsql', PostgreSQLDatabaseManager::class],
['pgsql', PostgreSQLSchemaManager::class], ['pgsql', PostgreSQLSchemaManager::class],