mirror of
https://github.com/nuxsmin/sysPass.git
synced 2026-03-04 23:54:08 +01:00
feat: Installer refactoring to inject all dependencies at build time.
Signed-off-by: Rubén D <nuxsmin@syspass.org>
This commit is contained in:
@@ -148,10 +148,8 @@ final class InstallCommand extends CommandBase
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(
|
||||
InputInterface $input,
|
||||
OutputInterface $output
|
||||
): int {
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$style = new SymfonyStyle($input, $output);
|
||||
|
||||
try {
|
||||
@@ -166,7 +164,7 @@ final class InstallCommand extends CommandBase
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$this->installer->run(InstallerService::getDatabaseSetup($installData, $this->configData), $installData);
|
||||
$this->installer->run($installData);
|
||||
|
||||
$this->logger->info(__('Installation finished'));
|
||||
|
||||
|
||||
@@ -27,9 +27,8 @@ namespace SP\Modules\Web\Controllers\Install;
|
||||
|
||||
use Exception;
|
||||
use SP\Core\Application;
|
||||
use SP\Domain\Install\In\InstallData;
|
||||
use SP\Domain\Install\In\InstallDataFactory;
|
||||
use SP\Domain\Install\InstallerServiceInterface;
|
||||
use SP\Domain\Install\Services\InstallerService;
|
||||
use SP\Http\JsonResponse;
|
||||
use SP\Modules\Web\Controllers\ControllerBase;
|
||||
use SP\Modules\Web\Controllers\Traits\JsonTrait;
|
||||
@@ -47,7 +46,7 @@ final class InstallController extends ControllerBase
|
||||
public function __construct(
|
||||
Application $application,
|
||||
WebControllerHelper $webControllerHelper,
|
||||
InstallerServiceInterface $installer
|
||||
InstallerServiceInterface $installer,
|
||||
) {
|
||||
parent::__construct($application, $webControllerHelper);
|
||||
|
||||
@@ -56,14 +55,14 @@ final class InstallController extends ControllerBase
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws \JsonException
|
||||
* @throws \SP\Core\Exceptions\SPException
|
||||
*/
|
||||
public function installAction(): bool
|
||||
{
|
||||
$installData = $this->getInstallDataFromRequest();
|
||||
$installData = InstallDataFactory::buildFromRequest($this->request);
|
||||
|
||||
try {
|
||||
$this->installer->run(InstallerService::getDatabaseSetup($installData, $this->configData), $installData);
|
||||
$this->installer->run($installData);
|
||||
|
||||
return $this->returnJsonResponse(JsonResponse::JSON_SUCCESS, __u('Installation finished'));
|
||||
} catch (Exception $e) {
|
||||
@@ -72,23 +71,4 @@ final class InstallController extends ControllerBase
|
||||
return $this->returnJsonResponseException($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \SP\Domain\Install\In\InstallData
|
||||
*/
|
||||
private function getInstallDataFromRequest(): InstallData
|
||||
{
|
||||
$installData = new InstallData();
|
||||
$installData->setSiteLang($this->request->analyzeString('sitelang', 'en_US'));
|
||||
$installData->setAdminLogin($this->request->analyzeString('adminlogin', 'admin'));
|
||||
$installData->setAdminPass($this->request->analyzeEncrypted('adminpass'));
|
||||
$installData->setMasterPassword($this->request->analyzeEncrypted('masterpassword'));
|
||||
$installData->setDbAdminUser($this->request->analyzeString('dbuser', 'root'));
|
||||
$installData->setDbAdminPass($this->request->analyzeEncrypted('dbpass'));
|
||||
$installData->setDbName($this->request->analyzeString('dbname', 'syspass'));
|
||||
$installData->setDbHost($this->request->analyzeString('dbhost', 'localhost'));
|
||||
$installData->setHostingMode($this->request->analyzeBool('hostingmode', false));
|
||||
|
||||
return $installData;
|
||||
}
|
||||
}
|
||||
@@ -27,11 +27,11 @@ use PHPMailer\PHPMailer\PHPMailer;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use SP\Core\Acl\Acl;
|
||||
use SP\Core\Acl\Actions;
|
||||
use SP\Core\Application;
|
||||
use SP\Core\Context\ContextFactory;
|
||||
use SP\Core\Context\ContextInterface;
|
||||
use SP\Core\Crypt\CryptPKI;
|
||||
use SP\Core\Crypt\CSRF;
|
||||
use SP\Core\Exceptions\SPException;
|
||||
use SP\Core\Language;
|
||||
use SP\Core\LanguageInterface;
|
||||
use SP\Core\MimeTypes;
|
||||
@@ -42,6 +42,9 @@ use SP\Domain\Config\ConfigInterface;
|
||||
use SP\Domain\Config\In\ConfigDataInterface;
|
||||
use SP\Domain\Config\Services\ConfigBackupService;
|
||||
use SP\Domain\Config\Services\ConfigFileService;
|
||||
use SP\Domain\Install\DatabaseSetupInterface;
|
||||
use SP\Domain\Install\In\InstallDataFactory;
|
||||
use SP\Domain\Install\Services\MysqlSetupBuilder;
|
||||
use SP\Domain\Providers\MailerInterface;
|
||||
use SP\Domain\Providers\MailProviderInterface;
|
||||
use SP\Http\Client;
|
||||
@@ -50,8 +53,8 @@ use SP\Http\RequestInterface;
|
||||
use SP\Infrastructure\Database\Database;
|
||||
use SP\Infrastructure\Database\DatabaseConnectionData;
|
||||
use SP\Infrastructure\Database\DatabaseInterface;
|
||||
use SP\Infrastructure\Database\DBStorageInterface;
|
||||
use SP\Infrastructure\Database\MySQLHandler;
|
||||
use SP\Infrastructure\Database\DbStorageInterface;
|
||||
use SP\Infrastructure\Database\MysqlHandler;
|
||||
use SP\Infrastructure\File\FileCache;
|
||||
use SP\Infrastructure\File\FileHandler;
|
||||
use SP\Infrastructure\File\XmlHandler;
|
||||
@@ -75,11 +78,11 @@ use function DI\factory;
|
||||
use function DI\get;
|
||||
|
||||
return [
|
||||
RequestInterface::class => create(Request::class)
|
||||
RequestInterface::class => create(Request::class)
|
||||
->constructor(\Klein\Request::createFromGlobals(), autowire(CryptPKI::class)),
|
||||
ContextInterface::class =>
|
||||
ContextInterface::class =>
|
||||
static fn() => ContextFactory::getForModule(APP_MODULE),
|
||||
ConfigInterface::class => create(ConfigFileService::class)
|
||||
ConfigInterface::class => create(ConfigFileService::class)
|
||||
->constructor(
|
||||
create(XmlHandler::class)
|
||||
->constructor(create(FileHandler::class)->constructor(CONFIG_FILE)),
|
||||
@@ -87,84 +90,91 @@ return [
|
||||
get(ContextInterface::class),
|
||||
autowire(ConfigBackupService::class)->lazy()
|
||||
),
|
||||
ConfigDataInterface::class =>
|
||||
ConfigDataInterface::class =>
|
||||
static fn(ConfigInterface $config) => $config->getConfigData(),
|
||||
DBStorageInterface::class => create(MySQLHandler::class)
|
||||
->constructor(factory([DatabaseConnectionData::class, 'getFromConfig'])),
|
||||
Actions::class =>
|
||||
DatabaseConnectionData::class => factory([DatabaseConnectionData::class, 'getFromConfig']),
|
||||
DbStorageInterface::class => autowire(MysqlHandler::class),
|
||||
Actions::class =>
|
||||
static fn() => new Actions(
|
||||
new FileCache(Actions::ACTIONS_CACHE_FILE),
|
||||
new XmlHandler(new FileHandler(ACTIONS_FILE))
|
||||
),
|
||||
MimeTypesInterface::class =>
|
||||
MimeTypesInterface::class =>
|
||||
static fn() => new MimeTypes(
|
||||
new FileCache(MimeTypes::MIME_CACHE_FILE),
|
||||
new XmlHandler(new FileHandler(MIMETYPES_FILE))
|
||||
),
|
||||
Acl::class => autowire(Acl::class)
|
||||
Acl::class => autowire(Acl::class)
|
||||
->constructorParameter('actions', get(Actions::class)),
|
||||
ThemeInterface::class => autowire(Theme::class)
|
||||
ThemeInterface::class => autowire(Theme::class)
|
||||
->constructorParameter('module', APP_MODULE)
|
||||
->constructorParameter(
|
||||
'fileCache',
|
||||
create(FileCache::class)->constructor(Theme::ICONS_CACHE_FILE)
|
||||
),
|
||||
TemplateInterface::class => autowire(Template::class),
|
||||
DatabaseAuthInterface::class => autowire(DatabaseAuth::class),
|
||||
BrowserAuthInterface::class => autowire(BrowserAuth::class),
|
||||
LdapAuthInterface::class => autowire(LdapAuth::class)
|
||||
TemplateInterface::class => autowire(Template::class),
|
||||
DatabaseAuthInterface::class => autowire(DatabaseAuth::class),
|
||||
BrowserAuthInterface::class => autowire(BrowserAuth::class),
|
||||
LdapAuthInterface::class => autowire(LdapAuth::class)
|
||||
->constructorParameter(
|
||||
'ldap',
|
||||
factory([Ldap::class, 'factory'])
|
||||
->parameter('ldapParams', factory([LdapParams::class, 'getFrom']))
|
||||
factory([Ldap::class, 'factory'])->parameter('ldapParams', factory([LdapParams::class, 'getFrom']))
|
||||
),
|
||||
AuthProviderInterface::class => static function (
|
||||
ContainerInterface $c,
|
||||
ConfigDataInterface $configData
|
||||
) {
|
||||
$provider = new AuthProvider($c->get(Application::class), $c->get(DatabaseAuthInterface::class));
|
||||
AuthProviderInterface::class =>
|
||||
static function (ContainerInterface $c, ConfigDataInterface $configData) {
|
||||
/** @var AuthProvider $provider */
|
||||
$provider = autowire(AuthProvider::class);
|
||||
|
||||
if ($configData->isLdapEnabled()) {
|
||||
$provider->withLdapAuth($c->get(LdapAuthInterface::class));
|
||||
}
|
||||
if ($configData->isLdapEnabled()) {
|
||||
$provider->withLdapAuth($c->get(LdapAuthInterface::class));
|
||||
}
|
||||
|
||||
if ($configData->isAuthBasicEnabled()) {
|
||||
$provider->withBrowserAuth($c->get(BrowserAuthInterface::class));
|
||||
}
|
||||
if ($configData->isAuthBasicEnabled()) {
|
||||
$provider->withBrowserAuth($c->get(BrowserAuthInterface::class));
|
||||
}
|
||||
|
||||
return $provider;
|
||||
},
|
||||
Logger::class => create(Logger::class)
|
||||
return $provider;
|
||||
},
|
||||
Logger::class => create(Logger::class)
|
||||
->constructor('syspass'),
|
||||
\GuzzleHttp\Client::class => create(GuzzleHttp\Client::class)
|
||||
\GuzzleHttp\Client::class => create(GuzzleHttp\Client::class)
|
||||
->constructor(factory([Client::class, 'getOptions'])),
|
||||
CSRF::class => autowire(CSRF::class),
|
||||
LanguageInterface::class => autowire(Language::class),
|
||||
DatabaseInterface::class => autowire(Database::class),
|
||||
MailProviderInterface::class => autowire(MailProvider::class),
|
||||
MailerInterface::class => autowire(PhpMailerWrapper::class)->constructor(
|
||||
CSRF::class => autowire(CSRF::class),
|
||||
LanguageInterface::class => autowire(Language::class),
|
||||
DatabaseInterface::class => autowire(Database::class),
|
||||
MailProviderInterface::class => autowire(MailProvider::class),
|
||||
MailerInterface::class => autowire(PhpMailerWrapper::class)->constructor(
|
||||
create(PHPMailer::class)->constructor(true)
|
||||
),
|
||||
'SP\Domain\Account\*ServiceInterface' => autowire('SP\Domain\Account\Services\*Service'),
|
||||
'SP\Domain\Account\In\*RepositoryInterface' => autowire('SP\Infrastructure\Account\Repositories\*Repository'),
|
||||
'SP\Domain\Category\*ServiceInterface' => autowire('SP\Domain\Category\Services\*Service'),
|
||||
'SP\Domain\Category\In\*RepositoryInterface' => autowire('SP\Infrastructure\Category\Repositories\*Repository'),
|
||||
'SP\Domain\Client\*ServiceInterface' => autowire('SP\Domain\Client\Services\*Service'),
|
||||
'SP\Domain\Client\In\*RepositoryInterface' => autowire('SP\Infrastructure\Client\Repositories\*Repository'),
|
||||
'SP\Domain\Tag\*ServiceInterface' => autowire('SP\Domain\Tag\Services\*Service'),
|
||||
'SP\Domain\Tag\In\*RepositoryInterface' => autowire('SP\Infrastructure\Tag\Repositories\*Repository'),
|
||||
'SP\Domain\User\*ServiceInterface' => autowire('SP\Domain\User\Services\*Service'),
|
||||
'SP\Domain\User\In\*RepositoryInterface' => autowire('SP\Infrastructure\User\Repositories\*Repository'),
|
||||
'SP\Domain\Auth\*ServiceInterface' => autowire('SP\Domain\Auth\Services\*Service'),
|
||||
'SP\Domain\Auth\In\*RepositoryInterface' => autowire('SP\Infrastructure\Auth\Repositories\*Repository'),
|
||||
'SP\Domain\CustomField\*ServiceInterface' => autowire('SP\Domain\CustomField\Services\*Service'),
|
||||
'SP\Domain\CustomField\In\*RepositoryInterface' => autowire(
|
||||
DatabaseSetupInterface::class => static function (RequestInterface $request) {
|
||||
$installData = InstallDataFactory::buildFromRequest($request);
|
||||
|
||||
if ($installData->getBackendType() === 'mysql') {
|
||||
return MysqlSetupBuilder::build($installData);
|
||||
}
|
||||
|
||||
throw new SPException(__u('Unimplemented'), SPException::ERROR, __u('Wrong backend type'));
|
||||
},
|
||||
'SP\Domain\Account\*ServiceInterface' => autowire('SP\Domain\Account\Services\*Service'),
|
||||
'SP\Domain\Account\In\*RepositoryInterface' => autowire('SP\Infrastructure\Account\Repositories\*Repository'),
|
||||
'SP\Domain\Category\*ServiceInterface' => autowire('SP\Domain\Category\Services\*Service'),
|
||||
'SP\Domain\Category\In\*RepositoryInterface' => autowire('SP\Infrastructure\Category\Repositories\*Repository'),
|
||||
'SP\Domain\Client\*ServiceInterface' => autowire('SP\Domain\Client\Services\*Service'),
|
||||
'SP\Domain\Client\In\*RepositoryInterface' => autowire('SP\Infrastructure\Client\Repositories\*Repository'),
|
||||
'SP\Domain\Tag\*ServiceInterface' => autowire('SP\Domain\Tag\Services\*Service'),
|
||||
'SP\Domain\Tag\In\*RepositoryInterface' => autowire('SP\Infrastructure\Tag\Repositories\*Repository'),
|
||||
'SP\Domain\User\*ServiceInterface' => autowire('SP\Domain\User\Services\*Service'),
|
||||
'SP\Domain\User\In\*RepositoryInterface' => autowire('SP\Infrastructure\User\Repositories\*Repository'),
|
||||
'SP\Domain\Auth\*ServiceInterface' => autowire('SP\Domain\Auth\Services\*Service'),
|
||||
'SP\Domain\Auth\In\*RepositoryInterface' => autowire('SP\Infrastructure\Auth\Repositories\*Repository'),
|
||||
'SP\Domain\CustomField\*ServiceInterface' => autowire('SP\Domain\CustomField\Services\*Service'),
|
||||
'SP\Domain\CustomField\In\*RepositoryInterface' => autowire(
|
||||
'SP\Infrastructure\CustomField\Repositories\*Repository'
|
||||
),
|
||||
'SP\Domain\Export\*ServiceInterface' => autowire('SP\Domain\Export\Services\*Service'),
|
||||
'SP\Domain\Import\*ServiceInterface' => autowire('SP\Domain\Import\Services\*Service'),
|
||||
'SP\Domain\Install\*ServiceInterface' => autowire('SP\Domain\Install\Services\*Service'),
|
||||
'SP\Domain\Crypt\*ServiceInterface' => autowire('SP\Domain\Crypt\Services\*Service'),
|
||||
'SP\Domain\Export\*ServiceInterface' => autowire('SP\Domain\Export\Services\*Service'),
|
||||
'SP\Domain\Import\*ServiceInterface' => autowire('SP\Domain\Import\Services\*Service'),
|
||||
'SP\Domain\Install\*ServiceInterface' => autowire('SP\Domain\Install\Services\*Service'),
|
||||
'SP\Domain\Crypt\*ServiceInterface' => autowire('SP\Domain\Crypt\Services\*Service'),
|
||||
'SP\Domain\Plugin\*ServiceInterface' => autowire('SP\Domain\Plugin\Services\*Service'),
|
||||
'SP\Domain\ItemPreset\*ServiceInterface' => autowire('SP\Domain\ItemPreset\Services\*Service'),
|
||||
'SP\Domain\ItemPreset\In\*RepositoryInterface' => autowire(
|
||||
|
||||
@@ -53,7 +53,7 @@ class ConfigFileService implements ConfigInterface
|
||||
|
||||
private static int $timeUpdated;
|
||||
private bool $configLoaded = false;
|
||||
private ?ConfigDataInterface $configData = null;
|
||||
private static ?ConfigDataInterface $configData = null;
|
||||
private ContextInterface $context;
|
||||
private XmlFileStorageInterface $fileStorage;
|
||||
private FileCacheInterface $fileCache;
|
||||
@@ -83,12 +83,10 @@ class ConfigFileService implements ConfigInterface
|
||||
{
|
||||
if (!$this->configLoaded) {
|
||||
try {
|
||||
if ($this->fileCache->exists()
|
||||
&& !$this->isCacheExpired()
|
||||
) {
|
||||
$this->configData = $this->fileCache->load();
|
||||
if ($this->fileCache->exists() && !$this->isCacheExpired()) {
|
||||
self::$configData = $this->fileCache->load();
|
||||
|
||||
if ($this->configData->count() === 0) {
|
||||
if (self::$configData->count() === 0) {
|
||||
$this->fileCache->delete();
|
||||
$this->initialize();
|
||||
|
||||
@@ -98,8 +96,8 @@ class ConfigFileService implements ConfigInterface
|
||||
logger('Config cache loaded');
|
||||
} else {
|
||||
if (file_exists($this->fileStorage->getFileHandler()->getFile())) {
|
||||
$this->configData = $this->loadConfigFromFile();
|
||||
$this->fileCache->save($this->configData);
|
||||
self::$configData = $this->loadConfigFromFile();
|
||||
$this->fileCache->save(self::$configData);
|
||||
} else {
|
||||
$configData = new ConfigData();
|
||||
|
||||
@@ -114,7 +112,7 @@ class ConfigFileService implements ConfigInterface
|
||||
logger('Config loaded');
|
||||
}
|
||||
|
||||
self::$timeUpdated = $this->configData->getConfigDate();
|
||||
self::$timeUpdated = self::$configData->getConfigDate();
|
||||
|
||||
$this->configLoaded = true;
|
||||
} catch (Exception $e) {
|
||||
@@ -186,8 +184,7 @@ class ConfigFileService implements ConfigInterface
|
||||
$this->configBackupService->backup($configData);
|
||||
}
|
||||
|
||||
$configSaver = $this->context->getUserData()->getLogin()
|
||||
?: AppInfoInterface::APP_NAME;
|
||||
$configSaver = $this->context->getUserData()->getLogin() ?: AppInfoInterface::APP_NAME;
|
||||
|
||||
$configData->setConfigDate(time());
|
||||
$configData->setConfigSaver($configSaver);
|
||||
@@ -198,7 +195,7 @@ class ConfigFileService implements ConfigInterface
|
||||
// Save the class object (serialized)
|
||||
$this->fileCache->save($configData);
|
||||
|
||||
$this->configData = $configData;
|
||||
self::$configData = $configData;
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -217,7 +214,7 @@ class ConfigFileService implements ConfigInterface
|
||||
$configData->setConfigSaver($this->context->getUserData()->getLogin());
|
||||
$configData->setConfigHash();
|
||||
|
||||
$this->configData = $configData;
|
||||
self::$configData = $configData;
|
||||
|
||||
self::$timeUpdated = $configData->getConfigDate();
|
||||
|
||||
@@ -236,10 +233,10 @@ class ConfigFileService implements ConfigInterface
|
||||
|| $configData === null
|
||||
|| $this->isCacheExpired()
|
||||
) {
|
||||
$this->configData = $this->loadConfigFromFile();
|
||||
$this->fileCache->save($this->configData);
|
||||
self::$configData = $this->loadConfigFromFile();
|
||||
$this->fileCache->save(self::$configData);
|
||||
|
||||
return $this->configData;
|
||||
return self::$configData;
|
||||
}
|
||||
|
||||
return $configData;
|
||||
@@ -247,7 +244,7 @@ class ConfigFileService implements ConfigInterface
|
||||
processException($e);
|
||||
}
|
||||
|
||||
return $this->configData;
|
||||
return self::$configData;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,7 +254,7 @@ class ConfigFileService implements ConfigInterface
|
||||
*/
|
||||
public function getConfigData(): ConfigDataInterface
|
||||
{
|
||||
return clone $this->configData;
|
||||
return clone self::$configData;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,10 +263,10 @@ class ConfigFileService implements ConfigInterface
|
||||
*/
|
||||
public function generateUpgradeKey(): ConfigInterface
|
||||
{
|
||||
if (empty($this->configData->getUpgradeKey())) {
|
||||
if (empty(self::$configData->getUpgradeKey())) {
|
||||
logger('Generating upgrade key');
|
||||
|
||||
return $this->saveConfig($this->configData->setUpgradeKey(PasswordUtil::generateRandomBytes(16)), false);
|
||||
return $this->saveConfig(self::$configData->setUpgradeKey(PasswordUtil::generateRandomBytes(16)), false);
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
||||
@@ -53,7 +53,7 @@ interface DatabaseSetupInterface
|
||||
/**
|
||||
* Crear la base de datos
|
||||
*/
|
||||
public function createDatabase();
|
||||
public function createDatabase(?string $dbUser = null);
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
@@ -64,7 +64,7 @@ interface DatabaseSetupInterface
|
||||
* Deshacer la instalación en caso de fallo.
|
||||
* Esta función elimina la base de datos y el usuario de sysPass
|
||||
*/
|
||||
public function rollback();
|
||||
public function rollback(?string $dbUser = null);
|
||||
|
||||
/**
|
||||
* Crear la estructura de la base de datos.
|
||||
|
||||
49
lib/SP/Domain/Install/In/InstallDataFactory.php
Normal file
49
lib/SP/Domain/Install/In/InstallDataFactory.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/*
|
||||
* sysPass
|
||||
*
|
||||
* @author nuxsmin
|
||||
* @link https://syspass.org
|
||||
* @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org
|
||||
*
|
||||
* This file is part of sysPass.
|
||||
*
|
||||
* sysPass is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sysPass is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace SP\Domain\Install\In;
|
||||
|
||||
use SP\Http\RequestInterface;
|
||||
|
||||
/**
|
||||
* Class InstallDataAdapter
|
||||
*/
|
||||
final class InstallDataFactory
|
||||
{
|
||||
public static function buildFromRequest(RequestInterface $request): InstallData
|
||||
{
|
||||
$installData = new InstallData();
|
||||
$installData->setSiteLang($request->analyzeString('sitelang', 'en_US'));
|
||||
$installData->setAdminLogin($request->analyzeString('adminlogin', 'admin'));
|
||||
$installData->setAdminPass($request->analyzeEncrypted('adminpass'));
|
||||
$installData->setMasterPassword($request->analyzeEncrypted('masterpassword'));
|
||||
$installData->setDbAdminUser($request->analyzeString('dbuser', 'root'));
|
||||
$installData->setDbAdminPass($request->analyzeEncrypted('dbpass'));
|
||||
$installData->setDbName($request->analyzeString('dbname', 'syspass'));
|
||||
$installData->setDbHost($request->analyzeString('dbhost', 'localhost'));
|
||||
$installData->setHostingMode($request->analyzeBool('hostingmode', false));
|
||||
|
||||
return $installData;
|
||||
}
|
||||
}
|
||||
@@ -38,5 +38,5 @@ interface InstallerServiceInterface
|
||||
* @throws InvalidArgumentException
|
||||
* @throws SPException
|
||||
*/
|
||||
public function run(DatabaseSetupInterface $databaseSetup, InstallData $installData): InstallerServiceInterface;
|
||||
public function run(InstallData $installData): InstallerServiceInterface;
|
||||
}
|
||||
42
lib/SP/Domain/Install/MysqlSetupBuilderInterface.php
Normal file
42
lib/SP/Domain/Install/MysqlSetupBuilderInterface.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/*
|
||||
* sysPass
|
||||
*
|
||||
* @author nuxsmin
|
||||
* @link https://syspass.org
|
||||
* @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org
|
||||
*
|
||||
* This file is part of sysPass.
|
||||
*
|
||||
* sysPass is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sysPass is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace SP\Domain\Install;
|
||||
|
||||
|
||||
use SP\Domain\Install\In\InstallData;
|
||||
|
||||
/**
|
||||
* Class DatabaseSetupBuilder
|
||||
*/
|
||||
interface MysqlSetupBuilderInterface
|
||||
{
|
||||
/**
|
||||
* @param \SP\Domain\Install\In\InstallData $installData
|
||||
*
|
||||
* @return \SP\Domain\Install\DatabaseSetupInterface
|
||||
* @throws \SP\Core\Exceptions\SPException
|
||||
*/
|
||||
public static function build(InstallData $installData): DatabaseSetupInterface;
|
||||
}
|
||||
@@ -48,10 +48,6 @@ use SP\Domain\User\UserProfileServiceInterface;
|
||||
use SP\Domain\User\UserServiceInterface;
|
||||
use SP\Http\RequestInterface;
|
||||
use SP\Infrastructure\Database\DatabaseConnectionData;
|
||||
use SP\Infrastructure\Database\DatabaseUtil;
|
||||
use SP\Infrastructure\Database\MySQLFileParser;
|
||||
use SP\Infrastructure\Database\MySQLHandler;
|
||||
use SP\Infrastructure\File\FileHandler;
|
||||
use SP\Util\VersionUtil;
|
||||
|
||||
defined('APP_ROOT') || die();
|
||||
@@ -74,8 +70,9 @@ final class InstallerService implements InstallerServiceInterface
|
||||
private UserGroupServiceInterface $userGroupService;
|
||||
private UserProfileServiceInterface $userProfileService;
|
||||
private ConfigServiceInterface $configService;
|
||||
private ?DatabaseSetupInterface $databaseSetup = null;
|
||||
private ?InstallData $installData = null;
|
||||
private DatabaseConnectionData $databaseConnectionData;
|
||||
private DatabaseSetupInterface $databaseSetup;
|
||||
private ?InstallData $installData = null;
|
||||
|
||||
public function __construct(
|
||||
RequestInterface $request,
|
||||
@@ -83,7 +80,9 @@ final class InstallerService implements InstallerServiceInterface
|
||||
UserServiceInterface $userService,
|
||||
UserGroupServiceInterface $userGroupService,
|
||||
UserProfileServiceInterface $userProfileService,
|
||||
ConfigServiceInterface $configService
|
||||
ConfigServiceInterface $configService,
|
||||
DatabaseConnectionData $databaseConnectionData,
|
||||
DatabaseSetupInterface $databaseSetup
|
||||
) {
|
||||
$this->request = $request;
|
||||
$this->config = $config;
|
||||
@@ -91,42 +90,16 @@ final class InstallerService implements InstallerServiceInterface
|
||||
$this->userGroupService = $userGroupService;
|
||||
$this->userProfileService = $userProfileService;
|
||||
$this->configService = $configService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \SP\Domain\Install\In\InstallData $installData
|
||||
* @param $configData
|
||||
*
|
||||
* @return \SP\Domain\Install\DatabaseSetupInterface
|
||||
* @throws \SP\Core\Exceptions\SPException
|
||||
*/
|
||||
public static function getDatabaseSetup(InstallData $installData, $configData): DatabaseSetupInterface
|
||||
{
|
||||
$connectionData = (new DatabaseConnectionData())
|
||||
->setDbHost($installData->getDbHost())
|
||||
->setDbPort($installData->getDbPort())
|
||||
->setDbSocket($installData->getDbSocket())
|
||||
->setDbUser($installData->getDbAdminUser())
|
||||
->setDbPass($installData->getDbAdminPass());
|
||||
|
||||
if ($installData->getBackendType() === 'mysql') {
|
||||
$parser = new MySQLFileParser(new FileHandler(SQL_PATH.DIRECTORY_SEPARATOR.'dbstructure.sql'));
|
||||
|
||||
$mySQLHandler = new MySQLHandler($connectionData);
|
||||
|
||||
return new MysqlService($mySQLHandler, $installData, $configData, $parser, new DatabaseUtil($mySQLHandler));
|
||||
}
|
||||
|
||||
throw new SPException(__u('Unimplemented'), SPException::ERROR, __u('Wrong backend type'));
|
||||
$this->databaseConnectionData = $databaseConnectionData;
|
||||
$this->databaseSetup = $databaseSetup;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
* @throws SPException
|
||||
*/
|
||||
public function run(DatabaseSetupInterface $databaseSetup, InstallData $installData): InstallerServiceInterface
|
||||
public function run(InstallData $installData): InstallerServiceInterface
|
||||
{
|
||||
$this->databaseSetup = $databaseSetup;
|
||||
$this->installData = $installData;
|
||||
|
||||
$this->checkData();
|
||||
@@ -310,6 +283,10 @@ final class InstallerService implements InstallerServiceInterface
|
||||
*/
|
||||
private function setupDb(ConfigDataInterface $configData): void
|
||||
{
|
||||
$user = null;
|
||||
|
||||
$this->databaseSetup->connectDatabase();
|
||||
|
||||
if ($this->installData->isHostingMode()) {
|
||||
// Save DB connection user and pass
|
||||
$configData->setDbUser($this->installData->getDbAdminUser());
|
||||
@@ -323,10 +300,11 @@ final class InstallerService implements InstallerServiceInterface
|
||||
|
||||
$this->config->updateConfig($configData);
|
||||
|
||||
$this->databaseSetup->connectDatabase();
|
||||
$this->databaseSetup->createDatabase();
|
||||
$this->databaseSetup->createDatabase($user);
|
||||
$this->databaseSetup->createDBStructure();
|
||||
$this->databaseSetup->checkConnection();
|
||||
|
||||
$this->databaseConnectionData->refreshFromConfig($configData);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -346,15 +324,9 @@ final class InstallerService implements InstallerServiceInterface
|
||||
} catch (Exception $e) {
|
||||
processException($e);
|
||||
|
||||
$this->databaseSetup->rollback();
|
||||
$this->databaseSetup->rollback($this->config->getConfigData()->getDbUser());
|
||||
|
||||
throw new SPException(
|
||||
$e->getMessage(),
|
||||
SPException::CRITICAL,
|
||||
__u('Warn to developer'),
|
||||
$e->getCode(),
|
||||
$e
|
||||
);
|
||||
throw new SPException($e->getMessage(), SPException::CRITICAL, __u('Warn to developer'), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,7 +363,7 @@ final class InstallerService implements InstallerServiceInterface
|
||||
} catch (Exception $e) {
|
||||
processException($e);
|
||||
|
||||
$this->databaseSetup->rollback();
|
||||
$this->databaseSetup->rollback($this->config->getConfigData()->getDbUser());
|
||||
|
||||
throw new SPException(
|
||||
$e->getMessage(),
|
||||
|
||||
@@ -26,12 +26,11 @@ namespace SP\Domain\Install\Services;
|
||||
|
||||
use PDOException;
|
||||
use SP\Core\Exceptions\SPException;
|
||||
use SP\Domain\Config\In\ConfigDataInterface;
|
||||
use SP\Domain\Install\DatabaseSetupInterface;
|
||||
use SP\Domain\Install\In\InstallData;
|
||||
use SP\Infrastructure\Database\DatabaseFileInterface;
|
||||
use SP\Infrastructure\Database\DatabaseUtil;
|
||||
use SP\Infrastructure\Database\DBStorageInterface;
|
||||
use SP\Infrastructure\Database\DbStorageInterface;
|
||||
use SP\Infrastructure\File\FileException;
|
||||
use SP\Util\PasswordUtil;
|
||||
|
||||
@@ -43,8 +42,7 @@ use SP\Util\PasswordUtil;
|
||||
final class MysqlService implements DatabaseSetupInterface
|
||||
{
|
||||
private InstallData $installData;
|
||||
private DBStorageInterface $DBStorage;
|
||||
private ConfigDataInterface $configData;
|
||||
private DbStorageInterface $DBStorage;
|
||||
private DatabaseFileInterface $databaseFile;
|
||||
private DatabaseUtil $databaseUtil;
|
||||
|
||||
@@ -53,14 +51,12 @@ final class MysqlService implements DatabaseSetupInterface
|
||||
*
|
||||
*/
|
||||
public function __construct(
|
||||
DBStorageInterface $DBStorage,
|
||||
DbStorageInterface $DBStorage,
|
||||
InstallData $installData,
|
||||
ConfigDataInterface $configData,
|
||||
DatabaseFileInterface $databaseFile,
|
||||
DatabaseUtil $databaseUtil
|
||||
) {
|
||||
$this->installData = $installData;
|
||||
$this->configData = $configData;
|
||||
$this->DBStorage = $DBStorage;
|
||||
$this->databaseFile = $databaseFile;
|
||||
$this->databaseUtil = $databaseUtil;
|
||||
@@ -190,7 +186,7 @@ final class MysqlService implements DatabaseSetupInterface
|
||||
*
|
||||
* @throws SPException
|
||||
*/
|
||||
public function createDatabase(): void
|
||||
public function createDatabase(?string $dbUser = null): void
|
||||
{
|
||||
if (!$this->installData->isHostingMode()) {
|
||||
|
||||
@@ -228,7 +224,7 @@ final class MysqlService implements DatabaseSetupInterface
|
||||
sprintf(
|
||||
$query,
|
||||
$this->installData->getDbName(),
|
||||
$dbc->quote($this->configData->getDbUser()),
|
||||
$dbc->quote($dbUser),
|
||||
$dbc->quote($this->installData->getDbAuthHost())
|
||||
)
|
||||
);
|
||||
@@ -240,7 +236,7 @@ final class MysqlService implements DatabaseSetupInterface
|
||||
sprintf(
|
||||
$query,
|
||||
$this->installData->getDbName(),
|
||||
$dbc->quote($this->configData->getDbUser()),
|
||||
$dbc->quote($dbUser),
|
||||
$dbc->quote($this->installData->getDbAuthHostDns())
|
||||
)
|
||||
);
|
||||
@@ -250,7 +246,7 @@ final class MysqlService implements DatabaseSetupInterface
|
||||
} catch (PDOException $e) {
|
||||
processException($e);
|
||||
|
||||
$this->rollback();
|
||||
$this->rollback($dbUser);
|
||||
|
||||
throw new SPException(
|
||||
sprintf(__('Error while setting the database permissions (\'%s\')'), $e->getMessage()),
|
||||
@@ -274,7 +270,7 @@ final class MysqlService implements DatabaseSetupInterface
|
||||
return (int)$sth->fetchColumn() === 1;
|
||||
}
|
||||
|
||||
public function rollback(): void
|
||||
public function rollback(?string $dbUser = null): void
|
||||
{
|
||||
$dbc = $this->DBStorage->getConnectionSimple();
|
||||
|
||||
@@ -295,22 +291,25 @@ final class MysqlService implements DatabaseSetupInterface
|
||||
$this->installData->getDbName()
|
||||
)
|
||||
);
|
||||
$dbc->exec(
|
||||
sprintf(
|
||||
'DROP USER IF EXISTS %s@%s',
|
||||
$dbc->quote($this->configData->getDbUser()),
|
||||
$dbc->quote($this->installData->getDbAuthHost())
|
||||
)
|
||||
);
|
||||
|
||||
if ($this->installData->getDbAuthHost() !== $this->installData->getDbAuthHostDns()) {
|
||||
if ($dbUser) {
|
||||
$dbc->exec(
|
||||
sprintf(
|
||||
'DROP USER IF EXISTS %s@%s',
|
||||
$dbc->quote($this->configData->getDbUser()),
|
||||
$dbc->quote($this->installData->getDbAuthHostDns())
|
||||
$dbc->quote($dbUser),
|
||||
$dbc->quote($this->installData->getDbAuthHost())
|
||||
)
|
||||
);
|
||||
|
||||
if ($this->installData->getDbAuthHost() !== $this->installData->getDbAuthHostDns()) {
|
||||
$dbc->exec(
|
||||
sprintf(
|
||||
'DROP USER IF EXISTS %s@%s',
|
||||
$dbc->quote($dbUser),
|
||||
$dbc->quote($this->installData->getDbAuthHostDns())
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
59
lib/SP/Domain/Install/Services/MysqlSetupBuilder.php
Normal file
59
lib/SP/Domain/Install/Services/MysqlSetupBuilder.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/*
|
||||
* sysPass
|
||||
*
|
||||
* @author nuxsmin
|
||||
* @link https://syspass.org
|
||||
* @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org
|
||||
*
|
||||
* This file is part of sysPass.
|
||||
*
|
||||
* sysPass is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sysPass is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace SP\Domain\Install\Services;
|
||||
|
||||
|
||||
use SP\Domain\Install\DatabaseSetupInterface;
|
||||
use SP\Domain\Install\In\InstallData;
|
||||
use SP\Domain\Install\MysqlSetupBuilderInterface;
|
||||
use SP\Infrastructure\Database\DatabaseConnectionData;
|
||||
use SP\Infrastructure\Database\DatabaseUtil;
|
||||
use SP\Infrastructure\Database\MysqlFileParser;
|
||||
use SP\Infrastructure\Database\MysqlHandler;
|
||||
use SP\Infrastructure\File\FileHandler;
|
||||
|
||||
/**
|
||||
* Class DatabaseSetupBuilder
|
||||
*/
|
||||
final class MysqlSetupBuilder implements MysqlSetupBuilderInterface
|
||||
{
|
||||
private const DATABASE_SCHEMA_FILE = SQL_PATH.DIRECTORY_SEPARATOR.'dbstructure.sql';
|
||||
|
||||
public static function build(InstallData $installData): DatabaseSetupInterface
|
||||
{
|
||||
$connectionData = (new DatabaseConnectionData())
|
||||
->setDbHost($installData->getDbHost())
|
||||
->setDbPort($installData->getDbPort())
|
||||
->setDbSocket($installData->getDbSocket())
|
||||
->setDbUser($installData->getDbAdminUser())
|
||||
->setDbPass($installData->getDbAdminPass());
|
||||
|
||||
$parser = new MysqlFileParser(new FileHandler(self::DATABASE_SCHEMA_FILE));
|
||||
|
||||
$mysqlHandler = new MysqlHandler($connectionData);
|
||||
|
||||
return new MysqlService($mysqlHandler, $installData, $parser, new DatabaseUtil($mysqlHandler));
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ use SP\Domain\Common\Services\Service;
|
||||
use SP\Domain\Config\In\ConfigDataInterface;
|
||||
use SP\Domain\Persistence\UpgradeDatabaseServiceInterface;
|
||||
use SP\Infrastructure\Database\DatabaseInterface;
|
||||
use SP\Infrastructure\Database\MySQLFileParser;
|
||||
use SP\Infrastructure\Database\MysqlFileParser;
|
||||
use SP\Infrastructure\File\FileException;
|
||||
use SP\Infrastructure\File\FileHandler;
|
||||
use SP\Providers\Log\FileLogHandler;
|
||||
@@ -205,7 +205,7 @@ final class UpgradeDatabaseService extends Service implements UpgradeDatabaseSer
|
||||
'.sql';
|
||||
|
||||
try {
|
||||
return (new MySQLFileParser(new FileHandler($fileName)))->parse('$$');
|
||||
return (new MysqlFileParser(new FileHandler($fileName)))->parse('$$');
|
||||
} catch (FileException $e) {
|
||||
processException($e);
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ use RuntimeException;
|
||||
use SP\DataModel\DataModelInterface;
|
||||
use SP\Domain\Common\In\RepositoryInterface;
|
||||
use SP\Infrastructure\Database\DatabaseUtil;
|
||||
use SP\Infrastructure\Database\DBStorageInterface;
|
||||
use SP\Infrastructure\Database\DbStorageInterface;
|
||||
|
||||
/**
|
||||
* Trait RepositoryItemTrait
|
||||
@@ -79,7 +79,7 @@ trait RepositoryItemTrait
|
||||
*/
|
||||
protected function makeItemHash(
|
||||
string $name,
|
||||
DBStorageInterface $DBStorage
|
||||
DbStorageInterface $DBStorage
|
||||
): string {
|
||||
$charsSrc = ['.', ' ', '_', ', ', '-', ';', '\'', '"', ':', '(', ')', '|', '/'];
|
||||
|
||||
|
||||
@@ -44,18 +44,18 @@ final class Database implements DatabaseInterface
|
||||
protected int $numRows = 0;
|
||||
protected int $numFields = 0;
|
||||
protected ?array $lastResult = null;
|
||||
protected DBStorageInterface $dbHandler;
|
||||
protected DbStorageInterface $dbHandler;
|
||||
private ?int $lastId = null;
|
||||
private EventDispatcher $eventDispatcher;
|
||||
|
||||
/**
|
||||
* DB constructor.
|
||||
*
|
||||
* @param DBStorageInterface $dbHandler
|
||||
* @param DbStorageInterface $dbHandler
|
||||
* @param EventDispatcher $eventDispatcher
|
||||
*/
|
||||
public function __construct(
|
||||
DBStorageInterface $dbHandler,
|
||||
DbStorageInterface $dbHandler,
|
||||
EventDispatcher $eventDispatcher
|
||||
) {
|
||||
$this->dbHandler = $dbHandler;
|
||||
@@ -82,7 +82,7 @@ final class Database implements DatabaseInterface
|
||||
return $this->lastId;
|
||||
}
|
||||
|
||||
public function getDbHandler(): DBStorageInterface
|
||||
public function getDbHandler(): DbStorageInterface
|
||||
{
|
||||
return $this->dbHandler;
|
||||
}
|
||||
@@ -291,7 +291,7 @@ final class Database implements DatabaseInterface
|
||||
array $options = [],
|
||||
?bool $buffered = null
|
||||
): PDOStatement {
|
||||
if ($buffered === false && $this->dbHandler instanceof MySQLHandler) {
|
||||
if ($buffered === false && $this->dbHandler instanceof MysqlHandler) {
|
||||
$this->dbHandler
|
||||
->getConnection()
|
||||
->setAttribute(
|
||||
|
||||
@@ -51,6 +51,16 @@ final class DatabaseConnectionData
|
||||
->setDbSocket($configData->getDbSocket());
|
||||
}
|
||||
|
||||
public function refreshFromConfig(ConfigDataInterface $configData): DatabaseConnectionData
|
||||
{
|
||||
return $this->setDbHost($configData->getDbHost())
|
||||
->setDbName($configData->getDbName())
|
||||
->setDbUser($configData->getDbUser())
|
||||
->setDbPass($configData->getDbPass())
|
||||
->setDbPort($configData->getDbPort())
|
||||
->setDbSocket($configData->getDbSocket());
|
||||
}
|
||||
|
||||
public function getDbHost(): ?string
|
||||
{
|
||||
return $this->dbHost;
|
||||
|
||||
@@ -53,7 +53,7 @@ interface DatabaseInterface
|
||||
*/
|
||||
public function getFullRowCount(QueryData $queryData): int;
|
||||
|
||||
public function getDbHandler(): DBStorageInterface;
|
||||
public function getDbHandler(): DbStorageInterface;
|
||||
|
||||
public function getNumRows(): int;
|
||||
|
||||
|
||||
@@ -65,12 +65,12 @@ final class DatabaseUtil
|
||||
'account_data_v',
|
||||
'account_search_v',
|
||||
];
|
||||
private DBStorageInterface $DBStorage;
|
||||
private DbStorageInterface $DBStorage;
|
||||
|
||||
/**
|
||||
* DatabaseUtil constructor.
|
||||
*/
|
||||
public function __construct(DBStorageInterface $DBStorage)
|
||||
public function __construct(DbStorageInterface $DBStorage)
|
||||
{
|
||||
$this->DBStorage = $DBStorage;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ use PDO;
|
||||
*
|
||||
* @package SP\Storage
|
||||
*/
|
||||
interface DBStorageInterface
|
||||
interface DbStorageInterface
|
||||
{
|
||||
/**
|
||||
* Obtener una conexión PDO
|
||||
@@ -33,7 +33,7 @@ use SP\Infrastructure\File\FileHandlerInterface;
|
||||
*
|
||||
* @package SP\Storage
|
||||
*/
|
||||
final class MySQLFileParser implements DatabaseFileInterface
|
||||
final class MysqlFileParser implements DatabaseFileInterface
|
||||
{
|
||||
private FileHandler $fileHandler;
|
||||
|
||||
@@ -35,7 +35,7 @@ defined('APP_ROOT') || die();
|
||||
*
|
||||
* Esta clase se encarga de crear las conexiones a la BD
|
||||
*/
|
||||
final class MySQLHandler implements DBStorageInterface
|
||||
final class MysqlHandler implements DbStorageInterface
|
||||
{
|
||||
public const STATUS_OK = 0;
|
||||
public const STATUS_KO = 1;
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* @author nuxsmin
|
||||
* @link https://syspass.org
|
||||
* @copyright 2012-2021, Rubén Domínguez nuxsmin@$syspass.org
|
||||
* @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org
|
||||
*
|
||||
* This file is part of sysPass.
|
||||
*
|
||||
@@ -19,7 +19,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace SP\Tests\Modules\Api;
|
||||
@@ -40,8 +40,8 @@ use SP\Domain\Auth\Services\AuthTokenService;
|
||||
use SP\Domain\Config\ConfigInterface;
|
||||
use SP\Domain\Config\In\ConfigDataInterface;
|
||||
use SP\Infrastructure\Database\DatabaseConnectionData;
|
||||
use SP\Infrastructure\Database\DBStorageInterface;
|
||||
use SP\Infrastructure\Database\MySQLHandler;
|
||||
use SP\Infrastructure\Database\DbStorageInterface;
|
||||
use SP\Infrastructure\Database\MysqlHandler;
|
||||
use SP\Tests\DatabaseTrait;
|
||||
use stdClass;
|
||||
use function DI\create;
|
||||
@@ -153,7 +153,7 @@ abstract class ApiTestCase extends TestCase
|
||||
|
||||
return new ApiRequest(json_encode($data, JSON_THROW_ON_ERROR));
|
||||
},
|
||||
DBStorageInterface::class => create(MySQLHandler::class)
|
||||
DbStorageInterface::class => create(MysqlHandler::class)
|
||||
->constructor($databaseConnectionData),
|
||||
ConfigDataInterface::class => static function (ConfigInterface $config) use ($databaseConnectionData) {
|
||||
$configData = $config->getConfigData()
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* @author nuxsmin
|
||||
* @link https://syspass.org
|
||||
* @copyright 2012-2021, Rubén Domínguez nuxsmin@$syspass.org
|
||||
* @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org
|
||||
*
|
||||
* This file is part of sysPass.
|
||||
*
|
||||
@@ -19,7 +19,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace SP\Tests\Modules\Cli;
|
||||
@@ -31,7 +31,7 @@ use Exception;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use SP\Core\Context\ContextInterface;
|
||||
use SP\Infrastructure\Database\DBStorageInterface;
|
||||
use SP\Infrastructure\Database\DbStorageInterface;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
use function SP\Tests\getDbHandler;
|
||||
use const SP\Tests\APP_DEFINITIONS_FILE;
|
||||
@@ -97,6 +97,6 @@ abstract class CliTestCase extends TestCase
|
||||
|
||||
protected function setupDatabase(): void
|
||||
{
|
||||
self::$dic->set(DBStorageInterface::class, getDbHandler());
|
||||
self::$dic->set(DbStorageInterface::class, getDbHandler());
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* sysPass
|
||||
*
|
||||
* @author nuxsmin
|
||||
* @link https://syspass.org
|
||||
* @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org
|
||||
* @author nuxsmin
|
||||
* @link https://syspass.org
|
||||
* @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org
|
||||
*
|
||||
* This file is part of sysPass.
|
||||
*
|
||||
@@ -19,7 +19,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace SP\Tests\Services\Backup;
|
||||
@@ -29,7 +29,7 @@ use SP\Domain\Export\Services\BackupFiles;
|
||||
use SP\Domain\Export\Services\FileBackupService;
|
||||
use SP\Infrastructure\Database\Database;
|
||||
use SP\Infrastructure\Database\DatabaseUtil;
|
||||
use SP\Infrastructure\Database\MySQLHandler;
|
||||
use SP\Infrastructure\Database\MysqlHandler;
|
||||
use SP\Infrastructure\File\ArchiveHandler;
|
||||
use SP\Tests\UnitaryTestCase;
|
||||
|
||||
@@ -63,7 +63,7 @@ class FileBackupServiceTest extends UnitaryTestCase
|
||||
|
||||
$database = $this->createStub(Database::class);
|
||||
$database->method('getDbHandler')->willReturn(
|
||||
$this->createStub(MySQLHandler::class)
|
||||
$this->createStub(MysqlHandler::class)
|
||||
);
|
||||
|
||||
$archiveHandler = $this->createMock(ArchiveHandler::class);
|
||||
|
||||
@@ -28,7 +28,9 @@ use Exception;
|
||||
use SP\Core\Exceptions\InvalidArgumentException;
|
||||
use SP\Core\Exceptions\SPException;
|
||||
use SP\Domain\Config\Services\ConfigService;
|
||||
use SP\Domain\Install\DatabaseSetupInterface;
|
||||
use SP\Domain\Install\In\InstallData;
|
||||
use SP\Domain\Install\InstallerServiceInterface;
|
||||
use SP\Domain\Install\Services\InstallerService;
|
||||
use SP\Domain\User\Services\UserGroupService;
|
||||
use SP\Domain\User\Services\UserProfileService;
|
||||
@@ -36,6 +38,7 @@ use SP\Domain\User\Services\UserService;
|
||||
use SP\Domain\User\UserProfileServiceInterface;
|
||||
use SP\Http\Request;
|
||||
use SP\Http\RequestInterface;
|
||||
use SP\Infrastructure\Database\DatabaseConnectionData;
|
||||
use SP\Tests\UnitaryTestCase;
|
||||
use SP\Util\VersionUtil;
|
||||
|
||||
@@ -93,7 +96,7 @@ class InstallerTest extends UnitaryTestCase
|
||||
|
||||
$installer = $this->getDefaultInstaller();
|
||||
|
||||
$installer->run($this->databaseSetup, $params);
|
||||
$installer->run($params);
|
||||
|
||||
$configData = $this->config->getConfigData();
|
||||
|
||||
@@ -131,7 +134,7 @@ class InstallerTest extends UnitaryTestCase
|
||||
/**
|
||||
* @return \SP\Domain\Install\InstallerServiceInterface
|
||||
*/
|
||||
private function getDefaultInstaller(): \SP\Domain\Install\InstallerServiceInterface
|
||||
private function getDefaultInstaller(): InstallerServiceInterface
|
||||
{
|
||||
return new InstallerService(
|
||||
$this->request,
|
||||
@@ -139,7 +142,9 @@ class InstallerTest extends UnitaryTestCase
|
||||
$this->userService,
|
||||
$this->userGroupService,
|
||||
$this->userProfileService,
|
||||
$this->configService
|
||||
$this->configService,
|
||||
new DatabaseConnectionData(),
|
||||
$this->databaseSetup
|
||||
);
|
||||
}
|
||||
|
||||
@@ -160,7 +165,7 @@ class InstallerTest extends UnitaryTestCase
|
||||
|
||||
$installer = $this->getDefaultInstaller();
|
||||
|
||||
$installer->run($this->databaseSetup, $params);
|
||||
$installer->run($params);
|
||||
|
||||
$configData = $this->config->getConfigData();
|
||||
|
||||
@@ -185,7 +190,7 @@ class InstallerTest extends UnitaryTestCase
|
||||
|
||||
$installer = $this->getDefaultInstaller();
|
||||
|
||||
$installer->run($this->databaseSetup, $params);
|
||||
$installer->run($params);
|
||||
|
||||
$this->assertEquals($params->getDbHost(), $params->getDbAuthHost());
|
||||
}
|
||||
@@ -206,7 +211,7 @@ class InstallerTest extends UnitaryTestCase
|
||||
|
||||
$installer = $this->getDefaultInstaller();
|
||||
|
||||
$installer->run($this->databaseSetup, $params);
|
||||
$installer->run($params);
|
||||
|
||||
$this->assertEquals(SELF_IP_ADDRESS, $params->getDbAuthHost());
|
||||
$this->assertEquals('host', $params->getDbHost());
|
||||
@@ -227,7 +232,7 @@ class InstallerTest extends UnitaryTestCase
|
||||
|
||||
$installer = $this->getDefaultInstaller();
|
||||
|
||||
$installer->run($this->databaseSetup, $params);
|
||||
$installer->run($params);
|
||||
|
||||
$configData = $this->config->getConfigData();
|
||||
|
||||
@@ -252,7 +257,7 @@ class InstallerTest extends UnitaryTestCase
|
||||
$this->expectException(SPException::class);
|
||||
$this->expectExceptionMessage('Error while creating \'admin\' user');
|
||||
|
||||
$installer->run($this->databaseSetup, $params);
|
||||
$installer->run($params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,7 +278,7 @@ class InstallerTest extends UnitaryTestCase
|
||||
$this->expectException(SPException::class);
|
||||
$this->expectExceptionMessage('Create exception');
|
||||
|
||||
$installer->run($this->databaseSetup, $params);
|
||||
$installer->run($params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -290,7 +295,7 @@ class InstallerTest extends UnitaryTestCase
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Please, enter the admin username');
|
||||
|
||||
$installer->run($this->databaseSetup, $params);
|
||||
$installer->run($params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -307,7 +312,7 @@ class InstallerTest extends UnitaryTestCase
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Please, enter the admin\'s password');
|
||||
|
||||
$installer->run($this->databaseSetup, $params);
|
||||
$installer->run($params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -324,7 +329,7 @@ class InstallerTest extends UnitaryTestCase
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Please, enter the Master Password');
|
||||
|
||||
$installer->run($this->databaseSetup, $params);
|
||||
$installer->run($params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -341,7 +346,7 @@ class InstallerTest extends UnitaryTestCase
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Master password too short');
|
||||
|
||||
$installer->run($this->databaseSetup, $params);
|
||||
$installer->run($params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -358,7 +363,7 @@ class InstallerTest extends UnitaryTestCase
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Please, enter the database user');
|
||||
|
||||
$installer->run($this->databaseSetup, $params);
|
||||
$installer->run($params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -375,7 +380,7 @@ class InstallerTest extends UnitaryTestCase
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Please, enter the database password');
|
||||
|
||||
$installer->run($this->databaseSetup, $params);
|
||||
$installer->run($params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -392,7 +397,7 @@ class InstallerTest extends UnitaryTestCase
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Please, enter the database name');
|
||||
|
||||
$installer->run($this->databaseSetup, $params);
|
||||
$installer->run($params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -409,7 +414,7 @@ class InstallerTest extends UnitaryTestCase
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Database name cannot contain "."');
|
||||
|
||||
$installer->run($this->databaseSetup, $params);
|
||||
$installer->run($params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -426,31 +431,7 @@ class InstallerTest extends UnitaryTestCase
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Please, enter the database server');
|
||||
|
||||
$installer->run($this->databaseSetup, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \SP\Core\Exceptions\SPException
|
||||
*
|
||||
* @doesNotPerformAssertions
|
||||
*/
|
||||
public function testGetDatabaseSetupIsSuccessful()
|
||||
{
|
||||
InstallerService::getDatabaseSetup($this->getInstallData(), $this->config->getConfigData());
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \SP\Core\Exceptions\SPException
|
||||
*/
|
||||
public function testGetDatabaseSetupIsNotSuccessfulWithWrongBackend()
|
||||
{
|
||||
$installData = $this->getInstallData();
|
||||
$installData->setBackendType('test');
|
||||
|
||||
$this->expectException(SPException::class);
|
||||
$this->expectExceptionMessage('Unimplemented');
|
||||
|
||||
InstallerService::getDatabaseSetup($installData, $this->config->getConfigData());
|
||||
$installer->run($params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -459,7 +440,7 @@ class InstallerTest extends UnitaryTestCase
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->databaseSetup = $this->createMock(\SP\Domain\Install\DatabaseSetupInterface::class);
|
||||
$this->databaseSetup = $this->createMock(DatabaseSetupInterface::class);
|
||||
$this->userService = $this->createMock(UserService::class);
|
||||
$this->request = $this->createStub(Request::class);
|
||||
$this->configService = $this->createMock(ConfigService::class);
|
||||
|
||||
@@ -31,7 +31,7 @@ use SP\Domain\Install\In\InstallData;
|
||||
use SP\Domain\Install\Services\MysqlService;
|
||||
use SP\Infrastructure\Database\DatabaseFileInterface;
|
||||
use SP\Infrastructure\Database\DatabaseUtil;
|
||||
use SP\Infrastructure\Database\DBStorageInterface;
|
||||
use SP\Infrastructure\Database\DbStorageInterface;
|
||||
use SP\Infrastructure\File\FileException;
|
||||
use SP\Tests\Stubs\Pdo;
|
||||
use SP\Tests\UnitaryTestCase;
|
||||
@@ -43,9 +43,9 @@ use SP\Tests\UnitaryTestCase;
|
||||
*/
|
||||
class MySQLTest extends UnitaryTestCase
|
||||
{
|
||||
private DBStorageInterface $DBStorage;
|
||||
private MysqlService $mysql;
|
||||
private Pdo $pdo;
|
||||
private DbStorageInterface $DBStorage;
|
||||
private MysqlService $mysql;
|
||||
private Pdo $pdo;
|
||||
private InstallData $installData;
|
||||
private ConfigDataInterface $configData;
|
||||
private DatabaseFileInterface $databaseFile;
|
||||
@@ -162,7 +162,7 @@ class MySQLTest extends UnitaryTestCase
|
||||
->method('exec')
|
||||
->withConsecutive(...$execArguments);
|
||||
|
||||
$this->mysql->createDatabase();
|
||||
$this->mysql->createDatabase($this->configData->getDbUser());
|
||||
}
|
||||
|
||||
|
||||
@@ -246,7 +246,7 @@ class MySQLTest extends UnitaryTestCase
|
||||
->method('exec')
|
||||
->withConsecutive(...$execArguments);
|
||||
|
||||
$this->mysql->createDatabase();
|
||||
$this->mysql->createDatabase($this->configData->getDbUser());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -323,7 +323,7 @@ class MySQLTest extends UnitaryTestCase
|
||||
sprintf(__('Error while setting the database permissions (\'%s\')'), $pdoException->getMessage())
|
||||
);
|
||||
|
||||
$this->mysql->createDatabase();
|
||||
$this->mysql->createDatabase($this->configData->getDbUser());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -387,7 +387,7 @@ class MySQLTest extends UnitaryTestCase
|
||||
->method('exec')
|
||||
->withConsecutive(...$execArguments);
|
||||
|
||||
$this->mysql->rollback();
|
||||
$this->mysql->rollback($this->configData->getDbUser());
|
||||
}
|
||||
|
||||
public function testRollbackIsSuccessfulWithSameDnsHost(): void
|
||||
@@ -416,7 +416,7 @@ class MySQLTest extends UnitaryTestCase
|
||||
->method('exec')
|
||||
->withConsecutive(...$execArguments);
|
||||
|
||||
$this->mysql->rollback();
|
||||
$this->mysql->rollback($this->configData->getDbUser());
|
||||
}
|
||||
|
||||
public function testRollbackIsSuccessfulWithHostingMode(): void
|
||||
@@ -498,23 +498,9 @@ class MySQLTest extends UnitaryTestCase
|
||||
'DROP DATABASE IF EXISTS `%s`',
|
||||
$this->installData->getDbName()
|
||||
),
|
||||
],
|
||||
[
|
||||
sprintf(
|
||||
'DROP USER IF EXISTS %s@%s',
|
||||
$this->configData->getDbUser(),
|
||||
$this->installData->getDbAuthHost()
|
||||
),
|
||||
],
|
||||
[
|
||||
sprintf(
|
||||
'DROP USER IF EXISTS %s@%s',
|
||||
$this->configData->getDbUser(),
|
||||
$this->installData->getDbAuthHostDns()
|
||||
),
|
||||
],
|
||||
]
|
||||
];
|
||||
$matcher = $this->exactly(5);
|
||||
$matcher = $this->exactly(3);
|
||||
|
||||
$this->pdo->expects($matcher)
|
||||
->method('exec')
|
||||
@@ -552,23 +538,9 @@ class MySQLTest extends UnitaryTestCase
|
||||
'DROP DATABASE IF EXISTS `%s`',
|
||||
$this->installData->getDbName()
|
||||
),
|
||||
],
|
||||
[
|
||||
sprintf(
|
||||
'DROP USER IF EXISTS %s@%s',
|
||||
$this->configData->getDbUser(),
|
||||
$this->installData->getDbAuthHost()
|
||||
),
|
||||
],
|
||||
[
|
||||
sprintf(
|
||||
'DROP USER IF EXISTS %s@%s',
|
||||
$this->configData->getDbUser(),
|
||||
$this->installData->getDbAuthHostDns()
|
||||
),
|
||||
],
|
||||
]
|
||||
];
|
||||
$matcher = $this->exactly(4);
|
||||
$matcher = $this->exactly(2);
|
||||
|
||||
$this->pdo->expects($matcher)
|
||||
->method('exec')
|
||||
@@ -615,26 +587,10 @@ class MySQLTest extends UnitaryTestCase
|
||||
'DROP DATABASE IF EXISTS `%s`',
|
||||
$this->installData->getDbName()
|
||||
),
|
||||
],
|
||||
[
|
||||
sprintf(
|
||||
'DROP USER IF EXISTS %s@%s',
|
||||
$this->configData->getDbUser(),
|
||||
$this->installData->getDbAuthHost()
|
||||
),
|
||||
],
|
||||
[
|
||||
sprintf(
|
||||
'DROP USER IF EXISTS %s@%s',
|
||||
$this->configData->getDbUser(),
|
||||
$this->installData->getDbAuthHostDns()
|
||||
),
|
||||
],
|
||||
]
|
||||
];
|
||||
|
||||
$matcher = $this->exactly(3);
|
||||
|
||||
$this->pdo->expects($matcher)
|
||||
$this->pdo->expects(self::once())
|
||||
->method('exec')
|
||||
->withConsecutive(...$execArguments);
|
||||
|
||||
@@ -754,7 +710,7 @@ class MySQLTest extends UnitaryTestCase
|
||||
|
||||
$this->pdo = $this->getMockBuilder(Pdo::class)->enableProxyingToOriginalMethods()->getMock();
|
||||
|
||||
$this->DBStorage = $this->createMock(DBStorageInterface::class);
|
||||
$this->DBStorage = $this->createMock(DbStorageInterface::class);
|
||||
$this->DBStorage->method('getConnectionSimple')->willReturn($this->pdo);
|
||||
$this->databaseFile = $this->createMock(DatabaseFileInterface::class);
|
||||
|
||||
@@ -762,7 +718,7 @@ class MySQLTest extends UnitaryTestCase
|
||||
$this->configData = $this->config->getConfigData();
|
||||
$this->databaseUtil = $this->createMock(DatabaseUtil::class);
|
||||
$this->mysql = new MysqlService(
|
||||
$this->DBStorage, $this->installData, $this->configData, $this->databaseFile, $this->databaseUtil
|
||||
$this->DBStorage, $this->installData, $this->databaseFile, $this->databaseUtil
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* @author nuxsmin
|
||||
* @link https://syspass.org
|
||||
* @copyright 2012-2021, Rubén Domínguez nuxsmin@$syspass.org
|
||||
* @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org
|
||||
*
|
||||
* This file is part of sysPass.
|
||||
*
|
||||
@@ -34,8 +34,8 @@ use SP\Core\Exceptions\FileNotFoundException;
|
||||
use SP\DataModel\ProfileData;
|
||||
use SP\Domain\User\Services\UserLoginResponse;
|
||||
use SP\Infrastructure\Database\DatabaseConnectionData;
|
||||
use SP\Infrastructure\Database\DBStorageInterface;
|
||||
use SP\Infrastructure\Database\MySQLHandler;
|
||||
use SP\Infrastructure\Database\DbStorageInterface;
|
||||
use SP\Infrastructure\Database\MysqlHandler;
|
||||
use SP\Util\FileUtil;
|
||||
|
||||
define('DEBUG', true);
|
||||
@@ -133,19 +133,19 @@ function setupContext(): Container
|
||||
$context->setUserProfile(new ProfileData());
|
||||
|
||||
// Inicializar los datos de conexión a la BBDD
|
||||
$dic->set(DBStorageInterface::class, getDbHandler());
|
||||
$dic->set(DbStorageInterface::class, getDbHandler());
|
||||
|
||||
return $dic;
|
||||
}
|
||||
|
||||
function getDbHandler(?DatabaseConnectionData $connectionData = null): MySQLHandler
|
||||
function getDbHandler(?DatabaseConnectionData $connectionData = null): MysqlHandler
|
||||
{
|
||||
if ($connectionData === null) {
|
||||
// Establecer configuración de conexión con la BBDD
|
||||
$connectionData = DatabaseConnectionData::getFromEnvironment();
|
||||
}
|
||||
|
||||
return new MySQLHandler($connectionData);
|
||||
return new MysqlHandler($connectionData);
|
||||
}
|
||||
|
||||
function getResource(string $dir, string $file): string
|
||||
|
||||
Reference in New Issue
Block a user