1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2025-12-12 23:34:03 +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

166
src/DatabaseConfig.php Normal file
View file

@ -0,0 +1,166 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Stancl\Tenancy\Contracts\Future\CanSetConnection;
use Stancl\Tenancy\Contracts\ManagesDatabaseUsers;
use Stancl\Tenancy\Contracts\ModifiesDatabaseNameForConnection;
use Stancl\Tenancy\Contracts\TenantDatabaseManager;
use Stancl\Tenancy\Exceptions\DatabaseManagerNotRegisteredException;
class DatabaseConfig
{
/** @var Tenant */
public $tenant;
/** @var callable */
public static $usernameGenerator;
/** @var callable */
public static $passwordGenerator;
/** @var callable */
public static $databaseNameGenerator;
public static function __constructStatic(): void
{
static::$usernameGenerator = static::$usernameGenerator ?? function (Tenant $tenant) {
return Str::random(16);
};
static::$passwordGenerator = static::$usernameGenerator ?? function (Tenant $tenant) {
return Hash::make(Str::random(32));
};
static::$databaseNameGenerator = static::$databaseNameGenerator ?? function (Tenant $tenant) {
return config('tenancy.database.prefix') . $tenant->id . config('tenancy.database.suffix');
};
}
public function __construct(Tenant $tenant)
{
static::__constructStatic();
$this->tenant = $tenant;
}
public static function generateDatabaseNamesUsing(callable $databaseNameGenerator): void
{
static::$databaseNameGenerator = $databaseNameGenerator;
}
public static function generateUsernamesUsing(callable $usernameGenerator): void
{
static::$usernameGenerator = $usernameGenerator;
}
public static function generatePasswordsUsing(callable $passwordGenerator): void
{
static::$passwordGenerator = $passwordGenerator;
}
public function getName(): ?string
{
return $this->tenant->data['_tenancy_db_name'] ?? (static::$databaseNameGenerator)($this->tenant);
}
public function getUsername(): ?string
{
return $this->tenant->data['_tenancy_db_username'] ?? null;
}
public function getPassword(): ?string
{
return $this->tenant->data['_tenancy_db_password'] ?? null;
}
public function makeCredentials(): void
{
$this->tenant->data['_tenancy_db_name'] = $this->getName() ?? (static::$databaseNameGenerator)($this->tenant);
if ($this->manager() instanceof ManagesDatabaseUsers) {
$this->tenant->data['_tenancy_db_username'] = $this->getUsername() ?? (static::$usernameGenerator)($this->tenant);
$this->tenant->data['_tenancy_db_password'] = $this->getPassword() ?? (static::$passwordGenerator)($this->tenant);
}
}
public function getTemplateConnectionName(): string
{
return $this->tenant->data['_tenancy_db_connection']
?? config('tenancy.database.template_connection')
?? DatabaseManager::$originalDefaultConnectionName;
}
/**
* Tenant's own database connection config.
*/
public function connection(): array
{
$template = $this->getTemplateConnectionName();
$templateConnection = config("database.connections.{$template}");
$databaseName = $this->getName();
if (($manager = $this->manager()) instanceof ModifiesDatabaseNameForConnection) {
/** @var ModifiesDatabaseNameForConnection $manager */
$databaseName = $manager->getDatabaseNameForConnection($databaseName);
}
return array_merge($templateConnection, $this->tenantConfig(), [
$this->manager()->getSeparator() => $databaseName,
]);
}
/**
* Additional config for the database connection, specific to this tenant.
*/
public function tenantConfig(): array
{
$dbConfig = array_filter(array_keys($this->tenant->data), function ($key) {
return Str::startsWith($key, '_tenancy_db_');
});
// Remove DB name because we set that separately
if (($pos = array_search('_tenancy_db_name', $dbConfig)) !== false) {
unset($dbConfig[$pos]);
}
// Remove DB connection because that's not used inside the array
if (($pos = array_search('_tenancy_db_connection', $dbConfig)) !== false) {
unset($dbConfig[$pos]);
}
return array_reduce($dbConfig, function ($config, $key) {
return array_merge($config, [
Str::substr($key, strlen('_tenancy_db_')) => $this->tenant[$key],
]);
}, []);
}
/**
* Get the TenantDatabaseManager for this tenant's connection.
*/
public function manager(): TenantDatabaseManager
{
$driver = config("database.connections.{$this->getTemplateConnectionName()}.driver");
$databaseManagers = config('tenancy.database_managers');
if (! array_key_exists($driver, $databaseManagers)) {
throw new DatabaseManagerNotRegisteredException($driver);
}
/** @var TenantDatabaseManager $databaseManager */
$databaseManager = app($databaseManagers[$driver]);
if ($databaseManager instanceof CanSetConnection) {
$databaseManager->setConnection($this->getTemplateConnectionName());
}
return $databaseManager;
}
}