1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2025-12-12 19:34:04 +00:00

[3.x] DB users (#382)

* Initial draft

* Apply fixes from StyleCI

* Use CI on master branch too

* Pass correct argument to queued DB creators/deleters

* Apply fixes from StyleCI

* Remove new interface from MySQLDBManager

* Make phpunit run

* Apply fixes from StyleCI

* Fix static property

* Default databaseName

* Use database transactions for creating users & granting permissions

* Apply fixes from StyleCI

* Get old tests to pass

* Apply fixes from StyleCI

* Add tests for PermissionControlledMySQLDatabaseManager

* Apply fixes from StyleCI

* Write test for extra config, fix bug with extra config

* Apply fixes from StyleCI
This commit is contained in:
Samuel Štancl 2020-05-03 18:12:27 +02:00 committed by GitHub
parent 60665517a0
commit 3bb2759fe2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 756 additions and 286 deletions

View file

@ -9,6 +9,7 @@ use Illuminate\Database\Connection;
use Illuminate\Support\Facades\DB;
use Stancl\Tenancy\Contracts\Future\CanSetConnection;
use Stancl\Tenancy\Contracts\TenantDatabaseManager;
use Stancl\Tenancy\Tenant;
class MySQLDatabaseManager implements TenantDatabaseManager, CanSetConnection
{
@ -20,6 +21,11 @@ class MySQLDatabaseManager implements TenantDatabaseManager, CanSetConnection
$this->connection = $config->get('tenancy.database_manager_connections.mysql');
}
public function getSeparator(): string
{
return 'database';
}
protected function database(): Connection
{
return DB::connection($this->connection);
@ -30,17 +36,18 @@ class MySQLDatabaseManager implements TenantDatabaseManager, CanSetConnection
$this->connection = $connection;
}
public function createDatabase(string $name): bool
public function createDatabase(Tenant $tenant): bool
{
$database = $tenant->database()->getName();
$charset = $this->database()->getConfig('charset');
$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

View file

@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\TenantDatabaseManagers;
use Stancl\Tenancy\Contracts\ManagesDatabaseUsers;
use Stancl\Tenancy\DatabaseConfig;
use Stancl\Tenancy\Exceptions\TenantDatabaseUserAlreadyExistsException;
use Stancl\Tenancy\Traits\CreatesDatabaseUsers;
class PermissionControlledMySQLDatabaseManager extends MySQLDatabaseManager implements ManagesDatabaseUsers
{
use CreatesDatabaseUsers;
public static $grants = [
'ALTER', 'ALTER ROUTINE', 'CREATE', 'CREATE ROUTINE', 'CREATE TEMPORARY TABLES', 'CREATE VIEW',
'DELETE', 'DROP', 'EVENT', 'EXECUTE', 'INDEX', 'INSERT', 'LOCK TABLES', 'REFERENCES', 'SELECT',
'SHOW VIEW', 'TRIGGER', 'UPDATE',
];
public function createUser(DatabaseConfig $databaseConfig): bool
{
$database = $databaseConfig->getName();
$username = $databaseConfig->getUsername();
$hostname = $databaseConfig->connection()['host'];
$password = $databaseConfig->getPassword();
if ($this->userExists($username)) {
throw new TenantDatabaseUserAlreadyExistsException($username);
}
$this->database()->statement("CREATE USER `{$username}`@`{$hostname}` IDENTIFIED BY '{$password}'");
$grants = implode(', ', static::$grants);
if ($this->isVersion8()) { // MySQL 8+
$grantQuery = "GRANT $grants ON `$database`.* TO `$username`@`$hostname`";
} else { // MySQL 5.7
$grantQuery = "GRANT $grants ON `$database`.* TO `$username`@`$hostname` IDENTIFIED BY '$password'";
}
return $this->database()->statement($grantQuery);
}
protected function isVersion8(): bool
{
$version = $this->database()->select($this->database()->raw('select version()'))[0]->{'version()'};
return version_compare($version, '8.0.0') >= 0;
}
public function deleteUser(DatabaseConfig $databaseConfig): bool
{
return $this->database()->statement("DROP USER IF EXISTS '{$databaseConfig->getUsername()}'");
}
public function userExists(string $username): bool
{
return (bool) $this->database()->select("SELECT count(*) FROM mysql.user WHERE user = '$username'")[0]->{'count(*)'};
}
}

View file

@ -9,6 +9,7 @@ use Illuminate\Database\Connection;
use Illuminate\Support\Facades\DB;
use Stancl\Tenancy\Contracts\Future\CanSetConnection;
use Stancl\Tenancy\Contracts\TenantDatabaseManager;
use Stancl\Tenancy\Tenant;
class PostgreSQLDatabaseManager implements TenantDatabaseManager, CanSetConnection
{
@ -20,6 +21,11 @@ class PostgreSQLDatabaseManager implements TenantDatabaseManager, CanSetConnecti
$this->connection = $config->get('tenancy.database_manager_connections.pgsql');
}
public function getSeparator(): string
{
return 'database';
}
protected function database(): Connection
{
return DB::connection($this->connection);
@ -30,14 +36,14 @@ class PostgreSQLDatabaseManager implements TenantDatabaseManager, CanSetConnecti
$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

View file

@ -9,6 +9,7 @@ use Illuminate\Database\Connection;
use Illuminate\Support\Facades\DB;
use Stancl\Tenancy\Contracts\Future\CanSetConnection;
use Stancl\Tenancy\Contracts\TenantDatabaseManager;
use Stancl\Tenancy\Tenant;
class PostgreSQLSchemaManager implements TenantDatabaseManager, CanSetConnection
{
@ -20,6 +21,11 @@ class PostgreSQLSchemaManager implements TenantDatabaseManager, CanSetConnection
$this->connection = $config->get('tenancy.database_manager_connections.pgsql');
}
public function getSeparator(): string
{
return 'schema';
}
protected function database(): Connection
{
return DB::connection($this->connection);
@ -30,14 +36,14 @@ class PostgreSQLSchemaManager implements TenantDatabaseManager, CanSetConnection
$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

View file

@ -4,23 +4,30 @@ declare(strict_types=1);
namespace Stancl\Tenancy\TenantDatabaseManagers;
use Stancl\Tenancy\Contracts\ModifiesDatabaseNameForConnection;
use Stancl\Tenancy\Contracts\TenantDatabaseManager;
use Stancl\Tenancy\Tenant;
class SQLiteDatabaseManager implements TenantDatabaseManager
class SQLiteDatabaseManager implements TenantDatabaseManager, ModifiesDatabaseNameForConnection
{
public function createDatabase(string $name): bool
public function getSeparator(): string
{
return 'database';
}
public function createDatabase(Tenant $tenant): bool
{
try {
return fclose(fopen(database_path($name), 'w'));
return fclose(fopen(database_path($tenant->database()->getName()), 'w'));
} catch (\Throwable $th) {
return false;
}
}
public function deleteDatabase(string $name): bool
public function deleteDatabase(Tenant $tenant): bool
{
try {
return unlink(database_path($name));
return unlink(database_path($tenant->database()->getName()));
} catch (\Throwable $th) {
return false;
}
@ -30,4 +37,9 @@ class SQLiteDatabaseManager implements TenantDatabaseManager
{
return file_exists(database_path($name));
}
public function getDatabaseNameForConnection(string $original): string
{
return database_path($original);
}
}