mirror of
https://github.com/archtechx/tenancy.git
synced 2026-02-05 10:14:04 +00:00
Use database transactions for creating users & granting permissions
This commit is contained in:
parent
70e6679678
commit
65ebc043dc
12 changed files with 66 additions and 48 deletions
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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}'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
26
src/Traits/CreatesDatabaseUsers.php
Normal file
26
src/Traits/CreatesDatabaseUsers.php
Normal 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());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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],
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue