diff --git a/app/config/actions.xml b/app/config/actions.xml
index b48c95eb..7fa0eb5b 100644
--- a/app/config/actions.xml
+++ b/app/config/actions.xml
@@ -565,6 +565,12 @@
Restablecer Plugin
plugin/reset
+
+ 1108
+ PLUGIN_DELETE
+ Eliminar Plugin
+ plugin/delete
+
703
USER_VIEW
diff --git a/app/modules/api/Init.php b/app/modules/api/Init.php
index e81dd776..2ed17ca6 100644
--- a/app/modules/api/Init.php
+++ b/app/modules/api/Init.php
@@ -33,7 +33,6 @@ use SP\Core\ModuleBase;
use SP\Services\Upgrade\UpgradeAppService;
use SP\Services\Upgrade\UpgradeDatabaseService;
use SP\Services\Upgrade\UpgradeUtil;
-use SP\Storage\Database\Database;
use SP\Storage\Database\DatabaseUtil;
use SP\Util\HttpUtil;
@@ -92,18 +91,26 @@ final class Init extends ModuleBase
HttpUtil::checkHttps($this->configData, $this->request);
// Checks if sysPass is installed
- if (!$this->checkInstalled()) {
- throw new InitializationException('Not installed');
- }
+ $this->checkInstalled();
// Checks if maintenance mode is turned on
- $this->checkMaintenanceMode($this->context);
+ if (!$this->checkMaintenanceMode($this->context)) {
+ throw new InitializationException('Maintenance mode');
+ }
// Checks if upgrade is needed
$this->checkUpgrade();
+ $databaseUtil = $this->container->get(DatabaseUtil::class);
+
// Checks if the database is set up
- DatabaseUtil::checkDatabaseExist($this->container->get(Database::class)->getDbHandler(), $this->configData->getDbName());
+ if (!$databaseUtil->checkDatabaseConnection()) {
+ throw new InitializationException('Database connection error');
+ }
+
+ if (!$databaseUtil->checkDatabaseTables($this->configData->getDbName())) {
+ throw new InitializationException('Database checking error');
+ }
// Initialize event handlers
$this->initEventHandlers();
@@ -112,10 +119,14 @@ final class Init extends ModuleBase
/**
* Comprueba que la aplicación esté instalada
* Esta función comprueba si la aplicación está instalada. Si no lo está, redirige al instalador.
+ *
+ * @throws InitializationException
*/
private function checkInstalled()
{
- return $this->configData->isInstalled();
+ if (!$this->configData->isInstalled()) {
+ throw new InitializationException('Not installed');
+ }
}
/**
diff --git a/app/modules/web/Controllers/ConfigManagerController.php b/app/modules/web/Controllers/ConfigManagerController.php
index 550fafdf..c6c51c0e 100644
--- a/app/modules/web/Controllers/ConfigManagerController.php
+++ b/app/modules/web/Controllers/ConfigManagerController.php
@@ -45,7 +45,6 @@ use SP\Services\User\UserService;
use SP\Services\UserGroup\UserGroupService;
use SP\Services\UserProfile\UserProfileService;
use SP\Storage\Database\DatabaseUtil;
-use SP\Storage\Database\DBStorageInterface;
use SP\Storage\File\FileException;
use SP\Storage\File\FileHandler;
use SP\Util\Util;
@@ -345,7 +344,9 @@ final class ConfigManagerController extends ControllerBase
$template->setBase('config');
$template->addTemplate('info');
- $template->assign('dbInfo', DatabaseUtil::getDBinfo($this->dic->get(DBStorageInterface::class)));
+ $databaseUtil = $this->dic->get(DatabaseUtil::class);
+
+ $template->assign('dbInfo', $databaseUtil->getDBinfo());
$template->assign('dbName', $this->configData->getDbName() . '@' . $this->configData->getDbHost());
$template->assign('configBackupDate', date('r', $this->dic->get(ConfigService::class)->getByParam('config_backup_date', 0)));
$template->assign('plugins', $this->dic->get(PluginManager::class)->getLoadedPlugins());
diff --git a/app/modules/web/Controllers/ErrorController.php b/app/modules/web/Controllers/ErrorController.php
index c6330d9a..92743c83 100644
--- a/app/modules/web/Controllers/ErrorController.php
+++ b/app/modules/web/Controllers/ErrorController.php
@@ -126,4 +126,20 @@ final class ErrorController
$this->view();
}
+
+ /**
+ * databaseErrorAction
+ */
+ public function databaseConnectionAction()
+ {
+ $this->layoutHelper->getPublicLayout('error-database');
+
+ $this->view->append('errors', [
+ 'type' => SPException::CRITICAL,
+ 'description' => __('No es posible conectar con la BD'),
+ 'hint' => __('Consulte con el administrador')
+ ]);
+
+ $this->view();
+ }
}
\ No newline at end of file
diff --git a/app/modules/web/Controllers/Helpers/Grid/PluginGrid.php b/app/modules/web/Controllers/Helpers/Grid/PluginGrid.php
index e3aa8c62..ac2257e8 100644
--- a/app/modules/web/Controllers/Helpers/Grid/PluginGrid.php
+++ b/app/modules/web/Controllers/Helpers/Grid/PluginGrid.php
@@ -68,6 +68,13 @@ final class PluginGrid extends GridBase
$grid->addDataAction($this->getEnableAction());
$grid->addDataAction($this->getDisableAction());
$grid->addDataAction($this->getResetAction());
+ $grid->addDataAction($this->getDeleteAction());
+ $grid->addDataAction(
+ $this->getDeleteAction()
+ ->setName(__('Eliminar Seleccionados'))
+ ->setTitle(__('Eliminar Seleccionados'))
+ ->setIsSelection(true),
+ true);
$grid->setTime(round(getElapsedTime($this->queryTimeStart), 5));
@@ -208,6 +215,26 @@ final class PluginGrid extends GridBase
$gridAction->setFilterRowSource('available', 0);
$gridAction->addData('action-route', Acl::getActionRoute(ActionsInterface::PLUGIN_RESET));
$gridAction->addData('action-method', 'get');
+ $gridAction->addData('action-next', Acl::getActionRoute(ActionsInterface::PLUGIN));
+
+ return $gridAction;
+ }
+
+ /**
+ * @return \SP\Html\DataGrid\Action\DataGridAction
+ */
+ private function getDeleteAction()
+ {
+ $gridAction = new DataGridAction();
+ $gridAction->setId(ActionsInterface::PLUGIN_DELETE);
+ $gridAction->setType(DataGridActionType::DELETE_ITEM);
+ $gridAction->setName(__('Eliminar Plugin'));
+ $gridAction->setTitle(__('Eliminar Plugin'));
+ $gridAction->setIcon($this->icons->getIconDelete());
+ $gridAction->setFilterRowSource('available', 1);
+ $gridAction->setOnClickFunction('plugin/delete');
+ $gridAction->addData('action-route', Acl::getActionRoute(ActionsInterface::PLUGIN_DELETE));
+ $gridAction->addData('action-next', Acl::getActionRoute(ActionsInterface::PLUGIN));
return $gridAction;
}
diff --git a/app/modules/web/Controllers/PluginController.php b/app/modules/web/Controllers/PluginController.php
index f589e213..50aa36a7 100644
--- a/app/modules/web/Controllers/PluginController.php
+++ b/app/modules/web/Controllers/PluginController.php
@@ -261,6 +261,42 @@ final class PluginController extends ControllerBase
}
}
+ /**
+ * resetAction
+ *
+ * @param $id
+ *
+ * @return bool
+ */
+ public function deleteAction($id)
+ {
+ try {
+ $this->checkSecurityToken($this->previousSk, $this->request);
+
+ if (!$this->acl->checkUserAccess(Acl::PLUGIN_DELETE)) {
+ return $this->returnJsonResponse(JsonResponse::JSON_ERROR, __u('No tiene permisos para realizar esta operación'));
+ }
+
+ if ($id === null) {
+ $this->pluginService->deleteByIdBatch($this->getItemsIdFromRequest($this->request));
+
+ $this->eventDispatcher->notifyEvent('delete.plugin.selection', new Event($this));
+
+ return $this->returnJsonResponse(JsonResponse::JSON_SUCCESS, __u('Plugins eliminados'));
+ } else {
+ $this->pluginService->delete($id);
+
+ $this->eventDispatcher->notifyEvent('delete.plugin', new Event($this));
+
+ return $this->returnJsonResponse(JsonResponse::JSON_SUCCESS, __u('Plugin eliminado'));
+ }
+ } catch (\Exception $e) {
+ processException($e);
+
+ return $this->returnJsonResponseException($e);
+ }
+ }
+
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
diff --git a/app/modules/web/Init.php b/app/modules/web/Init.php
index 2e242b86..cd4d802d 100644
--- a/app/modules/web/Init.php
+++ b/app/modules/web/Init.php
@@ -45,7 +45,6 @@ use SP\Services\Upgrade\UpgradeAppService;
use SP\Services\Upgrade\UpgradeDatabaseService;
use SP\Services\Upgrade\UpgradeUtil;
use SP\Services\UserProfile\UserProfileService;
-use SP\Storage\Database\Database;
use SP\Storage\Database\DatabaseUtil;
use SP\Util\HttpUtil;
@@ -165,15 +164,30 @@ final class Init extends ModuleBase
HttpUtil::checkHttps($this->configData, $this->request);
if (in_array($controller, self::PARTIAL_INIT, true) === false) {
+ $databaseUtil = $this->container->get(DatabaseUtil::class);
+
// Checks if sysPass is installed
- if ($this->checkInstalled() === false) {
+ if (!$this->checkInstalled()) {
+ logger('Not installed', 'ERROR');
+
$this->router->response()
->redirect('index.php?r=install/index')
->send();
}
+ // Checks if the database is set up
+ if (!$databaseUtil->checkDatabaseConnection()) {
+ logger('Database connection error', 'ERROR');
+
+ $this->router->response()
+ ->redirect('index.php?r=error/databaseConnection')
+ ->send();
+ }
+
// Checks if maintenance mode is turned on
if ($this->checkMaintenanceMode($this->context)) {
+ logger('Maintenance mode', 'INFO');
+
$this->router->response()
->redirect('index.php?r=error/maintenanceError')
->send();
@@ -181,6 +195,8 @@ final class Init extends ModuleBase
// Checks if upgrade is needed
if ($this->checkUpgrade()) {
+ logger('Upgrade needed', 'ERROR');
+
$this->config->generateUpgradeKey();
$this->router->response()
@@ -189,10 +205,9 @@ final class Init extends ModuleBase
}
// Checks if the database is set up
- if (!DatabaseUtil::checkDatabaseExist(
- $this->container->get(Database::class)->getDbHandler(),
- $this->configData->getDbName())
- ) {
+ if (!$databaseUtil->checkDatabaseTables($this->configData->getDbName())) {
+ logger('Database checking error', 'ERROR');
+
$this->router->response()
->redirect('index.php?r=error/databaseError')
->send();
diff --git a/composer.json b/composer.json
index cd441995..614c1adf 100644
--- a/composer.json
+++ b/composer.json
@@ -44,6 +44,9 @@
"fzaninotto/faker": "~v1.8",
"fabpot/goutte": "~v3.2"
},
+ "suggest": {
+ "syspass/plugin-authenticator": "^2.0"
+ },
"autoload": {
"psr-4": {
"SP\\": "lib/SP/",
diff --git a/lib/SP/Core/Acl/ActionsInterface.php b/lib/SP/Core/Acl/ActionsInterface.php
index c55fba0f..92db6d8c 100644
--- a/lib/SP/Core/Acl/ActionsInterface.php
+++ b/lib/SP/Core/Acl/ActionsInterface.php
@@ -127,6 +127,7 @@ interface ActionsInterface
const PLUGIN_ENABLE = 1105;
const PLUGIN_DISABLE = 1106;
const PLUGIN_RESET = 1107;
+ const PLUGIN_DELETE = 1108;
const WIKI = 1201;
const WIKI_SEARCH = 1202;
const WIKI_VIEW = 1203;
diff --git a/lib/SP/Plugin/PluginManager.php b/lib/SP/Plugin/PluginManager.php
index 10a3708b..f03dc8e1 100644
--- a/lib/SP/Plugin/PluginManager.php
+++ b/lib/SP/Plugin/PluginManager.php
@@ -102,7 +102,7 @@ final class PluginManager
if ($dir) {
while (false !== ($entry = $dir->read())) {
$pluginDir = PLUGINS_PATH . DIRECTORY_SEPARATOR . $entry;
- $pluginFile = $pluginDir . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'Plugin.php';
+ $pluginFile = $pluginDir . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'Plugin.php';
if (strpos($entry, '.') === false
&& is_dir($pluginDir)
diff --git a/lib/SP/Repositories/RepositoryItemTrait.php b/lib/SP/Repositories/RepositoryItemTrait.php
index aa565f98..2f9c64d3 100644
--- a/lib/SP/Repositories/RepositoryItemTrait.php
+++ b/lib/SP/Repositories/RepositoryItemTrait.php
@@ -74,7 +74,9 @@ trait RepositoryItemTrait
{
$charsSrc = ['.', ' ', '_', ', ', '-', ';', '\'', '"', ':', '(', ')', '|', '/'];
- return md5(strtolower(str_replace($charsSrc, '', DatabaseUtil::escape($name, $DBStorage))));
+ $databaseUtil = new DatabaseUtil($DBStorage);
+
+ return md5(strtolower(str_replace($charsSrc, '', $databaseUtil->escape($name))));
}
/**
diff --git a/lib/SP/Services/Backup/FileBackupService.php b/lib/SP/Services/Backup/FileBackupService.php
index a8ff0ae9..87875bd9 100644
--- a/lib/SP/Services/Backup/FileBackupService.php
+++ b/lib/SP/Services/Backup/FileBackupService.php
@@ -212,6 +212,7 @@ final class FileBackupService extends Service
$fileHandler->open('w');
$db = $this->dic->get(Database::class);
+ $databaseUtil = $this->dic->get(DatabaseUtil::class);
$queryData = new QueryData();
@@ -293,7 +294,7 @@ final class FileBackupService extends Service
if (is_numeric($value)) {
$fileHandler->write($value);
} else {
- $fileHandler->write(DatabaseUtil::escape($value, $db->getDbHandler()));
+ $fileHandler->write($databaseUtil->escape($value));
}
if ($field < $numColumns) {
diff --git a/lib/SP/Services/Install/Installer.php b/lib/SP/Services/Install/Installer.php
index a08e3aad..3fe2b84c 100644
--- a/lib/SP/Services/Install/Installer.php
+++ b/lib/SP/Services/Install/Installer.php
@@ -57,7 +57,7 @@ final class Installer extends Service
*/
const VERSION = [3, 0, 0];
const VERSION_TEXT = '3.0-beta';
- const BUILD = 18102801;
+ const BUILD = 18110101;
/**
* @var DatabaseSetupInterface
diff --git a/lib/SP/Services/Install/MySQL.php b/lib/SP/Services/Install/MySQL.php
index 01318fbd..84544ba8 100644
--- a/lib/SP/Services/Install/MySQL.php
+++ b/lib/SP/Services/Install/MySQL.php
@@ -354,7 +354,9 @@ final class MySQL implements DatabaseSetupInterface
*/
public function checkConnection()
{
- if (!DatabaseUtil::checkDatabaseExist($this->mysqlHandler, $this->installData->getDbName())) {
+ $databaseUtil = new DatabaseUtil($this->mysqlHandler);
+
+ if (!$databaseUtil->checkDatabaseTables($this->installData->getDbName())) {
$this->rollback();
throw new SPException(
diff --git a/lib/SP/Storage/Database/DatabaseUtil.php b/lib/SP/Storage/Database/DatabaseUtil.php
index eaf96a78..bf6cbb1e 100644
--- a/lib/SP/Storage/Database/DatabaseUtil.php
+++ b/lib/SP/Storage/Database/DatabaseUtil.php
@@ -24,8 +24,6 @@
namespace SP\Storage\Database;
-use SP\Core\Exceptions\SPException;
-
/**
* Class DBUtil con utilidades de la BD
*
@@ -64,39 +62,79 @@ final class DatabaseUtil
'account_data_v',
'account_search_v'
];
+ /**
+ * @var DBStorageInterface
+ */
+ private $DBStorage;
/**
- * Escapar una cadena de texto con funciones de mysqli.
+ * DatabaseUtil constructor.
*
- * @param string $str string con la cadena a escapar
* @param DBStorageInterface $DBStorage
- *
- * @return string con la cadena escapada
*/
- public static function escape($str, DBStorageInterface $DBStorage)
+ public function __construct(DBStorageInterface $DBStorage)
+ {
+
+ $this->DBStorage = $DBStorage;
+ }
+
+ /**
+ * Comprobar que la base de datos existe.
+ *
+ * @param string $dbName
+ *
+ * @return bool
+ */
+ public function checkDatabaseTables($dbName)
{
try {
- return $DBStorage->getConnection()->quote(trim($str));
- } catch (SPException $e) {
+ $tables = implode(',', array_map(function ($value) {
+ return '\'' . $value . '\'';
+ }, self::$tables));
+
+ $query = /** @lang SQL */
+ 'SELECT COUNT(*)
+ FROM information_schema.tables
+ WHERE table_schema = \'' . $dbName . '\'
+ AND `table_name` IN (' . $tables . ')';
+
+ $numTables = (int)$this->DBStorage->getConnection()->query($query)->fetchColumn();
+
+ return $numTables === count(self::$tables);
+ } catch (\Exception $e) {
processException($e);
}
- return $str;
+ return false;
+ }
+
+ /**
+ * @return bool
+ */
+ public function checkDatabaseConnection()
+ {
+ try {
+ $this->DBStorage->getConnection();
+
+ return true;
+ } catch (\Exception $e) {
+ processException($e);
+
+ return false;
+ }
}
/**
* Obtener la información del servidor de base de datos
*
- * @param DBStorageInterface $DBStorage
- *
* @return array
*/
- public static function getDBinfo(DBStorageInterface $DBStorage)
+ public function getDBinfo()
{
$dbinfo = [];
try {
- $db = $DBStorage->getConnection();
+ $db = $this->DBStorage->getConnection();
$attributes = [
'SERVER_VERSION',
@@ -118,33 +156,20 @@ final class DatabaseUtil
}
/**
- * Comprobar que la base de datos existe.
+ * Escapar una cadena de texto con funciones de mysqli.
*
- * @param DBStorageInterface $DBStorage
- * @param string $dbName
+ * @param string $str string con la cadena a escapar
*
- * @return bool
+ * @return string con la cadena escapada
*/
- public static function checkDatabaseExist(DBStorageInterface $DBStorage, $dbName)
+ public function escape($str)
{
try {
- $tables = implode(',', array_map(function ($value) {
- return '\'' . $value . '\'';
- }, self::$tables));
-
- $query = /** @lang SQL */
- 'SELECT COUNT(*)
- FROM information_schema.tables
- WHERE table_schema = \'' . $dbName . '\'
- AND `table_name` IN (' . $tables . ')';
-
- $numTables = (int)$DBStorage->getConnection()->query($query)->fetchColumn();
-
- return $numTables === count(self::$tables);
+ return $this->DBStorage->getConnection()->quote(trim($str));
} catch (\Exception $e) {
processException($e);
}
- return false;
+ return $str;
}
}
\ No newline at end of file
diff --git a/public/js/app-actions.js b/public/js/app-actions.js
index d86281f2..0265bf5b 100644
--- a/public/js/app-actions.js
+++ b/public/js/app-actions.js
@@ -1398,7 +1398,22 @@ sysPass.Actions = function (log) {
onClick: function (e) {
e.preventDefault();
- tabs.save($obj);
+ const opts = sysPassApp.requests.getRequestOpts();
+ opts.method = "get";
+ opts.url = sysPassApp.util.getUrl(ajaxUrl.entrypoint,
+ {
+ r: [$obj.data("action-route"), $obj.data("item-id")],
+ sk: sysPassApp.sk.get(),
+ isAjax: 1
+ });
+
+ sysPassApp.requests.getActionCall(opts, function (json) {
+ sysPassApp.msg.out(json);
+
+ if (json.status === 0) {
+ getContent({r: $obj.data("action-next")});
+ }
+ });
}
}
});
@@ -1434,7 +1449,30 @@ sysPass.Actions = function (log) {
log.info("plugin:nav");
grid.nav($obj);
- }
+ },
+ delete: function ($obj) {
+ log.info("plugin:delete");
+
+ grid.delete($obj, function (items) {
+ const opts = sysPassApp.requests.getRequestOpts();
+ opts.method = "get";
+ opts.url = sysPassApp.util.getUrl(ajaxUrl.entrypoint,
+ {
+ r: [$obj.data("action-route"), (items.length === 0 ? $obj.data("item-id") : null)],
+ sk: sysPassApp.sk.get(),
+ isAjax: 1
+ });
+ opts.data = {items: items};
+
+ sysPassApp.requests.getActionCall(opts, function (json) {
+ sysPassApp.msg.out(json);
+
+ if (json.status === 0) {
+ getContent({r: $obj.data("action-next")});
+ }
+ });
+ });
+ },
};
/**
diff --git a/public/js/app-actions.min.js b/public/js/app-actions.min.js
index 398affc5..61c0753e 100644
--- a/public/js/app-actions.min.js
+++ b/public/js/app-actions.min.js
@@ -2,16 +2,16 @@ var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.findInternal=function(c,k,e){c
$jscomp.getGlobal=function(c){return"undefined"!=typeof window&&window===c?c:"undefined"!=typeof global&&null!=global?global:c};$jscomp.global=$jscomp.getGlobal(this);$jscomp.polyfill=function(c,k,e,f){if(k){e=$jscomp.global;c=c.split(".");for(f=0;f