From 53aba99af1f9b4bc1e1676bd1ee8d723eaf12403 Mon Sep 17 00:00:00 2001 From: nuxsmin Date: Tue, 6 Mar 2018 01:28:47 +0100 Subject: [PATCH] * [ADD] Added mail event handler * [MOD] Improved import process * [MOD] Improved event logging * [MOD] Code refactoring. Work in progress * [FIX] Several bugfixes. Work in progress --- .../Controllers/AccessManagerController.php | 9 + .../ConfigEncryptionController.php | 85 +++++--- .../Controllers/ConfigImportController.php | 13 +- .../Controllers/ConfigManagerController.php | 10 + .../web/Controllers/ControllerBase.php | 2 +- .../web/Controllers/CustomFieldController.php | 2 +- .../Helpers/Account/AccountHelper.php | 7 +- .../Controllers/Helpers/ItemsGridHelper.php | 3 +- .../web/Controllers/Helpers/LayoutHelper.php | 2 +- .../web/Controllers/InstallController.php | 2 +- .../web/Controllers/ItemManagerController.php | 10 + .../web/Controllers/SimpleControllerBase.php | 2 +- .../web/Controllers/Traits/ConfigTrait.php | 6 +- .../web/Controllers/UserController.php | 6 +- .../Controllers/UserPassResetController.php | 4 +- .../material-blue/views/config/mail.inc | 3 +- lib/Base.php | 1 + lib/SP/Bootstrap.php | 50 +++-- lib/SP/Config/Config.php | 8 +- lib/SP/Config/ConfigData.php | 2 +- lib/SP/Core/Events/EventDispatcherBase.php | 2 + lib/SP/Core/Events/EventMessage.php | 20 ++ lib/SP/Mvc/Controller/ControllerTrait.php | 10 +- lib/SP/Providers/Log/LogHandler.php | 10 +- lib/SP/Providers/Mail/MailHandler.php | 183 +++++++---------- lib/SP/Providers/Mail/MailProvider.php | 186 ++++++++++++++++++ ...xception.php => MailProviderException.php} | 2 +- lib/SP/Providers/Provider.php | 15 +- .../Category/CategoryRepository.php | 179 +++++++++-------- .../Repositories/Client/ClientRepository.php | 53 +++-- .../DuplicatedItemException.php} | 13 +- lib/SP/Repositories/User/UserRepository.php | 34 +++- lib/SP/Services/Account/AccountAclService.php | 5 +- .../Services/Account/AccountCryptService.php | 90 +++++---- lib/SP/Services/Account/AccountService.php | 5 - lib/SP/Services/Category/CategoryService.php | 23 ++- lib/SP/Services/Client/ClientService.php | 49 +++-- lib/SP/Services/Crypt/MasterPassService.php | 7 +- .../Crypt/TemporaryMasterPassService.php | 32 ++- .../CustomField/CustomFieldCryptService.php | 55 +++--- lib/SP/Services/Export/XmlExportService.php | 2 +- lib/SP/Services/Import/CsvImport.php | 9 +- lib/SP/Services/Import/CsvImportBase.php | 48 +++-- lib/SP/Services/Import/ImportService.php | 16 +- lib/SP/Services/Import/ImportTrait.php | 80 +++++++- lib/SP/Services/Import/KeepassImport.php | 20 +- lib/SP/Services/Import/SyspassImport.php | 80 +++----- lib/SP/Services/Import/XmlImport.php | 20 +- lib/SP/Services/Import/XmlImportBase.php | 6 +- lib/SP/Services/Service.php | 15 +- lib/SP/Services/User/UserService.php | 11 ++ lib/SP/Storage/DBUtil.php | 2 +- lib/SP/Storage/Database.php | 9 +- lib/SP/Storage/FileCache.php | 20 +- lib/SP/Storage/FileHandler.php | 6 +- lib/SP/Util/Checks.php | 10 +- lib/SP/Util/HttpUtil.php | 10 +- lib/SP/Util/Util.php | 44 ++--- 58 files changed, 998 insertions(+), 610 deletions(-) create mode 100644 lib/SP/Providers/Mail/MailProvider.php rename lib/SP/Providers/Mail/{MailHandlerException.php => MailProviderException.php} (95%) rename lib/SP/{Storage/CacheableInterface.php => Repositories/DuplicatedItemException.php} (82%) diff --git a/app/modules/web/Controllers/AccessManagerController.php b/app/modules/web/Controllers/AccessManagerController.php index ee1e3fa3..1d829e8a 100644 --- a/app/modules/web/Controllers/AccessManagerController.php +++ b/app/modules/web/Controllers/AccessManagerController.php @@ -182,4 +182,13 @@ class AccessManagerController extends ControllerBase { return $this->tabsGridHelper; } + /** + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \SP\Services\Auth\AuthException + */ + protected function initialize() + { + $this->checkLoggedIn(); + } } \ No newline at end of file diff --git a/app/modules/web/Controllers/ConfigEncryptionController.php b/app/modules/web/Controllers/ConfigEncryptionController.php index 16a02e4b..bd9cb236 100644 --- a/app/modules/web/Controllers/ConfigEncryptionController.php +++ b/app/modules/web/Controllers/ConfigEncryptionController.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -32,15 +32,17 @@ use SP\Core\Crypt\Hash; use SP\Core\Crypt\Session as CryptSession; use SP\Core\Events\Event; use SP\Core\Events\EventMessage; +use SP\Core\Messages\MailMessage; use SP\Core\TaskFactory; use SP\Http\JsonResponse; use SP\Http\Request; use SP\Modules\Web\Controllers\Traits\JsonTrait; +use SP\Providers\Mail\MailProvider; use SP\Services\Config\ConfigService; use SP\Services\Crypt\MasterPassService; use SP\Services\Crypt\TemporaryMasterPassService; use SP\Services\Crypt\UpdateMasterPassRequest; -use SP\Services\ServiceException; +use SP\Services\User\UserService; use SP\Util\Util; /** @@ -98,7 +100,7 @@ class ConfigEncryptionController extends SimpleControllerBase $configService = $this->dic->get(ConfigService::class); if (!$noAccountPassChange) { - Util::lockApp(); + Util::lockApp($this->session->getUserData()->getId(), 'masterpass'); $request = new UpdateMasterPassRequest( $currentMasterPass, @@ -108,12 +110,18 @@ class ConfigEncryptionController extends SimpleControllerBase ); try { + $this->eventDispatcher->notifyEvent('update.masterPassword.start', new Event($this)); + $mastePassService->changeMasterPassword($request); $configService->save('masterPwd', $request->getHash()); $configService->save('lastupdatempass', time()); + + $this->eventDispatcher->notifyEvent('update.masterPassword.end', new Event($this)); } catch (\Exception $e) { processException($e); + $this->eventDispatcher->notifyEvent('exception', new Event($e)); + $this->returnJsonResponseException($e); } finally { Util::unlockApp(); @@ -127,6 +135,8 @@ class ConfigEncryptionController extends SimpleControllerBase } catch (\Exception $e) { processException($e); + $this->eventDispatcher->notifyEvent('exception', new Event($e)); + $this->returnJsonResponse(JsonResponse::JSON_ERROR, __u('Error al guardar el hash de la clave maestra')); } } @@ -154,6 +164,9 @@ class ConfigEncryptionController extends SimpleControllerBase } catch (\Exception $e) { processException($e); + $this->eventDispatcher->notifyEvent('exception', new Event($e)); + + $this->returnJsonResponse(JsonResponse::JSON_ERROR, __u('Error al actualizar el hash de la clave maestra')); } } @@ -165,37 +178,47 @@ class ConfigEncryptionController extends SimpleControllerBase { try { $temporaryMasterPassService = $this->dic->get(TemporaryMasterPassService::class); - $temporaryMasterPassService->create(Request::analyzeInt('tmpass_maxtime', 3600)); + $key = $temporaryMasterPassService->create(Request::analyzeInt('tmpass_maxtime', 3600)); + + $groupId = Request::analyzeInt('tmpass_group'); + $sendEmail = Request::analyzeBool('tmpass_chkSendEmail'); + + if ($sendEmail && $groupId) { + $mailMessage = new MailMessage(); + $mailMessage->setTitle(sprintf(__('Clave Maestra %s'), Util::getAppInfo('appname'))); + $mailMessage->addDescription(__('Se ha generado una nueva clave para el acceso a sysPass y se solicitará en el siguiente inicio.')); + $mailMessage->addDescriptionLine(); + $mailMessage->addDescription(sprintf(__('La nueva clave es: %s'), $key)); + $mailMessage->addDescriptionLine(); + $mailMessage->addDescription(sprintf(__('Esta clave estará activa hasta: %s'), date('r', $temporaryMasterPassService->getMaxTime()))); + $mailMessage->addDescriptionLine(); + $mailMessage->addDescription(__('No olvide acceder lo antes posible para guardar los cambios.')); + + try { + $emails = array_map(function ($value) { + return $value->email; + }, $this->dic->get(UserService::class)->getUserEmailForGroup($groupId)); + + $this->dic->get(MailProvider::class)->sendBatch($mailMessage->getTitle(), $emails, $mailMessage); + + $this->returnJsonResponse(JsonResponse::JSON_SUCCESS, __u('Clave Temporal Generada'), [__u('Email enviado')]); + } catch (\Exception $e) { + processException($e); + + $this->eventDispatcher->notifyEvent('exception', new Event($e)); + + $this->returnJsonResponse(JsonResponse::JSON_WARNING, __u('Clave Temporal Generada'), [__u('Error al enviar email')]); + } + } $this->returnJsonResponse(JsonResponse::JSON_SUCCESS, __u('Clave Temporal Generada')); - } catch (ServiceException $e) { + } catch (\Exception $e) { + processException($e); + + $this->eventDispatcher->notifyEvent('exception', new Event($e)); + $this->returnJsonResponseException($e); } - - -// $tempMasterGroup = Request::analyze('tmpass_group', 0); -// $tempMasterEmail = Request::analyze('tmpass_chkSendEmail', 0, false, 1); -// -// $this->LogMessage->addDescription(__('Clave Temporal Generada', false)); -// -// if ($tempMasterEmail) { -// $Message = new NoticeMessage(); -// $Message->setTitle(sprintf(__('Clave Maestra %s'), Util::getAppInfo('appname'))); -// $Message->addDescription(__('Se ha generado una nueva clave para el acceso a sysPass y se solicitará en el siguiente inicio.')); -// $Message->addDescription(''); -// $Message->addDescription(sprintf(__('La nueva clave es: %s'), $tempMasterPass)); -// $Message->addDescription(''); -// $Message->addDescription(__('No olvide acceder lo antes posible para guardar los cambios.')); -// -// if ($tempMasterGroup !== 0) { -// Email::sendEmailBatch($Message, UserUtil::getUserGroupEmail($tempMasterGroup)); -// } else { -// Email::sendEmailBatch($Message, UserUtil::getUsersEmail()); -// } -// } -// -// $this->JsonResponse->setStatus(0); - } protected function initialize() diff --git a/app/modules/web/Controllers/ConfigImportController.php b/app/modules/web/Controllers/ConfigImportController.php index da70db22..3d13e3ac 100644 --- a/app/modules/web/Controllers/ConfigImportController.php +++ b/app/modules/web/Controllers/ConfigImportController.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -64,12 +64,11 @@ class ConfigImportController extends SimpleControllerBase $importParams->setCsvDelimiter(Request::analyzeString('csvDelimiter')); try { - $importService = $this->dic->get(ImportService::class); - $counter = $importService->doImport($importParams, new FileImport($this->router->request()->files()->get('inFile'))); + $counter = $this->dic->get(ImportService::class) + ->doImport($importParams, new FileImport($this->router->request()->files()->get('inFile'))); - $this->eventDispatcher->notifyEvent('run.import', new Event($this, - EventMessage::factory() - ->addDetail(__u('Cuentas importadas'), $counter)) + $this->eventDispatcher->notifyEvent('run.import.end', + new Event($this, EventMessage::factory()->addDetail(__u('Cuentas importadas'), $counter)) ); if ($counter > 0) { diff --git a/app/modules/web/Controllers/ConfigManagerController.php b/app/modules/web/Controllers/ConfigManagerController.php index 4ebb1dd3..7d65032e 100644 --- a/app/modules/web/Controllers/ConfigManagerController.php +++ b/app/modules/web/Controllers/ConfigManagerController.php @@ -337,4 +337,14 @@ class ConfigManagerController extends ControllerBase { return $this->tabsHelper; } + + /** + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \SP\Services\Auth\AuthException + */ + protected function initialize() + { + $this->checkLoggedIn(); + } } \ No newline at end of file diff --git a/app/modules/web/Controllers/ControllerBase.php b/app/modules/web/Controllers/ControllerBase.php index 2df6bb64..0cca1509 100644 --- a/app/modules/web/Controllers/ControllerBase.php +++ b/app/modules/web/Controllers/ControllerBase.php @@ -281,7 +281,7 @@ abstract class ControllerBase } } - $this->checkLoggedInSession($this->session); + $this->checkLoggedInSession($this->session, $this->router); } /** diff --git a/app/modules/web/Controllers/CustomFieldController.php b/app/modules/web/Controllers/CustomFieldController.php index f04be587..292e15e9 100644 --- a/app/modules/web/Controllers/CustomFieldController.php +++ b/app/modules/web/Controllers/CustomFieldController.php @@ -133,7 +133,7 @@ class CustomFieldController extends ControllerBase implements CrudControllerInte $customField = $customFieldId ? $this->customFieldService->getById($customFieldId) : new CustomFieldDefinitionData(); // FIXME - $customFieldTypeService = new CustomFieldTypeRepository(); + $customFieldTypeService = $this->dic->get(CustomFieldTypeRepository::class); $this->view->assign('field', $customField); $this->view->assign('types', $customFieldTypeService->getAll()); diff --git a/app/modules/web/Controllers/Helpers/Account/AccountHelper.php b/app/modules/web/Controllers/Helpers/Account/AccountHelper.php index a32eb485..06eed2bf 100644 --- a/app/modules/web/Controllers/Helpers/Account/AccountHelper.php +++ b/app/modules/web/Controllers/Helpers/Account/AccountHelper.php @@ -25,7 +25,6 @@ namespace SP\Modules\Web\Controllers\Helpers\Account; use SP\Account\AccountAcl; -use SP\Bootstrap; use SP\Core\Acl\AccountPermissionException; use SP\Core\Acl\Acl; use SP\Core\Acl\ActionsInterface; @@ -156,7 +155,7 @@ class AccountHelper extends HelperBase $this->view->assign('accountData', $accountData); $this->view->assign('gotData', true); - $this->view->assign('accountActions', Bootstrap::getContainer()->get(AccountActionsHelper::class)->getActionsForAccount($this->accountAcl, $accountActionsDto)); + $this->view->assign('accountActions', $this->dic->get(AccountActionsHelper::class)->getActionsForAccount($this->accountAcl, $accountActionsDto)); $this->setViewCommon(); } @@ -281,7 +280,7 @@ class AccountHelper extends HelperBase $this->view->assign('accountId', 0); $this->view->assign('gotData', false); - $this->view->assign('accountActions', Bootstrap::getContainer()->get(AccountActionsHelper::class)->getActionsForAccount($this->accountAcl, new AccountActionsDto($this->accountId))); + $this->view->assign('accountActions', $this->dic->get(AccountActionsHelper::class)->getActionsForAccount($this->accountAcl, new AccountActionsDto($this->accountId))); $this->setViewCommon(); } @@ -311,7 +310,7 @@ class AccountHelper extends HelperBase $this->view->assign('accountId', $accountData->getId()); $this->view->assign('accountData', $accountDetailsResponse->getAccountVData()); - $this->view->assign('accountActions', Bootstrap::getContainer()->get(AccountActionsHelper::class)->getActionsForAccount($this->accountAcl, new AccountActionsDto($this->accountId, null, $accountData->getParentId()))); + $this->view->assign('accountActions', $this->dic->get(AccountActionsHelper::class)->getActionsForAccount($this->accountAcl, new AccountActionsDto($this->accountId, null, $accountData->getParentId()))); return true; } diff --git a/app/modules/web/Controllers/Helpers/ItemsGridHelper.php b/app/modules/web/Controllers/Helpers/ItemsGridHelper.php index d746418e..2de77cff 100644 --- a/app/modules/web/Controllers/Helpers/ItemsGridHelper.php +++ b/app/modules/web/Controllers/Helpers/ItemsGridHelper.php @@ -26,7 +26,6 @@ namespace SP\Modules\Web\Controllers\Helpers; defined('APP_ROOT') || die(); -use SP\Bootstrap; use SP\Core\Acl\Acl; use SP\Core\Acl\ActionsInterface; use SP\Core\UI\ThemeIconsBase; @@ -1477,7 +1476,7 @@ class ItemsGridHelper extends HelperBase */ protected function initialize() { - $this->acl = Bootstrap::getContainer()->get(Acl::class); + $this->acl = $this->dic->get(Acl::class); $this->icons = $this->view->getTheme()->getIcons(); } } \ No newline at end of file diff --git a/app/modules/web/Controllers/Helpers/LayoutHelper.php b/app/modules/web/Controllers/Helpers/LayoutHelper.php index 78940074..73ead596 100644 --- a/app/modules/web/Controllers/Helpers/LayoutHelper.php +++ b/app/modules/web/Controllers/Helpers/LayoutHelper.php @@ -369,7 +369,7 @@ class LayoutHelper extends HelperBase */ protected function initialize() { - $this->theme = Bootstrap::getContainer()->get(Theme::class); + $this->theme = $this->dic->get(Theme::class); $this->loggedIn = $this->session->isLoggedIn(); diff --git a/app/modules/web/Controllers/InstallController.php b/app/modules/web/Controllers/InstallController.php index cef9ff7a..e8cb11a7 100644 --- a/app/modules/web/Controllers/InstallController.php +++ b/app/modules/web/Controllers/InstallController.php @@ -120,7 +120,7 @@ class InstallController extends ControllerBase try { Installer::run($installData); - $this->returnJsonResponse(JsonResponse::JSON_SUCCESS_STICKY, __('Instalación finalizada')); + $this->returnJsonResponse(JsonResponse::JSON_SUCCESS_STICKY, __u('Instalación finalizada')); } catch (\Exception $e) { $this->returnJsonResponseException($e); } diff --git a/app/modules/web/Controllers/ItemManagerController.php b/app/modules/web/Controllers/ItemManagerController.php index d4341950..a6b02c8b 100644 --- a/app/modules/web/Controllers/ItemManagerController.php +++ b/app/modules/web/Controllers/ItemManagerController.php @@ -236,4 +236,14 @@ class ItemManagerController extends ControllerBase { return $this->tabsGridHelper; } + + /** + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \SP\Services\Auth\AuthException + */ + protected function initialize() + { + $this->checkLoggedIn(); + } } \ No newline at end of file diff --git a/app/modules/web/Controllers/SimpleControllerBase.php b/app/modules/web/Controllers/SimpleControllerBase.php index c86196a5..bbfe40ea 100644 --- a/app/modules/web/Controllers/SimpleControllerBase.php +++ b/app/modules/web/Controllers/SimpleControllerBase.php @@ -113,7 +113,7 @@ abstract class SimpleControllerBase */ protected function checks() { - $this->checkLoggedInSession($this->session); + $this->checkLoggedInSession($this->session, $this->router); $this->checkSecurityToken($this->session); } diff --git a/app/modules/web/Controllers/Traits/ConfigTrait.php b/app/modules/web/Controllers/Traits/ConfigTrait.php index 85dee11b..c269cf5e 100644 --- a/app/modules/web/Controllers/Traits/ConfigTrait.php +++ b/app/modules/web/Controllers/Traits/ConfigTrait.php @@ -55,10 +55,8 @@ trait ConfigTrait $config->saveConfig($configData); - if ($configData->isMaintenance()) { - Util::lockApp(false); - } elseif (Bootstrap::$LOCK > 0) { - Util::unlockApp(false); + if ($configData->isMaintenance() === false && Bootstrap::$LOCK !== false) { + Util::unlockApp(); } if ($onSuccess !== null) { diff --git a/app/modules/web/Controllers/UserController.php b/app/modules/web/Controllers/UserController.php index 6db2180d..bc367cd0 100644 --- a/app/modules/web/Controllers/UserController.php +++ b/app/modules/web/Controllers/UserController.php @@ -39,7 +39,7 @@ use SP\Modules\Web\Controllers\Traits\JsonTrait; use SP\Modules\Web\Forms\UserForm; use SP\Mvc\Controller\CrudControllerInterface; use SP\Mvc\View\Components\SelectItemAdapter; -use SP\Providers\Mail\MailHandler; +use SP\Providers\Mail\MailProvider; use SP\Services\User\UserService; use SP\Services\UserGroup\UserGroupService; use SP\Services\UserPassRecover\UserPassRecoverService; @@ -308,14 +308,14 @@ class UserController extends ControllerBase implements CrudControllerInterface * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException * @throws \SP\Core\Exceptions\ConstraintException * @throws \SP\Core\Exceptions\QueryException - * @throws \SP\Providers\Mail\MailHandlerException + * @throws \SP\Providers\Mail\MailProviderException * @throws \SP\Services\ServiceException */ protected function checkChangeUserPass(UserData $userData) { if ($userData->isChangePass()) { $hash = $this->dic->get(UserPassRecoverService::class)->requestForUserId($userData->getId()); - $this->dic->get(MailHandler::class)->send(__('Cambio de Clave'), $userData->getEmail(), UserPassRecoverService::getMailMessage($hash)); + $this->dic->get(MailProvider::class)->send(__('Cambio de Clave'), $userData->getEmail(), UserPassRecoverService::getMailMessage($hash)); // $this->returnJsonResponse( // JsonResponse::JSON_WARNING, diff --git a/app/modules/web/Controllers/UserPassResetController.php b/app/modules/web/Controllers/UserPassResetController.php index 3d338a93..675de258 100644 --- a/app/modules/web/Controllers/UserPassResetController.php +++ b/app/modules/web/Controllers/UserPassResetController.php @@ -32,7 +32,7 @@ use SP\Http\JsonResponse; use SP\Http\Request; use SP\Modules\Web\Controllers\Helpers\LayoutHelper; use SP\Modules\Web\Controllers\Traits\JsonTrait; -use SP\Providers\Mail\MailHandler; +use SP\Providers\Mail\MailProvider; use SP\Repositories\Track\TrackRequest; use SP\Services\Track\TrackService; use SP\Services\User\UserService; @@ -103,7 +103,7 @@ class UserPassResetController extends ControllerBase ->addDetail(__u('Solicitado para'), sprintf('%s (%s)', $login, $email))) ); - $this->dic->get(MailHandler::class)->send(__('Cambio de Clave'), $email, UserPassRecoverService::getMailMessage($hash)); + $this->dic->get(MailProvider::class)->send(__('Cambio de Clave'), $email, UserPassRecoverService::getMailMessage($hash)); $this->returnJsonResponse(JsonResponse::JSON_SUCCESS, __u('Solicitud enviada'), [__u('En breve recibirá un correo para completar la solicitud.')]); } catch (\Exception $e) { diff --git a/app/modules/web/themes/material-blue/views/config/mail.inc b/app/modules/web/themes/material-blue/views/config/mail.inc index f20ddb55..77b6515d 100644 --- a/app/modules/web/themes/material-blue/views/config/mail.inc +++ b/app/modules/web/themes/material-blue/views/config/mail.inc @@ -4,7 +4,8 @@ -
diff --git a/lib/Base.php b/lib/Base.php index 9892e00d..a6a48367 100644 --- a/lib/Base.php +++ b/lib/Base.php @@ -42,6 +42,7 @@ define('CONFIG_FILE', CONFIG_PATH . DIRECTORY_SEPARATOR . 'config.xml'); define('ACTIONS_FILE', CONFIG_PATH . DIRECTORY_SEPARATOR . 'actions.xml'); define('OLD_CONFIG_FILE', CONFIG_PATH . DIRECTORY_SEPARATOR . 'config.php'); define('LOG_FILE', CONFIG_PATH . DIRECTORY_SEPARATOR . 'syspass.log'); +define('LOCK_FILE', CONFIG_PATH . DIRECTORY_SEPARATOR . '.lock'); // Setup application paths define('MODULES_PATH', APP_PATH . DIRECTORY_SEPARATOR . 'modules'); diff --git a/lib/SP/Bootstrap.php b/lib/SP/Bootstrap.php index 2f18e8b3..ec183d6d 100644 --- a/lib/SP/Bootstrap.php +++ b/lib/SP/Bootstrap.php @@ -48,6 +48,7 @@ use SP\Core\Upgrade\Upgrade; use SP\Http\Request; use SP\Log\Log; use SP\Providers\Log\LogHandler; +use SP\Providers\Mail\MailHandler; use SP\Providers\Notification\NotificationHandler; use SP\Services\UserProfile\UserProfileService; use SP\Storage\Database; @@ -82,9 +83,9 @@ class Bootstrap */ public static $UPDATED = false; /** - * @var int + * @var mixed */ - public static $LOCK = 0; + public static $LOCK; /** * @var ContainerInterface|Container */ @@ -292,7 +293,7 @@ class Bootstrap } // Comprobar si es necesario cambiar a HTTPS - HttpUtil::checkHttps(); + HttpUtil::checkHttps($this->configData); } /** @@ -535,7 +536,6 @@ class Bootstrap * Devuelve un error 503 y un reintento de 120s al cliente. * * @param bool $check sólo comprobar si está activado el modo - * @return bool * @throws InitializationException */ public function checkMaintenanceMode($check = false) @@ -546,15 +546,37 @@ class Bootstrap if ($check === true || Checks::isAjax($this->router) || Request::analyzeInt('nodbupgrade') === 1 - || (self::$LOCK > 0 && $this->session->isLoggedIn() && self::$LOCK === $this->session->getUserData()->getId()) + || (self::$LOCK !== false && self::$LOCK->userId > 0 && $this->session->isLoggedIn() && self::$LOCK->userId === $this->session->getUserData()->getId()) ) { - return true; + return; } - throw new InitializationException(__u('Aplicación en mantenimiento'), SPException::INFO, __u('En breve estará operativa')); + throw new InitializationException( + __u('Aplicación en mantenimiento'), + InitializationException::INFO, + __u('En breve estará operativa') + ); } } + /** + * Initializes event handlers + */ + protected function initEventHandlers() + { + $eventDispatcher = self::$container->get(EventDispatcher::class); + + if ($this->configData->isLogEnabled()) { + $eventDispatcher->attach(self::$container->get(LogHandler::class)); + } + + if ($this->configData->isMailEnabled()) { + $eventDispatcher->attach(self::$container->get(MailHandler::class)); + } + + $eventDispatcher->attach(self::$container->get(NotificationHandler::class)); + } + /** * Inicializar la sesión de usuario * @@ -648,20 +670,6 @@ class Bootstrap } } - /** - * Initializes event handlers - */ - protected function initEventHandlers() - { - $eventDispatcher = self::$container->get(EventDispatcher::class); - - if ($this->configData->isLogEnabled()) { - $eventDispatcher->attach(self::$container->get(LogHandler::class)); - } - - $eventDispatcher->attach(self::$container->get(NotificationHandler::class)); - } - /** * Comprobar si es necesario actualizar componentes * diff --git a/lib/SP/Config/Config.php b/lib/SP/Config/Config.php index 5767080f..de0d39cf 100644 --- a/lib/SP/Config/Config.php +++ b/lib/SP/Config/Config.php @@ -152,13 +152,15 @@ class Config * * @param ConfigData $configData * @param bool $backup - * @throws \DI\DependencyException - * @throws \DI\NotFoundException */ public function saveConfig(ConfigData $configData, $backup = true) { if ($backup) { - $this->dic->get(ConfigBackupService::class)->backup(); + try { + $this->dic->get(ConfigBackupService::class)->backup(); + } catch (\Exception $e) { + processException($e); + } } $configData->setConfigDate(time()); diff --git a/lib/SP/Config/ConfigData.php b/lib/SP/Config/ConfigData.php index 92440aac..982192a5 100644 --- a/lib/SP/Config/ConfigData.php +++ b/lib/SP/Config/ConfigData.php @@ -1378,7 +1378,7 @@ class ConfigData implements JsonSerializable */ public function isMaintenance() { - return $this->maintenance; + return (bool)$this->maintenance; } /** diff --git a/lib/SP/Core/Events/EventDispatcherBase.php b/lib/SP/Core/Events/EventDispatcherBase.php index cc7aec35..58510acb 100644 --- a/lib/SP/Core/Events/EventDispatcherBase.php +++ b/lib/SP/Core/Events/EventDispatcherBase.php @@ -58,6 +58,8 @@ abstract class EventDispatcherBase implements EventDispatcherInterface // throw new InvalidClassException(sprintf(__('Observador ya inicializado "%s"'), $observerClass)); } + debugLog('Attach: ' . $observerClass); + $this->observers[$observerClass] = $observer; } diff --git a/lib/SP/Core/Events/EventMessage.php b/lib/SP/Core/Events/EventMessage.php index 814a9604..0b838c0a 100644 --- a/lib/SP/Core/Events/EventMessage.php +++ b/lib/SP/Core/Events/EventMessage.php @@ -93,6 +93,16 @@ class EventMessage implements MessageInterface return implode(PHP_EOL, $this->description); } + /** + * Devuelve la descripción + * + * @return array + */ + public function getDescriptionRaw() + { + return $this->description; + } + /** * Añadir detalle en formato HTML. Se resalta el texto clave. * @@ -217,6 +227,16 @@ class EventMessage implements MessageInterface return sprintf('%s : %s', $detail[0], $detail[1]); } + /** + * Devuelve los detalles + * + * @return array + */ + public function getDetailsRaw() + { + return $this->details; + } + /** * Componer un mensaje en formato HTML * diff --git a/lib/SP/Mvc/Controller/ControllerTrait.php b/lib/SP/Mvc/Controller/ControllerTrait.php index 4213f015..1d8af60a 100644 --- a/lib/SP/Mvc/Controller/ControllerTrait.php +++ b/lib/SP/Mvc/Controller/ControllerTrait.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -24,6 +24,7 @@ namespace SP\Mvc\Controller; +use Klein\Klein; use SP\Core\Session\Session; use SP\Http\JsonResponse; use SP\Http\Request; @@ -53,11 +54,12 @@ trait ControllerTrait * Comprobar si la sesión está activa * * @param Session $session + * @param Klein $router */ - protected function checkLoggedInSession(Session $session) + protected function checkLoggedInSession(Session $session, Klein $router) { if (!$session->isLoggedIn()) { - if (Checks::isJson()) { + if (Checks::isJson($router)) { $JsonResponse = new JsonResponse(); $JsonResponse->setDescription(__u('La sesión no se ha iniciado o ha caducado')); $JsonResponse->setStatus(10); diff --git a/lib/SP/Providers/Log/LogHandler.php b/lib/SP/Providers/Log/LogHandler.php index cc9e0004..1ed91ef3 100644 --- a/lib/SP/Providers/Log/LogHandler.php +++ b/lib/SP/Providers/Log/LogHandler.php @@ -50,7 +50,13 @@ class LogHandler extends Provider implements EventReceiver 'login.', 'logout', 'track.', - 'acl.deny' + 'acl.deny', + 'check.tempMasterPassword', + 'expire.tempMasterPassword', + 'refresh.masterPassword', + 'update.', + 'import.ldap.', + 'run.' ]; /** @@ -99,7 +105,7 @@ class LogHandler extends Provider implements EventReceiver if (($e = $event->getSource()) instanceof \Exception) { /** @var \Exception $e */ - $eventlogData->setDescription($e->getMessage()); + $eventlogData->setDescription(__($e->getMessage())); $eventlogData->setLevel('ERROR'); } elseif (($eventMessage = $event->getEventMessage()) !== null) { $eventlogData->setDescription($eventMessage->composeText()); diff --git a/lib/SP/Providers/Mail/MailHandler.php b/lib/SP/Providers/Mail/MailHandler.php index b2245f33..5fcc4610 100644 --- a/lib/SP/Providers/Mail/MailHandler.php +++ b/lib/SP/Providers/Mail/MailHandler.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -24,143 +24,110 @@ namespace SP\Providers\Mail; -use PHPMailer\PHPMailer\Exception; -use PHPMailer\PHPMailer\PHPMailer; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; -use SP\Bootstrap; use SP\Core\Events\Event; -use SP\Core\Events\EventMessage; +use SP\Core\Events\EventReceiver; use SP\Core\Messages\MailMessage; -use SP\Html\Html; use SP\Providers\Provider; -use SP\Util\Util; +use SplSubject; /** - * Class Mailer + * Class MailHandler * * @package SP\Providers\Mail */ -class MailHandler extends Provider +class MailHandler extends Provider implements EventReceiver { - /** - * @var PHPMailer - */ - private $mailer; - /** - * @var array - */ - private $appInfo; + const EVENTS = [ + 'create.', + 'delete.', + 'edit.', + 'save.', + 'clear.eventlog', + 'refresh.masterPassword', + 'update.masterPassword.end', + 'import.ldap.end', + 'run.backup.end', + 'run.import.end' + ]; /** - * @param string $subject - * @param string $to - * @param MailMessage $mailMessage - * @throws MailHandlerException + * @var MailProvider */ - public function send($subject, $to, MailMessage $mailMessage) + protected $mailProvider; + /** + * @var string + */ + protected $events; + + /** + * Inicialización del observador + */ + public function init() { - $this->mailer->isHTML(); - $this->mailer->addAddress($to); - $this->mailer->Subject = sprintf('%s - %s', $this->appInfo['appname'], $subject);; - $this->mailer->Body = $mailMessage->setFooter($this->getEmailFooter())->composeHtml(); - - $this->sendMail(); + // TODO: Implement init() method. } /** - * Devolver el pie del email con la firma de la aplicación + * Evento de actualización + * + * @param string $eventType Nombre del evento + * @param Event $event Objeto del evento + */ + public function updateEvent($eventType, Event $event) + { + if (($eventMessage = $event->getEventMessage()) !== null) { + try { + $configData = $this->config->getConfigData(); + + $mailMessage = new MailMessage(); + $mailMessage->setDescription($eventMessage->getDescriptionRaw()); + + $this->mailProvider->send($eventMessage->getDescription(), $configData->getMailFrom(), $mailMessage); + } catch (\Exception $e) { + processException($e); + } + } + } + + /** + * Devuelve los eventos que implementa el observador * * @return array */ - protected function getEmailFooter() + public function getEvents() { - return [ - '', - '--', - sprintf('%s - %s', $this->appInfo['appname'], $this->appInfo['appdesc']), - Html::anchorText(Bootstrap::$WEBURI) - ]; + return self::EVENTS; } /** - * @throws MailHandlerException + * Devuelve los eventos que implementa el observador en formato cadena + * + * @return string */ - private function sendMail() + public function getEventsString() { - try { - $this->mailer->send(); - - $this->eventDispatcher->notifyEvent('mail.send', new Event($this, - EventMessage::factory() - ->addDescription(__u('Correo enviado')) - ->addDetail(__u('Destinatario'), implode(',', $this->mailer->getToAddresses()))) - ); - } catch (Exception $e) { - processException($e); - - $this->eventDispatcher->notifyEvent('exception', new Event($e)); - - throw new MailHandlerException(__u('Error al enviar correo')); - } + return $this->events; } /** - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - * @throws MailHandlerException + * Receive update from subject + * + * @link http://php.net/manual/en/splobserver.update.php + * @param SplSubject $subject

+ * The SplSubject notifying the observer of an update. + *

+ * @return void + * @since 5.1.0 */ + public function update(SplSubject $subject) + { + // TODO: Implement update() method. + } + protected function initialize() { - $this->mailer = $this->getMailer(); - $this->appInfo = Util::getAppInfo(); - } + $this->mailProvider = $this->dic->get(MailProvider::class); - /** - * Inicializar la clase PHPMailer. - * - * @return PHPMailer - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - * @throws MailHandlerException - */ - private function getMailer() - { - try { - $configData = $this->config->getConfigData(); - - $mailer = $this->dic->get(PHPMailer::class); - $mailer->SMTPAutoTLS = false; - $mailer->isSMTP(); - $mailer->CharSet = 'utf-8'; - $mailer->Host = $configData->getMailServer(); - $mailer->Port = $configData->getMailPort(); - - if ($configData->isMailAuthenabled()) { - $mailer->SMTPAuth = true; - $mailer->Username = $configData->getMailUser(); - $mailer->Password = $configData->getMailPass(); - } - - $mailer->SMTPSecure = strtolower($configData->getMailSecurity()); - //$mail->SMTPDebug = 2; - //$mail->Debugoutput = 'error_log'; - - $mailer->setFrom($configData->getMailFrom(), $this->appInfo['appname']); - $mailer->addReplyTo($configData->getMailFrom(), $this->appInfo['appname']); - $mailer->WordWrap = 100; - - return $mailer; - } catch (\Exception $e) { - processException($e); - - throw new MailHandlerException( - __u('No es posible inicializar'), - MailHandlerException::ERROR, - $e->getMessage(), - $e->getCode(), - $e - ); - } + $this->events = str_replace('.', '\\.', implode('|', self::EVENTS)); } } \ No newline at end of file diff --git a/lib/SP/Providers/Mail/MailProvider.php b/lib/SP/Providers/Mail/MailProvider.php new file mode 100644 index 00000000..7d02bf05 --- /dev/null +++ b/lib/SP/Providers/Mail/MailProvider.php @@ -0,0 +1,186 @@ +. + */ + +namespace SP\Providers\Mail; + +use PHPMailer\PHPMailer\Exception; +use PHPMailer\PHPMailer\PHPMailer; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; +use SP\Bootstrap; +use SP\Core\Events\Event; +use SP\Core\Events\EventMessage; +use SP\Core\Messages\MailMessage; +use SP\Html\Html; +use SP\Providers\Provider; +use SP\Util\Util; + +/** + * Class MailProvider + * + * @package SP\Providers\Mail + */ +class MailProvider extends Provider +{ + /** + * @var PHPMailer + */ + private $mailer; + /** + * @var array + */ + private $appInfo; + + /** + * @param string $subject + * @param string $to + * @param MailMessage $mailMessage + * @throws MailProviderException + */ + public function send($subject, $to, MailMessage $mailMessage) + { + $this->mailer->isHTML(); + $this->mailer->addAddress($to); + $this->mailer->Subject = sprintf('%s - %s', $this->appInfo['appname'], $subject);; + $this->mailer->Body = $mailMessage->setFooter($this->getEmailFooter())->composeHtml(); + + $this->sendMail(); + } + + /** + * Devolver el pie del email con la firma de la aplicación + * + * @return array + */ + protected function getEmailFooter() + { + return [ + '', + '--', + sprintf('%s - %s', $this->appInfo['appname'], $this->appInfo['appdesc']), + Html::anchorText(Bootstrap::$WEBURI) + ]; + } + + /** + * @throws MailProviderException + */ + private function sendMail() + { + try { + $this->mailer->send(); + + $this->eventDispatcher->notifyEvent('mail.send', new Event($this, + EventMessage::factory() + ->addDescription(__u('Correo enviado')) + ->addDetail(__u('Destinatario'), implode(',', $this->mailer->getToAddresses()))) + ); + } catch (Exception $e) { + processException($e); + + $this->eventDispatcher->notifyEvent('exception', new Event($e)); + + throw new MailProviderException(__u('Error al enviar correo')); + } + } + + /** + * @param string $subject + * @param array $to + * @param MailMessage $mailMessage + * @throws MailProviderException + */ + public function sendBatch($subject, array $to, MailMessage $mailMessage) + { + $this->mailer->isHTML(); + + foreach ($to as $address) { + $this->mailer->addAddress($address); + } + + $this->mailer->Subject = sprintf('%s - %s', $this->appInfo['appname'], $subject);; + $this->mailer->Body = $mailMessage->setFooter($this->getEmailFooter())->composeHtml(); + + $this->sendMail(); + } + + /** + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws MailProviderException + */ + protected function initialize() + { + $this->mailer = $this->getMailer(); + $this->appInfo = Util::getAppInfo(); + } + + /** + * Inicializar la clase PHPMailer. + * + * @return PHPMailer + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws MailProviderException + */ + private function getMailer() + { + try { + $configData = $this->config->getConfigData(); + + $mailer = $this->dic->get(PHPMailer::class); + $mailer->SMTPAutoTLS = false; + $mailer->isSMTP(); + $mailer->CharSet = 'utf-8'; + $mailer->Host = $configData->getMailServer(); + $mailer->Port = $configData->getMailPort(); + + if ($configData->isMailAuthenabled()) { + $mailer->SMTPAuth = true; + $mailer->Username = $configData->getMailUser(); + $mailer->Password = $configData->getMailPass(); + } + + $mailer->SMTPSecure = strtolower($configData->getMailSecurity()); + //$mail->SMTPDebug = 2; + //$mail->Debugoutput = 'error_log'; + + $mailer->setFrom($configData->getMailFrom(), $this->appInfo['appname']); + $mailer->addReplyTo($configData->getMailFrom(), $this->appInfo['appname']); + $mailer->WordWrap = 100; + + return $mailer; + } catch (\Exception $e) { + processException($e); + + throw new MailProviderException( + __u('No es posible inicializar'), + MailProviderException::ERROR, + $e->getMessage(), + $e->getCode(), + $e + ); + } + } +} \ No newline at end of file diff --git a/lib/SP/Providers/Mail/MailHandlerException.php b/lib/SP/Providers/Mail/MailProviderException.php similarity index 95% rename from lib/SP/Providers/Mail/MailHandlerException.php rename to lib/SP/Providers/Mail/MailProviderException.php index 0733359b..23adb503 100644 --- a/lib/SP/Providers/Mail/MailHandlerException.php +++ b/lib/SP/Providers/Mail/MailProviderException.php @@ -31,7 +31,7 @@ use SP\Core\Exceptions\SPException; * * @package SP\Providers\Mail */ -class MailHandlerException extends SPException +class MailProviderException extends SPException { } \ No newline at end of file diff --git a/lib/SP/Providers/Provider.php b/lib/SP/Providers/Provider.php index d568ee1f..e6729684 100644 --- a/lib/SP/Providers/Provider.php +++ b/lib/SP/Providers/Provider.php @@ -59,17 +59,16 @@ abstract class Provider /** * Provider constructor. * - * @param Container $dic - * @param Config $config - * @param Session $session - * @param EventDispatcher $eventDispatcher + * @param Container $dic + * @throws \DI\DependencyException + * @throws \DI\NotFoundException */ - final public function __construct(Container $dic, Config $config, Session $session, EventDispatcher $eventDispatcher) + public function __construct(Container $dic) { $this->dic = $dic; - $this->config = $config; - $this->session = $session; - $this->eventDispatcher = $eventDispatcher; + $this->config = $dic->get(Config::class); + $this->session = $dic->get(Session::class); + $this->eventDispatcher = $dic->get(EventDispatcher::class); if (method_exists($this, 'initialize')) { $this->initialize(); diff --git a/lib/SP/Repositories/Category/CategoryRepository.php b/lib/SP/Repositories/Category/CategoryRepository.php index 2f11a5bf..397b3247 100644 --- a/lib/SP/Repositories/Category/CategoryRepository.php +++ b/lib/SP/Repositories/Category/CategoryRepository.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -27,6 +27,7 @@ namespace SP\Repositories\Category; use SP\Core\Exceptions\SPException; use SP\DataModel\CategoryData; use SP\DataModel\ItemSearchData; +use SP\Repositories\DuplicatedItemException; use SP\Repositories\Repository; use SP\Repositories\RepositoryItemInterface; use SP\Repositories\RepositoryItemTrait; @@ -46,26 +47,24 @@ class CategoryRepository extends Repository implements RepositoryItemInterface * Creates an item * * @param CategoryData $itemData - * @return mixed + * @return int * @throws SPException + * @throws DuplicatedItemException */ public function create($itemData) { if ($this->checkDuplicatedOnAdd($itemData)) { - throw new SPException(__u('Categoría duplicada'), SPException::WARNING); + throw new DuplicatedItemException(__u('Categoría duplicada'), DuplicatedItemException::WARNING); } - $query = /** @lang SQL */ - 'INSERT INTO Category SET name = ?, description = ?, `hash` = ?'; + $queryData = new QueryData(); + $queryData->setQuery('INSERT INTO Category SET `name` = ?, description = ?, `hash` = ?'); + $queryData->addParam($itemData->getName()); + $queryData->addParam($itemData->getDescription()); + $queryData->addParam($this->makeItemHash($itemData->getName(), $this->db->getDbHandler())); + $queryData->setOnErrorMessage(__u('Error al crear la categoría')); - $Data = new QueryData(); - $Data->setQuery($query); - $Data->addParam($itemData->getName()); - $Data->addParam($itemData->getDescription()); - $Data->addParam($this->makeItemHash($itemData->getName(), $this->db->getDbHandler())); - $Data->setOnErrorMessage(__u('Error al crear la categoría')); - - DbWrapper::getQuery($Data, $this->db); + DbWrapper::getQuery($queryData, $this->db); return $this->db->getLastId(); } @@ -80,17 +79,14 @@ class CategoryRepository extends Repository implements RepositoryItemInterface */ public function checkDuplicatedOnAdd($itemData) { - $query = /** @lang SQL */ - 'SELECT id FROM Category WHERE `hash` = ? OR name = ?'; + $queryData = new QueryData(); + $queryData->setQuery('SELECT id FROM Category WHERE `hash` = ? OR `name` = ?'); + $queryData->addParam($this->makeItemHash($itemData->getName(), $this->db->getDbHandler())); + $queryData->addParam($itemData->getName()); - $Data = new QueryData(); - $Data->setQuery($query); - $Data->addParam($this->makeItemHash($itemData->getName(), $this->db->getDbHandler())); - $Data->addParam($itemData->getName()); + DbWrapper::getQuery($queryData, $this->db); - DbWrapper::getQuery($Data, $this->db); - - return $Data->getQueryNumRows() > 0; + return $queryData->getQueryNumRows() > 0; } /** @@ -99,31 +95,32 @@ class CategoryRepository extends Repository implements RepositoryItemInterface * @param CategoryData $itemData * @return mixed * @throws SPException + * @throws DuplicatedItemException * @throws \SP\Core\Exceptions\ConstraintException * @throws \SP\Core\Exceptions\QueryException */ public function update($itemData) { if ($this->checkDuplicatedOnUpdate($itemData)) { - throw new SPException(__u('Nombre de categoría duplicado'), SPException::WARNING); + throw new DuplicatedItemException(__u('Nombre de categoría duplicado'), DuplicatedItemException::WARNING); } $query = /** @lang SQL */ 'UPDATE Category - SET name = ?, + SET `name` = ?, description = ?, `hash` = ? WHERE id = ? LIMIT 1'; - $Data = new QueryData(); - $Data->setQuery($query); - $Data->addParam($itemData->getName()); - $Data->addParam($itemData->getDescription()); - $Data->addParam($this->makeItemHash($itemData->getName(), $this->db->getDbHandler())); - $Data->addParam($itemData->getId()); - $Data->setOnErrorMessage(__u('Error al actualizar la categoría')); + $queryData = new QueryData(); + $queryData->setQuery($query); + $queryData->addParam($itemData->getName()); + $queryData->addParam($itemData->getDescription()); + $queryData->addParam($this->makeItemHash($itemData->getName(), $this->db->getDbHandler())); + $queryData->addParam($itemData->getId()); + $queryData->setOnErrorMessage(__u('Error al actualizar la categoría')); - DbWrapper::getQuery($Data, $this->db); + DbWrapper::getQuery($queryData, $this->db); return $this; } @@ -138,37 +135,48 @@ class CategoryRepository extends Repository implements RepositoryItemInterface */ public function checkDuplicatedOnUpdate($itemData) { - $query = /** @lang SQL */ - 'SELECT id FROM Category WHERE (`hash` = ? OR name = ?) AND id <> ?'; + $queryData = new QueryData(); + $queryData->setQuery('SELECT id FROM Category WHERE (`hash` = ? OR `name` = ?) AND id <> ?'); + $queryData->addParam($this->makeItemHash($itemData->getName(), $this->db->getDbHandler())); + $queryData->addParam($itemData->getName()); + $queryData->addParam($itemData->getId()); - $Data = new QueryData(); - $Data->setQuery($query); - $Data->addParam($this->makeItemHash($itemData->getName(), $this->db->getDbHandler())); - $Data->addParam($itemData->getName()); - $Data->addParam($itemData->getId()); + DbWrapper::getQuery($queryData, $this->db); - DbWrapper::getQuery($Data, $this->db); - - return $Data->getQueryNumRows() > 0; + return $queryData->getQueryNumRows() > 0; } /** * Returns the item for given id * * @param int $id - * @return mixed + * @return CategoryData */ public function getById($id) { - $query = /** @lang SQL */ - 'SELECT id, name, description FROM Category WHERE id = ? LIMIT 1'; + $queryData = new QueryData(); + $queryData->setQuery('SELECT id, `name`, description FROM Category WHERE id = ? LIMIT 1'); + $queryData->addParam($id); + $queryData->setMapClassName(CategoryData::class); - $Data = new QueryData(); - $Data->setQuery($query); - $Data->addParam($id); - $Data->setMapClassName(CategoryData::class); + return DbWrapper::getResults($queryData, $this->db); + } - return DbWrapper::getResults($Data, $this->db); + /** + * Returns the item for given id + * + * @param string $name + * @return CategoryData + */ + public function getByName($name) + { + $queryData = new QueryData(); + $queryData->setQuery('SELECT id, `name`, description FROM Category WHERE `name` = ? OR `hash` = ? LIMIT 1'); + $queryData->addParam($name); + $queryData->addParam($this->makeItemHash($name, $this->db->getDbHandler())); + $queryData->setMapClassName(CategoryData::class); + + return DbWrapper::getResults($queryData, $this->db); } /** @@ -178,14 +186,11 @@ class CategoryRepository extends Repository implements RepositoryItemInterface */ public function getAll() { - $query = /** @lang SQL */ - 'SELECT id, name, description, `hash` FROM Category ORDER BY name'; + $queryData = new QueryData(); + $queryData->setMapClassName(CategoryData::class); + $queryData->setQuery('SELECT id, `name`, description, `hash` FROM Category ORDER BY `name`'); - $Data = new QueryData(); - $Data->setMapClassName(CategoryData::class); - $Data->setQuery($query); - - return DbWrapper::getResultsArray($Data, $this->db); + return DbWrapper::getResultsArray($queryData, $this->db); } /** @@ -197,14 +202,14 @@ class CategoryRepository extends Repository implements RepositoryItemInterface public function getByIdBatch(array $ids) { $query = /** @lang SQL */ - 'SELECT id, name, description FROM Category WHERE id IN (' . $this->getParamsFromArray($ids) . ')'; + 'SELECT id, `name`, description FROM Category WHERE id IN (' . $this->getParamsFromArray($ids) . ')'; - $Data = new QueryData(); - $Data->setQuery($query); - $Data->setParams($ids); - $Data->setMapClassName(CategoryData::class); + $queryData = new QueryData(); + $queryData->setQuery($query); + $queryData->setParams($ids); + $queryData->setMapClassName(CategoryData::class); - return DbWrapper::getResultsArray($Data, $this->db); + return DbWrapper::getResultsArray($queryData, $this->db); } /** @@ -217,12 +222,12 @@ class CategoryRepository extends Repository implements RepositoryItemInterface */ public function deleteByIdBatch(array $ids) { - $Data = new QueryData(); - $Data->setQuery('DELETE FROM Category WHERE id IN (' . $this->getParamsFromArray($ids) . ')'); - $Data->setParams($ids); - $Data->setOnErrorMessage(__u('Error al eliminar la categorías')); + $queryData = new QueryData(); + $queryData->setQuery('DELETE FROM Category WHERE id IN (' . $this->getParamsFromArray($ids) . ')'); + $queryData->setParams($ids); + $queryData->setOnErrorMessage(__u('Error al eliminar la categorías')); - DbWrapper::getQuery($Data, $this->db); + DbWrapper::getQuery($queryData, $this->db); return $this->db->getNumRows(); } @@ -240,14 +245,14 @@ class CategoryRepository extends Repository implements RepositoryItemInterface $query = /** @lang SQL */ 'DELETE FROM Category WHERE id = ? LIMIT 1'; - $Data = new QueryData(); - $Data->setQuery($query); - $Data->addParam($id); - $Data->setOnErrorMessage(__u('Error al eliminar la categoría')); + $queryData = new QueryData(); + $queryData->setQuery($query); + $queryData->addParam($id); + $queryData->setOnErrorMessage(__u('Error al eliminar la categoría')); - DbWrapper::getQuery($Data, $this->db); + DbWrapper::getQuery($queryData, $this->db); - return $Data->getQueryNumRows(); + return $queryData->getQueryNumRows(); } /** @@ -269,29 +274,29 @@ class CategoryRepository extends Repository implements RepositoryItemInterface */ public function search(ItemSearchData $SearchData) { - $Data = new QueryData(); - $Data->setSelect('id, name, description'); - $Data->setFrom('Category'); - $Data->setOrder('name'); + $queryData = new QueryData(); + $queryData->setSelect('id, name, description'); + $queryData->setFrom('Category'); + $queryData->setOrder('name'); if ($SearchData->getSeachString() !== '') { - $Data->setWhere('name LIKE ? OR description LIKE ?'); + $queryData->setWhere('name LIKE ? OR description LIKE ?'); $search = '%' . $SearchData->getSeachString() . '%'; - $Data->addParam($search); - $Data->addParam($search); + $queryData->addParam($search); + $queryData->addParam($search); } - $Data->setLimit('?,?'); - $Data->addParam($SearchData->getLimitStart()); - $Data->addParam($SearchData->getLimitCount()); + $queryData->setLimit('?,?'); + $queryData->addParam($SearchData->getLimitStart()); + $queryData->addParam($SearchData->getLimitCount()); DbWrapper::setFullRowCount(); /** @var array $queryRes */ - $queryRes = DbWrapper::getResultsArray($Data, $this->db); + $queryRes = DbWrapper::getResultsArray($queryData, $this->db); - $queryRes['count'] = $Data->getQueryNumRows(); + $queryRes['count'] = $queryData->getQueryNumRows(); return $queryRes; } diff --git a/lib/SP/Repositories/Client/ClientRepository.php b/lib/SP/Repositories/Client/ClientRepository.php index 0c70b45c..036a46ce 100644 --- a/lib/SP/Repositories/Client/ClientRepository.php +++ b/lib/SP/Repositories/Client/ClientRepository.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -29,6 +29,7 @@ use SP\DataModel\ClientData; use SP\DataModel\ItemData; use SP\DataModel\ItemSearchData; use SP\Mvc\Model\QueryCondition; +use SP\Repositories\DuplicatedItemException; use SP\Repositories\Repository; use SP\Repositories\RepositoryItemInterface; use SP\Repositories\RepositoryItemTrait; @@ -48,18 +49,19 @@ class ClientRepository extends Repository implements RepositoryItemInterface * Creates an item * * @param ClientData $itemData - * @return mixed + * @return int + * @throws DuplicatedItemException * @throws SPException */ public function create($itemData) { if ($this->checkDuplicatedOnAdd($itemData)) { - throw new SPException(__u('Cliente duplicado'), SPException::WARNING); + throw new DuplicatedItemException(__u('Cliente duplicado'), DuplicatedItemException::WARNING); } $query = /** @lang SQL */ 'INSERT INTO Client - SET name = ?, + SET `name` = ?, description = ?, isGlobal = ?, `hash` = ?'; @@ -101,19 +103,19 @@ class ClientRepository extends Repository implements RepositoryItemInterface * * @param ClientData $itemData * @return mixed - * @throws SPException * @throws \SP\Core\Exceptions\ConstraintException * @throws \SP\Core\Exceptions\QueryException + * @throws DuplicatedItemException */ public function update($itemData) { if ($this->checkDuplicatedOnUpdate($itemData)) { - throw new SPException(__u('Cliente duplicado'), SPException::WARNING); + throw new DuplicatedItemException(__u('Cliente duplicado'), DuplicatedItemException::WARNING); } $query = /** @lang SQL */ 'UPDATE Client - SET name = ?, + SET `name` = ?, description = ?, isGlobal = ?, `hash` = ? @@ -157,18 +159,35 @@ class ClientRepository extends Repository implements RepositoryItemInterface * Returns the item for given id * * @param int $id - * @return mixed + * @return ClientData */ public function getById($id) { $queryData = new QueryData(); - $queryData->setQuery('SELECT id, name, description, isGlobal FROM Client WHERE id = ? LIMIT 1'); + $queryData->setQuery('SELECT id, `name`, description, isGlobal FROM Client WHERE id = ? LIMIT 1'); $queryData->addParam($id); $queryData->setMapClassName(ClientData::class); return DbWrapper::getResults($queryData, $this->db); } + /** + * Returns the item for given name + * + * @param string $name + * @return ClientData + */ + public function getByName($name) + { + $queryData = new QueryData(); + $queryData->setQuery('SELECT id, `name`, description, isGlobal FROM Client WHERE `name` = ? OR `hash` = ? LIMIT 1'); + $queryData->addParam($name); + $queryData->addParam($this->makeItemHash($name, $this->db->getDbHandler())); + $queryData->setMapClassName(ClientData::class); + + return DbWrapper::getResults($queryData, $this->db); + } + /** * Returns all the items * @@ -177,7 +196,7 @@ class ClientRepository extends Repository implements RepositoryItemInterface public function getAll() { $queryData = new QueryData(); - $queryData->setQuery('SELECT id, name, description, isGlobal FROM Client ORDER BY name'); + $queryData->setQuery('SELECT id, `name`, description, isGlobal FROM Client ORDER BY `name`'); $queryData->setMapClassName(ClientData::class); return DbWrapper::getResultsArray($queryData, $this->db); @@ -192,7 +211,7 @@ class ClientRepository extends Repository implements RepositoryItemInterface public function getByIdBatch(array $ids) { $query = /** @lang SQL */ - 'SELECT id, name, description, isGlobal FROM Client WHERE id IN (' . $this->getParamsFromArray($ids) . ')'; + 'SELECT id, `name`, description, isGlobal FROM Client WHERE id IN (' . $this->getParamsFromArray($ids) . ')'; $queryData = new QueryData(); $queryData->setQuery($query); @@ -213,7 +232,7 @@ class ClientRepository extends Repository implements RepositoryItemInterface public function deleteByIdBatch(array $ids) { $queryData = new QueryData(); - $queryData->setQuery('DELETE FROM Client WHERE id IN ('. $this->getParamsFromArray($ids) . ')'); + $queryData->setQuery('DELETE FROM Client WHERE id IN (' . $this->getParamsFromArray($ids) . ')'); $queryData->setParams($ids); $queryData->setOnErrorMessage(__u('Error al eliminar los clientes')); @@ -291,19 +310,19 @@ class ClientRepository extends Repository implements RepositoryItemInterface * Devolver los clientes visibles por el usuario * * @param QueryCondition $queryFilter - * @return array + * @return ItemData[] */ public function getAllForFilter(QueryCondition $queryFilter) { $query = /** @lang SQL */ 'SELECT C.id, C.name FROM Account A - RIGHT JOIN Client C ON clientId = C.id + RIGHT JOIN Client C ON A.clientId = C.id WHERE A.clientId IS NULL - OR isGlobal = 1 + OR C.isGlobal = 1 OR (' . $queryFilter->getFilters() . ') GROUP BY id - ORDER BY name'; + ORDER BY C.name'; $queryData = new QueryData(); $queryData->setMapClassName(ItemData::class); diff --git a/lib/SP/Storage/CacheableInterface.php b/lib/SP/Repositories/DuplicatedItemException.php similarity index 82% rename from lib/SP/Storage/CacheableInterface.php rename to lib/SP/Repositories/DuplicatedItemException.php index 5282bc30..15636589 100644 --- a/lib/SP/Storage/CacheableInterface.php +++ b/lib/SP/Repositories/DuplicatedItemException.php @@ -22,19 +22,16 @@ * along with sysPass. If not, see . */ -namespace SP\Storage; +namespace SP\Repositories; +use SP\Core\Exceptions\SPException; /** - * Interface CacheableInterface + * Class DuplicatedItemException * - * @package SP\Storage + * @package SP\Repositories */ -interface CacheableInterface +class DuplicatedItemException extends SPException { - /** - * @return mixed - */ - public function save(); } \ No newline at end of file diff --git a/lib/SP/Repositories/User/UserRepository.php b/lib/SP/Repositories/User/UserRepository.php index 6c92ac49..ed6f9570 100644 --- a/lib/SP/Repositories/User/UserRepository.php +++ b/lib/SP/Repositories/User/UserRepository.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -135,7 +135,7 @@ class UserRepository extends Repository implements RepositoryItemInterface /** * Updates an user's pass * - * @param int $id + * @param int $id * @param UpdatePassRequest $passRequest * @return bool * @throws \SP\Core\Exceptions\ConstraintException @@ -676,7 +676,7 @@ class UserRepository extends Repository implements RepositoryItemInterface /** * Updates an user's pass * - * @param int $id + * @param int $id * @param UserPreferencesData $userPreferencesData * @return bool * @throws \SP\Core\Exceptions\ConstraintException @@ -692,4 +692,30 @@ class UserRepository extends Repository implements RepositoryItemInterface return DbWrapper::getQuery($queryData, $this->db); } + + /** + * Obtener el email de los usuarios de un grupo + * + * @param $groupId + * @return array + */ + public function getUserEmailForGroup($groupId) + { + $query = /** @lang SQL */ + 'SELECT U.id, U.login, U.name, U.email + FROM User U + INNER JOIN UserGroup UG ON U.userGroupId = UG.id + LEFT JOIN UserToUserGroup UUG ON U.id = UUG.userId + WHERE U.email IS NOT NULL + AND U.userGroupId = ? OR UUG.userGroupId = ? + AND U.isDisabled = 0 + ORDER BY U.login'; + + $queryData = new QueryData(); + $queryData->setQuery($query); + $queryData->addParam($groupId); + $queryData->addParam($groupId); + + return DbWrapper::getResultsArray($queryData, $this->db); + } } \ No newline at end of file diff --git a/lib/SP/Services/Account/AccountAclService.php b/lib/SP/Services/Account/AccountAclService.php index 7ac1f780..1b793509 100644 --- a/lib/SP/Services/Account/AccountAclService.php +++ b/lib/SP/Services/Account/AccountAclService.php @@ -32,6 +32,7 @@ use SP\Services\Service; use SP\Services\User\UserLoginResponse; use SP\Services\UserGroup\UserToUserGroupService; use SP\Storage\FileCache; +use SP\Storage\FileException; use SP\Util\ArrayUtil; use SP\Util\FileUtil; @@ -136,7 +137,7 @@ class AccountAclService extends Service { try { return $this->fileCache->load($this->getCacheFileForAcl($accountId, $actionId)); - } catch (\RuntimeException $e) { + } catch (FileException $e) { return null; } } @@ -311,7 +312,7 @@ class AccountAclService extends Service { try { return $this->fileCache->save($this->getCacheFileForAcl($accountAcl->getAccountId(), $accountAcl->getActionId()), $accountAcl); - } catch (\RuntimeException $e) { + } catch (FileException $e) { return null; } } diff --git a/lib/SP/Services/Account/AccountCryptService.php b/lib/SP/Services/Account/AccountCryptService.php index e0a2bc3b..846ed048 100644 --- a/lib/SP/Services/Account/AccountCryptService.php +++ b/lib/SP/Services/Account/AccountCryptService.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -27,6 +27,7 @@ namespace SP\Services\Account; use Defuse\Crypto\Exception\CryptoException; use SP\Core\Crypt\Crypt; use SP\Core\Events\Event; +use SP\Core\Events\EventMessage; use SP\Core\Exceptions\SPException; use SP\Core\OldCrypt; use SP\Core\TaskFactory; @@ -71,9 +72,10 @@ class AccountCryptService extends Service $accountsOk = []; $errorCount = 0; - $messages = []; - $this->eventDispatcher->notifyEvent('update.masterPassword.accounts.start', new Event($this, [__u('Actualizar Clave Maestra')])); + $this->eventDispatcher->notifyEvent('update.masterPassword.accounts.start', + new Event($this, EventMessage::factory()->addDescription(__u('Actualizar Clave Maestra'))) + ); if (!OldCrypt::checkCryptModule()) { throw new ServiceException(__u('Error en el módulo de encriptación'), ServiceException::ERROR); @@ -93,7 +95,7 @@ class AccountCryptService extends Service $counter = 0; $startTime = time(); $configData = $this->config->getConfigData(); - $accountRequestBase = new AccountPasswordRequest(); + $eventMessage = EventMessage::factory(); foreach ($accountsPass as $account) { // No realizar cambios si está en modo demo @@ -115,8 +117,7 @@ class AccountCryptService extends Service debugLog($taskMessage->composeText()); } - $accountRequest = clone $accountRequestBase; - + $accountRequest = new AccountPasswordRequest(); $accountRequest->id = $account->id; try { @@ -134,17 +135,15 @@ class AccountCryptService extends Service $counter++; } catch (SPException $e) { $errorCount++; - $messages[] = __u('Fallo al actualizar la clave de la cuenta'); - $messages[] = sprintf('%s (%d)', $account->name, $account->id); + $eventMessage->addDescription(__u('Fallo al actualizar la clave de la cuenta')); + $eventMessage->addDetail($account->name, $account->id); } } - $messages[] = __u('Cuentas actualizadas'); - $messages[] = implode(',', $accountsOk); - $messages[] = __u('Errores'); - $messages[] = $errorCount; + $eventMessage->addDetail(__u('Cuentas actualizadas'), implode(',', $accountsOk)); + $eventMessage->addDetail(__u('Errores'), $errorCount); - $this->eventDispatcher->notifyEvent('update.masterPassword.accounts.end', new Event($this, $messages)); + $this->eventDispatcher->notifyEvent('update.masterPassword.accounts.end', new Event($this, $eventMessage)); } /** @@ -158,26 +157,35 @@ class AccountCryptService extends Service $this->request = $updateMasterPassRequest; try { - $this->eventDispatcher->notifyEvent('update.masterPassword.accounts.start', new Event($this, [__u('Actualizar Clave Maestra')])); + $this->eventDispatcher->notifyEvent('update.masterPassword.accounts.start', + new Event($this, EventMessage::factory()->addDescription(__u('Actualizar Clave Maestra'))) + ); $taskId = $this->request->getTask(); TaskFactory::update($taskId, TaskFactory::createMessage($taskId, __u('Actualizar Clave Maestra'))); - $process = $this->processAccounts($this->accountService->getAccountsPassData(), function ($request) { + $eventMessage = $this->processAccounts($this->accountService->getAccountsPassData(), function ($request) { $this->accountService->updatePasswordMasterPass($request); }); - $this->eventDispatcher->notifyEvent('update.masterPassword.accounts.end', new Event($this, $process)); + $this->eventDispatcher->notifyEvent('update.masterPassword.accounts.end', new Event($this, $eventMessage)); } catch (\Exception $e) { - throw new ServiceException(__u('Errores al actualizar las claves de las cuentas'), ServiceException::ERROR, null, $e->getCode(), $e); + $this->eventDispatcher->notifyEvent('exception', new Event($e)); + + throw new ServiceException( + __u('Errores al actualizar las claves de las cuentas'), + ServiceException::ERROR, + null, + $e->getCode(), + $e); } } /** * @param array $accounts * @param callable $passUpdater - * @return array + * @return EventMessage * @throws ServiceException */ protected function processAccounts(array $accounts, callable $passUpdater) @@ -185,7 +193,6 @@ class AccountCryptService extends Service set_time_limit(0); $accountsOk = []; - $messages = []; $errorCount = 0; $counter = 0; $startTime = time(); @@ -197,9 +204,8 @@ class AccountCryptService extends Service $configData = $this->config->getConfigData(); $currentMasterPassHash = $this->request->getCurrentHash(); - $accountPasswordRequest = new AccountPasswordRequest(); - $taskId = $this->request->getTask()->getTaskId(); + $eventMessage = EventMessage::factory(); foreach ($accounts as $account) { // No realizar cambios si está en modo demo @@ -222,13 +228,12 @@ class AccountCryptService extends Service } if (isset($account->mPassHash) && $account->mPassHash !== $currentMasterPassHash) { - $messages[] = __u('La clave maestra del registro no coincide'); - $messages[] = sprintf('%s (%d)', $account->name, $account->id); + $eventMessage->addDescription(__u('La clave maestra del registro no coincide')); + $eventMessage->addDetail($account->name, $account->id); continue; } - $request = clone $accountPasswordRequest; - + $request = new AccountPasswordRequest(); $request->id = $account->id; try { @@ -247,22 +252,20 @@ class AccountCryptService extends Service $counter++; } catch (SPException $e) { $errorCount++; - $messages[] = __u('Fallo al actualizar la clave de la cuenta'); - $messages[] = sprintf('%s (%d)', $account->name, $account->id); + $eventMessage->addDescription(__u('Fallo al actualizar la clave de la cuenta')); + $eventMessage->addDetail($account->name, $account->id); } catch (CryptoException $e) { $errorCount++; - $messages[] = __u('Fallo al actualizar la clave de la cuenta'); - $messages[] = sprintf('%s (%d)', $account->name, $account->id); + $eventMessage->addDescription(__u('Fallo al actualizar la clave de la cuenta')); + $eventMessage->addDetail($account->name, $account->id); } } - $messages[] = __u('Cuentas actualizadas'); - $messages[] = implode(',', $accountsOk); - $messages[] = __u('Errores'); - $messages[] = $errorCount; + $eventMessage->addDetail(__u('Cuentas actualizadas'), implode(',', $accountsOk)); + $eventMessage->addDetail(__u('Errores'), $errorCount); - return $messages; + return $eventMessage; } /** @@ -276,22 +279,31 @@ class AccountCryptService extends Service $this->request = $updateMasterPassRequest; try { - $this->eventDispatcher->notifyEvent('update.masterPassword.accountsHistory.start', new Event($this, [__u('Actualizar Clave Maestra (H)')])); + $this->eventDispatcher->notifyEvent('update.masterPassword.accountsHistory.start', + new Event($this, EventMessage::factory()->addDescription(__u('Actualizar Clave Maestra (H)'))) + ); $taskId = $this->request->getTask(); TaskFactory::update($taskId, TaskFactory::createMessage($taskId, __u('Actualizar Clave Maestra (H)'))); - $process = $this->processAccounts($this->accountHistoryService->getAccountsPassData(), function ($request) { + $eventMessage = $this->processAccounts($this->accountHistoryService->getAccountsPassData(), function ($request) { /** @var AccountPasswordRequest $request */ $request->hash = $this->request->getHash(); $this->accountHistoryService->updatePasswordMasterPass($request); }); - $this->eventDispatcher->notifyEvent('update.masterPassword.accountsHistory.end', new Event($this, $process)); + $this->eventDispatcher->notifyEvent('update.masterPassword.accountsHistory.end', new Event($this, $eventMessage)); } catch (\Exception $e) { - throw new ServiceException(__u('Errores al actualizar las claves de las cuentas del histórico'), ServiceException::ERROR, null, $e->getCode(), $e); + $this->eventDispatcher->notifyEvent('exception', new Event($e)); + + throw new ServiceException( + __u('Errores al actualizar las claves de las cuentas del histórico'), + ServiceException::ERROR, + null, + $e->getCode(), + $e); } } diff --git a/lib/SP/Services/Account/AccountService.php b/lib/SP/Services/Account/AccountService.php index 8c5294bc..b2a3aa3f 100644 --- a/lib/SP/Services/Account/AccountService.php +++ b/lib/SP/Services/Account/AccountService.php @@ -31,7 +31,6 @@ use SP\Core\Crypt\Crypt; use SP\Core\Crypt\Session as CryptSession; use SP\Core\Exceptions\QueryException; use SP\Core\Exceptions\SPException; -use SP\Core\Session\Session; use SP\DataModel\AccountData; use SP\DataModel\Dto\AccountDetailsResponse; use SP\DataModel\ItemSearchData; @@ -69,10 +68,6 @@ class AccountService extends Service implements AccountServiceInterface * @var AccountToTagRepository */ protected $accountToTagRepository; - /** - * @var Session - */ - protected $session; /** * @throws \Psr\Container\ContainerExceptionInterface diff --git a/lib/SP/Services/Category/CategoryService.php b/lib/SP/Services/Category/CategoryService.php index db55ab12..6971a8f4 100644 --- a/lib/SP/Services/Category/CategoryService.php +++ b/lib/SP/Services/Category/CategoryService.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -28,6 +28,7 @@ use SP\Core\Exceptions\SPException; use SP\DataModel\CategoryData; use SP\DataModel\ItemSearchData; use SP\Repositories\Category\CategoryRepository; +use SP\Repositories\DuplicatedItemException; use SP\Services\Service; use SP\Services\ServiceException; use SP\Services\ServiceItemTrait; @@ -65,14 +66,25 @@ class CategoryService extends Service } /** - * @param $id - * @return mixed + * @param int $id + * @return CategoryData */ public function getById($id) { return $this->categoryRepository->getById($id); } + /** + * Returns the item for given id + * + * @param string $name + * @return CategoryData + */ + public function getByName($name) + { + return $this->categoryRepository->getByName($name); + } + /** * @param $id * @return $this @@ -109,8 +121,9 @@ class CategoryService extends Service /** * @param $itemData - * @return mixed + * @return int * @throws SPException + * @throws DuplicatedItemException */ public function create($itemData) { diff --git a/lib/SP/Services/Client/ClientService.php b/lib/SP/Services/Client/ClientService.php index 4a9a26e7..9140c31f 100644 --- a/lib/SP/Services/Client/ClientService.php +++ b/lib/SP/Services/Client/ClientService.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -26,10 +26,11 @@ namespace SP\Services\Client; use SP\Account\AccountUtil; use SP\Core\Exceptions\SPException; -use SP\Core\Session\Session; use SP\DataModel\ClientData; +use SP\DataModel\ItemData; use SP\DataModel\ItemSearchData; use SP\Repositories\Client\ClientRepository; +use SP\Repositories\DuplicatedItemException; use SP\Services\Service; use SP\Services\ServiceException; use SP\Services\ServiceItemTrait; @@ -47,19 +48,6 @@ class ClientService extends Service * @var ClientRepository */ protected $clientRepository; - /** - * @var Session - */ - protected $session; - - /** - * @throws \Psr\Container\ContainerExceptionInterface - * @throws \Psr\Container\NotFoundExceptionInterface - */ - protected function initialize() - { - $this->clientRepository = $this->dic->get(ClientRepository::class); - } /** * @param ItemSearchData $itemSearchData @@ -71,14 +59,25 @@ class ClientService extends Service } /** - * @param $id - * @return mixed + * @param int $id + * @return ClientData */ public function getById($id) { return $this->clientRepository->getById($id); } + /** + * Returns the item for given name + * + * @param string $name + * @return ClientData + */ + public function getByName($name) + { + return $this->clientRepository->getByName($name); + } + /** * @param $id * @return $this @@ -111,8 +110,9 @@ class ClientService extends Service /** * @param $itemData - * @return mixed + * @return int * @throws SPException + * @throws DuplicatedItemException */ public function create($itemData) { @@ -144,10 +144,19 @@ class ClientService extends Service /** * Returns all clients visible for a given user * - * @return array + * @return ItemData[] */ public function getAllForUser() { return $this->clientRepository->getAllForFilter(AccountUtil::getAccountFilterUser($this->session)); } + + /** + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + protected function initialize() + { + $this->clientRepository = $this->dic->get(ClientRepository::class); + } } \ No newline at end of file diff --git a/lib/SP/Services/Crypt/MasterPassService.php b/lib/SP/Services/Crypt/MasterPassService.php index 1d0cac13..757f01b2 100644 --- a/lib/SP/Services/Crypt/MasterPassService.php +++ b/lib/SP/Services/Crypt/MasterPassService.php @@ -79,16 +79,15 @@ class MasterPassService extends Service /** * @param UpdateMasterPassRequest $request + * @throws SPException * @throws ServiceException - * @throws \Psr\Container\ContainerExceptionInterface - * @throws \Psr\Container\NotFoundExceptionInterface */ public function changeMasterPassword(UpdateMasterPassRequest $request) { $db = $this->dic->get(Database::class); if (!DbWrapper::beginTransaction($db)) { - throw new ServiceException(__u('No es posible iniciar una transacción'), SPException::ERROR); + throw new ServiceException(__u('No es posible iniciar una transacción'), ServiceException::ERROR); } try { @@ -104,7 +103,7 @@ class MasterPassService extends Service } if (!DbWrapper::endTransaction($db)) { - throw new ServiceException(__u('No es posible finalizar una transacción'), SPException::ERROR); + throw new ServiceException(__u('No es posible finalizar una transacción'), ServiceException::ERROR); } } diff --git a/lib/SP/Services/Crypt/TemporaryMasterPassService.php b/lib/SP/Services/Crypt/TemporaryMasterPassService.php index 328392dd..f7f1792c 100644 --- a/lib/SP/Services/Crypt/TemporaryMasterPassService.php +++ b/lib/SP/Services/Crypt/TemporaryMasterPassService.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -48,6 +48,10 @@ class TemporaryMasterPassService extends Service * @var ConfigService */ protected $configService; + /** + * @var int + */ + protected $maxTime; /** * Crea una clave temporal para encriptar la clave maestra y guardarla. @@ -59,20 +63,23 @@ class TemporaryMasterPassService extends Service public function create($maxTime = 14400) { try { + $this->maxTime = time() + $maxTime; + // Encriptar la clave maestra con hash aleatorio generado $randomKey = Util::generateRandomBytes(32); $this->configService->save('tempmaster_passkey', Crypt::makeSecuredKey($randomKey)); $this->configService->save('tempmaster_passhash', Hash::hashKey($randomKey)); $this->configService->save('tempmaster_passtime', time()); - $this->configService->save('tempmaster_maxtime', time() + $maxTime); + $this->configService->save('tempmaster_maxtime', $this->maxTime); $this->configService->save('tempmaster_attempts', 0); // Guardar la clave temporal hasta que finalice la sesión $this->session->setTemporaryMasterPass($randomKey); - $this->eventDispatcher->notifyEvent('create.tempMasterPass', - new Event($this, EventMessage::factory()->addDescription(__u('Generar Clave Temporal'))) + $this->eventDispatcher->notifyEvent('create.tempMasterPassword', + new Event($this, EventMessage::factory() + ->addDescription(__u('Generar Clave Temporal'))) ); return $randomKey; @@ -100,7 +107,7 @@ class TemporaryMasterPassService extends Service // Comprobar si el tiempo de validez o los intentos se han superado if ($passMaxTime === 0) { - $this->eventDispatcher->notifyEvent('check.tempMasterPass', + $this->eventDispatcher->notifyEvent('check.tempMasterPassword', new Event($this, EventMessage::factory()->addDescription(__u('Clave temporal caducada'))) ); @@ -140,8 +147,9 @@ class TemporaryMasterPassService extends Service $this->configService->save('tempmaster_maxtime', ''); $this->configService->save('tempmaster_attempts', 0); - $this->eventDispatcher->notifyEvent('tempMasterPass.expire', - new Event($this, EventMessage::factory()->addDescription(__u('Clave temporal caducada'))) + $this->eventDispatcher->notifyEvent('expire.tempMasterPassword', + new Event($this, EventMessage::factory() + ->addDescription(__u('Clave temporal caducada'))) ); } @@ -160,6 +168,14 @@ class TemporaryMasterPassService extends Service $key); } + /** + * @return int + */ + public function getMaxTime() + { + return $this->maxTime; + } + /** * @throws \Psr\Container\ContainerExceptionInterface * @throws \Psr\Container\NotFoundExceptionInterface diff --git a/lib/SP/Services/CustomField/CustomFieldCryptService.php b/lib/SP/Services/CustomField/CustomFieldCryptService.php index 851cd331..8796b489 100644 --- a/lib/SP/Services/CustomField/CustomFieldCryptService.php +++ b/lib/SP/Services/CustomField/CustomFieldCryptService.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -28,7 +28,7 @@ defined('APP_ROOT') || die(); use SP\Core\Crypt\Crypt; use SP\Core\Events\Event; -use SP\Core\Exceptions\SPException; +use SP\Core\Events\EventMessage; use SP\Core\OldCrypt; use SP\Core\TaskFactory; use SP\DataModel\CustomFieldData; @@ -56,7 +56,6 @@ class CustomFieldCryptService extends Service * Actualizar los datos encriptados con una nueva clave * * @param UpdateMasterPassRequest $request - * @return array * @throws ServiceException */ public function updateMasterPasswordOld(UpdateMasterPassRequest $request) @@ -64,32 +63,38 @@ class CustomFieldCryptService extends Service $this->request = $request; try { - return $this->processUpdateMasterPassword(function (CustomFieldData $customFieldData) { + $this->processUpdateMasterPassword(function (CustomFieldData $customFieldData) { return OldCrypt::getDecrypt($customFieldData->getData(), $customFieldData->getKey(), $this->request->getCurrentMasterPass()); }); } catch (ServiceException $e) { throw $e; } catch (\Exception $e) { - throw new ServiceException(__u('Errores al actualizar datos de campos personalizados'), SPException::ERROR, null, $e->getCode(), $e); + $this->eventDispatcher->notifyEvent('exception', new Event($e)); + + throw new ServiceException( + __u('Errores al actualizar datos de campos personalizados'), + ServiceException::ERROR, + null, + $e->getCode(), + $e); } } /** * @param callable $decryptor - * @return array * @throws ServiceException - * @throws \SP\Core\Exceptions\InvalidArgumentException */ protected function processUpdateMasterPassword(callable $decryptor) { - $messages = []; $customFields = $this->customFieldService->getAll(); if (count($customFields) === 0) { - throw new ServiceException(__u('No hay datos de campos personalizados'), SPException::INFO); + throw new ServiceException(__u('No hay datos de campos personalizados'), ServiceException::INFO); } - $this->eventDispatcher->notifyEvent('update.masterPassword.customFields.start', new Event($this, [__u('Actualizar Clave Maestra')])); + $this->eventDispatcher->notifyEvent('update.masterPassword.customFields.start', + new Event($this, EventMessage::factory()->addDescription(__u('Actualizar Clave Maestra'))) + ); $taskId = $this->request->getTask()->getTaskId(); @@ -108,25 +113,24 @@ class CustomFieldCryptService extends Service } catch (\Exception $e) { processException($e); + $this->eventDispatcher->notifyEvent('exception', new Event($e)); + $errors[] = $customField->getId(); } } - $messages[] = __u('Registros no actualizados'); - $messages[] = implode(',', $errors); - $messages[] = __u('Registros actualizados'); - $messages[] = implode(',', $success); - - $this->eventDispatcher->notifyEvent('update.masterPassword.customFields.end', new Event($this, [__u('Actualizar Clave Maestra')])); - - return $messages; + $this->eventDispatcher->notifyEvent('update.masterPassword.customFields.end', + new Event($this, EventMessage::factory() + ->addDescription(__u('Actualizar Clave Maestra')) + ->addDetail(__u('Registros actualizados'), implode(',', $success)) + ->addDetail(__u('Registros no actualizados'), implode(',', $errors))) + ); } /** * Actualizar los datos encriptados con una nueva clave * * @param UpdateMasterPassRequest $request - * @return array * @throws ServiceException */ public function updateMasterPassword(UpdateMasterPassRequest $request) @@ -134,7 +138,7 @@ class CustomFieldCryptService extends Service try { $this->request = $request; - return $this->processUpdateMasterPassword(function (CustomFieldData $customFieldData) { + $this->processUpdateMasterPassword(function (CustomFieldData $customFieldData) { return Crypt::decrypt( $customFieldData->getData(), Crypt::unlockSecuredKey($customFieldData->getKey(), $this->request->getCurrentMasterPass()), @@ -143,7 +147,14 @@ class CustomFieldCryptService extends Service } catch (ServiceException $e) { throw $e; } catch (\Exception $e) { - throw new ServiceException(__u('Errores al actualizar datos de campos personalizados'), SPException::ERROR, null, $e->getCode(), $e); + $this->eventDispatcher->notifyEvent('exception', new Event($e)); + + throw new ServiceException( + __u('Errores al actualizar datos de campos personalizados'), + ServiceException::ERROR, + null, + $e->getCode(), + $e); } } diff --git a/lib/SP/Services/Export/XmlExportService.php b/lib/SP/Services/Export/XmlExportService.php index 6d0cbb47..1f70f808 100644 --- a/lib/SP/Services/Export/XmlExportService.php +++ b/lib/SP/Services/Export/XmlExportService.php @@ -134,7 +134,7 @@ class XmlExportService extends Service // Generar hash unico para evitar descargas no permitidas $exportUniqueHash = sha1(uniqid('sysPassExport', true)); $this->configData->setExportHash($exportUniqueHash); - $this->config->saveConfig(); + $this->config->saveConfig($this->configData); $this->exportFile = $this->exportDir . DIRECTORY_SEPARATOR . Util::getAppInfo('appname') . '-' . $exportUniqueHash . '.xml'; } diff --git a/lib/SP/Services/Import/CsvImport.php b/lib/SP/Services/Import/CsvImport.php index 3f45e831..2339c7df 100644 --- a/lib/SP/Services/Import/CsvImport.php +++ b/lib/SP/Services/Import/CsvImport.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -44,9 +44,8 @@ class CsvImport extends CsvImportBase implements ImportInterface public function doImport() { $this->eventDispatcher->notifyEvent('run.import.csv', - new Event($this, - EventMessage::factory() - ->addDescription(sprintf(__('Formato detectado: %s'), 'CSV'))) + new Event($this, EventMessage::factory() + ->addDescription(sprintf(__('Formato detectado: %s'), 'CSV'))) ); $this->fileImport->readFileToArray(); diff --git a/lib/SP/Services/Import/CsvImportBase.php b/lib/SP/Services/Import/CsvImportBase.php index f775710e..be265c91 100644 --- a/lib/SP/Services/Import/CsvImportBase.php +++ b/lib/SP/Services/Import/CsvImportBase.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -24,8 +24,8 @@ namespace SP\Services\Import; +use DI\Container; use SP\Account\AccountRequest; -use SP\Bootstrap; use SP\Core\Events\Event; use SP\Core\Events\EventDispatcher; use SP\Core\Events\EventMessage; @@ -63,21 +63,29 @@ abstract class CsvImportBase * @var EventDispatcher */ protected $eventDispatcher; + /** + * @var array + */ + protected $categories = []; + /** + * @var array + */ + protected $clients = []; /** * ImportBase constructor. * - * @param FileImport $fileImport + * @param Container $dic + * @param FileImport $fileImport * @param ImportParams $importParams - * @throws \Psr\Container\ContainerExceptionInterface - * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \DI\DependencyException + * @throws \DI\NotFoundException */ - public function __construct(FileImport $fileImport, ImportParams $importParams) + public function __construct(Container $dic, FileImport $fileImport, ImportParams $importParams) { $this->fileImport = $fileImport; $this->importParams = $importParams; - $dic = Bootstrap::getContainer(); $this->accountService = $dic->get(AccountService::class); $this->categoryService = $dic->get(CategoryService::class); $this->clientService = $dic->get(ClientService::class); @@ -105,7 +113,6 @@ abstract class CsvImportBase * Obtener los datos de las entradas de sysPass y crearlas * * @throws ImportException - * @throws \SP\Core\Exceptions\InvalidArgumentException */ protected function processAccounts() { @@ -130,31 +137,32 @@ abstract class CsvImportBase try { // Obtener los ids de cliente y categoría - $clientData = new ClientData(null, $clientName); - $this->addClient($clientData); - - $categoryData = new CategoryData(null, $categoryName); - $this->addCategory($categoryData); + $clientId = $this->addClient(new ClientData(null, $clientName)); + $categoryId = $this->addCategory(new CategoryData(null, $categoryName)); // Crear la nueva cuenta $accountRequest = new AccountRequest(); $accountRequest->name = $accountName; $accountRequest->login = $login; - $accountRequest->clientId = $clientData->getId(); - $accountRequest->categoryId = $categoryData->getId(); + $accountRequest->clientId = $clientId; + $accountRequest->categoryId = $categoryId; $accountRequest->notes = $notes; $accountRequest->url = $url; $accountRequest->pass = $password; $this->addAccount($accountRequest); + + $this->eventDispatcher->notifyEvent('run.import.csv.account', + new Event($this, EventMessage::factory() + ->addDetail(__('Cuenta importada'), $accountRequest->name)) + ); } catch (\Exception $e) { processException($e); $this->eventDispatcher->notifyEvent('exception', - new Event($e, - EventMessage::factory() - ->addDetail(__u('Error importando cuenta'), $accountName) - ->addDetail(__u('Error procesando línea'), $line)) + new Event($e, EventMessage::factory() + ->addDetail(__u('Error importando cuenta'), $accountName) + ->addDetail(__u('Error procesando línea'), $line)) ); } } diff --git a/lib/SP/Services/Import/ImportService.php b/lib/SP/Services/Import/ImportService.php index 6ade8840..a8df64a8 100644 --- a/lib/SP/Services/Import/ImportService.php +++ b/lib/SP/Services/Import/ImportService.php @@ -3,8 +3,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -49,7 +49,7 @@ class ImportService extends Service * Iniciar la importación de cuentas. * * @param ImportParams $importParams - * @param FileImport $fileImport + * @param FileImport $fileImport * @return int * @throws \Exception * @throws \Psr\Container\ContainerExceptionInterface @@ -60,8 +60,6 @@ class ImportService extends Service $this->importParams = $importParams; $this->fileImport = $fileImport; -// $LogMessage->setAction(__('Importar Cuentas', false)); - $import = $this->selectImportType(); $db = $this->dic->get(Database::class); @@ -85,10 +83,6 @@ class ImportService extends Service throw $e; } - - -// $LogMessage->addDescription(__('Importación finalizada', false)); -// $LogMessage->addDescription(__('Revise el registro de eventos para más detalles', false)); } /** @@ -102,10 +96,10 @@ class ImportService extends Service switch ($this->fileImport->getFileType()) { case 'text/csv': case 'application/vnd.ms-excel': - return new CsvImport($this->fileImport, $this->importParams); + return new CsvImport($this->dic, $this->fileImport, $this->importParams); break; case 'text/xml': - return new XmlImport(new XmlFileImport($this->fileImport), $this->importParams); + return new XmlImport($this->dic, new XmlFileImport($this->fileImport), $this->importParams); break; } diff --git a/lib/SP/Services/Import/ImportTrait.php b/lib/SP/Services/Import/ImportTrait.php index c5353fcb..614c1823 100644 --- a/lib/SP/Services/Import/ImportTrait.php +++ b/lib/SP/Services/Import/ImportTrait.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -31,6 +31,7 @@ use SP\Core\OldCrypt; use SP\DataModel\CategoryData; use SP\DataModel\ClientData; use SP\DataModel\TagData; +use SP\Repositories\DuplicatedItemException; use SP\Services\Account\AccountService; use SP\Services\Category\CategoryService; use SP\Services\Client\ClientService; @@ -75,6 +76,10 @@ trait ImportTrait * @var TagService */ private $tagService; + /** + * @var array + */ + private $items; /** * @return int @@ -91,17 +96,16 @@ trait ImportTrait * @throws ImportException * @throws SPException * @throws \Defuse\Crypto\Exception\CryptoException - * @throws \SP\Core\Dic\ContainerException * @throws \SP\Core\Exceptions\ConstraintException * @throws \SP\Core\Exceptions\QueryException */ protected function addAccount(AccountRequest $accountRequest) { - if ($accountRequest->categoryId === 0) { + if (empty($accountRequest->categoryId)) { throw new ImportException(__u('Id de categoría no definido. No es posible importar cuenta.')); } - if ($accountRequest->clientId === 0) { + if (empty($accountRequest->clientId)) { throw new ImportException(__u('Id de cliente no definido. No es posible importar cuenta.')); } @@ -122,7 +126,6 @@ trait ImportTrait $this->accountService->create($accountRequest); -// $this->LogMessage->addDetails(__('Cuenta creada', false), $accountRequest->name); $this->counter++; } @@ -132,10 +135,56 @@ trait ImportTrait * @param CategoryData $categoryData * @return int * @throws SPException + * @throws DuplicatedItemException */ protected function addCategory(CategoryData $categoryData) { - return $this->categoryService->create($categoryData); + try { + if ($categoryId = $this->getWorkingItem('category', $categoryData->getName()) === null) { + return $this->categoryService->create($categoryData); + } + + return $categoryId; + } catch (DuplicatedItemException $e) { + $itemData = $this->categoryService->getByName($categoryData->getName()); + + if (empty($itemData)) { + throw $e; + } + + return $this->addWorkingItem('category', $itemData->getName(), $itemData->getId()); + } + } + + /** + * @param string $type + * @param string $value + * @return int|null + */ + protected function getWorkingItem($type, $value) + { + if (!isset($this->items[$type][$value])) { + return null; + } + + return $this->items[$type][$value]; + } + + /** + * @param string $type + * @param string $value + * @param int $id + * @return int|bool + */ + protected function addWorkingItem($type, $value, $id) + { + if (isset($this->items[$type][$value])) { + return false; + } + + $this->items[$type][$value] = $id; + + return $id; } /** @@ -144,10 +193,25 @@ trait ImportTrait * @param ClientData $clientData * @return int * @throws SPException + * @throws DuplicatedItemException */ protected function addClient(ClientData $clientData) { - return $this->clientService->create($clientData); + try { + if ($clientId = $this->getWorkingItem('client', $clientData->getName()) === null) { + return $this->clientService->create($clientData); + } + + return $clientId; + } catch (DuplicatedItemException $e) { + $itemData = $this->clientService->getByName($clientData->getName()); + + if (empty($itemData)) { + throw $e; + } + + return $this->addWorkingItem('client', $itemData->getName(), $itemData->getId()); + } } /** diff --git a/lib/SP/Services/Import/KeepassImport.php b/lib/SP/Services/Import/KeepassImport.php index d24b7e3a..3b274d23 100644 --- a/lib/SP/Services/Import/KeepassImport.php +++ b/lib/SP/Services/Import/KeepassImport.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -61,20 +61,18 @@ class KeepassImport extends XmlImportBase implements ImportInterface { $clientId = $this->addClient(new ClientData(null, 'KeePass')); - $this->eventDispatcher->notifyEvent('run.import.keepass', - new Event($this, - EventMessage::factory() - ->addDetail(__('Cliente creado'), 'KeePass')) + $this->eventDispatcher->notifyEvent('run.import.keepass.client', + new Event($this, EventMessage::factory() + ->addDetail(__('Cliente creado'), 'KeePass')) ); foreach ($this->getItems() as $group => $entry) { try { $categoryId = $this->addCategory(new CategoryData(null, $group)); - $this->eventDispatcher->notifyEvent('run.import.keepass', - new Event($this, - EventMessage::factory() - ->addDetail(__('Categoría importada'), $group)) + $this->eventDispatcher->notifyEvent('run.import.keepass.category', + new Event($this, EventMessage::factory() + ->addDetail(__('Categoría importada'), $group)) ); if (count($entry) > 0) { @@ -90,7 +88,7 @@ class KeepassImport extends XmlImportBase implements ImportInterface $this->addAccount($accountRequest); - $this->eventDispatcher->notifyEvent('run.import.keepass', + $this->eventDispatcher->notifyEvent('run.import.keepass.account', new Event($this, EventMessage::factory() ->addDetail(__('Cuenta importada'), $accountRequest->name)) diff --git a/lib/SP/Services/Import/SyspassImport.php b/lib/SP/Services/Import/SyspassImport.php index 61530df4..e011a0f2 100644 --- a/lib/SP/Services/Import/SyspassImport.php +++ b/lib/SP/Services/Import/SyspassImport.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -44,25 +44,6 @@ defined('APP_ROOT') || die(); */ class SyspassImport extends XmlImportBase implements ImportInterface { - /** - * Mapeo de etiquetas - * - * @var array - */ - protected $tags = []; - /** - * Mapeo de categorías. - * - * @var array - */ - protected $categories = []; - /** - * Mapeo de clientes. - * - * @var array - */ - protected $clients = []; - /** * Iniciar la importación desde sysPass. * @@ -170,10 +151,9 @@ class SyspassImport extends XmlImportBase implements ImportInterface $nodeData->parentNode->removeChild($nodeData); } - $this->eventDispatcher->notifyEvent('run.import.syspass', - new Event($this, - EventMessage::factory() - ->addDescription(__('Datos desencriptados'))) + $this->eventDispatcher->notifyEvent('run.import.syspass.decryption', + new Event($this, EventMessage::factory() + ->addDescription(__('Datos desencriptados'))) ); } @@ -202,12 +182,11 @@ class SyspassImport extends XmlImportBase implements ImportInterface } try { - $this->categories[$category->getAttribute('id')] = $this->addCategory($categoryData); + $this->addWorkingItem('category', (int)$category->getAttribute('id'), $this->addCategory($categoryData)); - $this->eventDispatcher->notifyEvent('run.import.syspass', - new Event($this, - EventMessage::factory() - ->addDetail(__('Categoría importada'), $categoryData->getName())) + $this->eventDispatcher->notifyEvent('run.import.syspass.category', + new Event($this, EventMessage::factory() + ->addDetail(__('Categoría importada'), $categoryData->getName())) ); } catch (\Exception $e) { processException($e); @@ -240,12 +219,11 @@ class SyspassImport extends XmlImportBase implements ImportInterface } try { - $this->clients[$client->getAttribute('id')] = $this->addClient($clientData); + $this->addWorkingItem('client', (int)$client->getAttribute('id'), $this->addClient($clientData)); - $this->eventDispatcher->notifyEvent('run.import.syspass', - new Event($this, - EventMessage::factory() - ->addDetail(__('Cliente importado'), $clientData->getName())) + $this->eventDispatcher->notifyEvent('run.import.syspass.client', + new Event($this, EventMessage::factory() + ->addDetail(__('Cliente importado'), $clientData->getName())) ); } catch (\Exception $e) { processException($e); @@ -279,13 +257,11 @@ class SyspassImport extends XmlImportBase implements ImportInterface } try { + $this->addWorkingItem('client', (int)$client->getAttribute('id'), $this->addClient($clientData)); - $this->clients[$client->getAttribute('id')] = $this->addClient($clientData); - - $this->eventDispatcher->notifyEvent('run.import.syspass', - new Event($this, - EventMessage::factory() - ->addDetail(__('Cliente importado'), $clientData->getName())) + $this->eventDispatcher->notifyEvent('run.import.syspass.customer', + new Event($this, EventMessage::factory() + ->addDetail(__('Cliente importado'), $clientData->getName())) ); } catch (\Exception $e) { processException($e); @@ -315,12 +291,11 @@ class SyspassImport extends XmlImportBase implements ImportInterface } try { - $this->tags[$tag->getAttribute('id')] = $this->addTag($tagData); + $this->addWorkingItem('tag', (int)$tag->getAttribute('id'), $this->addTag($tagData)); - $this->eventDispatcher->notifyEvent('run.import.syspass', - new Event($this, - EventMessage::factory() - ->addDetail(__('Etiqueta importada'), $tagData->getName())) + $this->eventDispatcher->notifyEvent('run.import.syspass.tag', + new Event($this, EventMessage::factory() + ->addDetail(__('Etiqueta importada'), $tagData->getName())) ); } catch (\Exception $e) { processException($e); @@ -350,11 +325,11 @@ class SyspassImport extends XmlImportBase implements ImportInterface $accountRequest->login = $node->nodeValue; break; case 'categoryId'; - $accountRequest->categoryId = isset($this->categories[(int)$node->nodeValue]) ? $this->categories[(int)$node->nodeValue] : null; + $accountRequest->categoryId = $this->getWorkingItem('category', (int)$node->nodeValue); break; case 'clientId'; case 'customerId'; - $accountRequest->clientId = isset($this->clients[(int)$node->nodeValue]) ? $this->clients[(int)$node->nodeValue] : null; + $accountRequest->clientId = $this->getWorkingItem('client', (int)$node->nodeValue); break; case 'url'; $accountRequest->url = $node->nodeValue; @@ -377,10 +352,9 @@ class SyspassImport extends XmlImportBase implements ImportInterface try { $this->addAccount($accountRequest); - $this->eventDispatcher->notifyEvent('run.import.syspass', - new Event($this, - EventMessage::factory() - ->addDetail(__('Cuenta importada'), $accountRequest->name)) + $this->eventDispatcher->notifyEvent('run.import.syspass.account', + new Event($this, EventMessage::factory() + ->addDetail(__('Cuenta importada'), $accountRequest->name)) ); } catch (\Exception $e) { processException($e); @@ -402,7 +376,7 @@ class SyspassImport extends XmlImportBase implements ImportInterface /** @var \DOMElement $node */ foreach ($nodes as $node) { if (isset($node->tagName)) { - $tags[] = $node->getAttribute('id'); + $tags[] = $this->getWorkingItem('tag', (int)$node->getAttribute('id')); } } } diff --git a/lib/SP/Services/Import/XmlImport.php b/lib/SP/Services/Import/XmlImport.php index ed3b87dd..cd1cc32c 100644 --- a/lib/SP/Services/Import/XmlImport.php +++ b/lib/SP/Services/Import/XmlImport.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -24,6 +24,8 @@ namespace SP\Services\Import; +use DI\Container; + defined('APP_ROOT') || die(); /** @@ -42,17 +44,23 @@ class XmlImport implements ImportInterface * @var ImportParams */ protected $importParams; + /** + * @var Container + */ + private $dic; /** * XmlImport constructor. * + * @param Container $dic * @param XmlFileImport $xmlFileImport * @param ImportParams $importParams */ - public function __construct(XmlFileImport $xmlFileImport, ImportParams $importParams) + public function __construct(Container $dic, XmlFileImport $xmlFileImport, ImportParams $importParams) { $this->xmlFileImport = $xmlFileImport; $this->importParams = $importParams; + $this->dic = $dic; } /** @@ -82,11 +90,11 @@ class XmlImport implements ImportInterface { switch ($format) { case 'syspass': - return new SyspassImport($this->xmlFileImport, $this->importParams); + return new SyspassImport($this->dic, $this->xmlFileImport, $this->importParams); case 'keepass': - return new KeepassImport($this->xmlFileImport, $this->importParams); + return new KeepassImport($this->dic, $this->xmlFileImport, $this->importParams); case 'keepassx': - return new KeepassXImport($this->xmlFileImport, $this->importParams); + return new KeepassXImport($this->dic, $this->xmlFileImport, $this->importParams); } throw new ImportException(__u('Formato no detectado')); diff --git a/lib/SP/Services/Import/XmlImportBase.php b/lib/SP/Services/Import/XmlImportBase.php index 27fd2a22..3ba53abd 100644 --- a/lib/SP/Services/Import/XmlImportBase.php +++ b/lib/SP/Services/Import/XmlImportBase.php @@ -24,7 +24,7 @@ namespace SP\Services\Import; -use SP\Bootstrap; +use DI\Container; use SP\Core\Events\EventDispatcher; use SP\Services\Account\AccountService; use SP\Services\Category\CategoryService; @@ -56,18 +56,18 @@ abstract class XmlImportBase /** * ImportBase constructor. * + * @param Container $dic * @param XmlFileImport $xmlFileImport * @param ImportParams $importParams * @throws \Psr\Container\ContainerExceptionInterface * @throws \Psr\Container\NotFoundExceptionInterface */ - public function __construct(XmlFileImport $xmlFileImport, ImportParams $importParams) + public function __construct(Container $dic, XmlFileImport $xmlFileImport, ImportParams $importParams) { $this->xmlFileImport = $xmlFileImport; $this->importParams = $importParams; $this->xmlDOM = $xmlFileImport->getXmlDOM(); - $dic = Bootstrap::getContainer(); $this->accountService = $dic->get(AccountService::class); $this->categoryService = $dic->get(CategoryService::class); $this->clientService = $dic->get(ClientService::class); diff --git a/lib/SP/Services/Service.php b/lib/SP/Services/Service.php index f688270e..8a1a1c5c 100644 --- a/lib/SP/Services/Service.php +++ b/lib/SP/Services/Service.php @@ -59,17 +59,16 @@ abstract class Service /** * Service constructor. * - * @param Container $dic - * @param Config $config - * @param Session $session - * @param EventDispatcher $eventDispatcher + * @param Container $dic + * @throws \DI\DependencyException + * @throws \DI\NotFoundException */ - final public function __construct(Container $dic, Config $config, Session $session, EventDispatcher $eventDispatcher) + public function __construct(Container $dic) { $this->dic = $dic; - $this->config = $config; - $this->session = $session; - $this->eventDispatcher = $eventDispatcher; + $this->config = $dic->get(Config::class); + $this->session = $dic->get(Session::class); + $this->eventDispatcher = $dic->get(EventDispatcher::class); if (method_exists($this, 'initialize')) { $this->initialize(); diff --git a/lib/SP/Services/User/UserService.php b/lib/SP/Services/User/UserService.php index 8dd12b18..7277aba8 100644 --- a/lib/SP/Services/User/UserService.php +++ b/lib/SP/Services/User/UserService.php @@ -312,6 +312,17 @@ class UserService extends Service return $this->userRepository->getBasicInfo(); } + /** + * Obtener el email de los usuarios de un grupo + * + * @param $groupId + * @return array + */ + public function getUserEmailForGroup($groupId) + { + return $this->userRepository->getUserEmailForGroup($groupId); + } + /** * @throws \Psr\Container\ContainerExceptionInterface * @throws \Psr\Container\NotFoundExceptionInterface diff --git a/lib/SP/Storage/DBUtil.php b/lib/SP/Storage/DBUtil.php index 00d2943b..8d277afa 100644 --- a/lib/SP/Storage/DBUtil.php +++ b/lib/SP/Storage/DBUtil.php @@ -63,7 +63,7 @@ class DBUtil 'UserPassRecover', 'UserToUserGroup', 'Plugin', - 'Notice', + 'Notification', 'account_data_v', 'account_search_v' ]; diff --git a/lib/SP/Storage/Database.php b/lib/SP/Storage/Database.php index 2dc9d637..0470e5df 100644 --- a/lib/SP/Storage/Database.php +++ b/lib/SP/Storage/Database.php @@ -202,10 +202,11 @@ class Database implements DatabaseInterface return $stmt; } catch (\Exception $e) { - ob_start(); - debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); - debugLog('Exception: ' . $e->getMessage()); - debugLog(ob_get_clean()); + processException($e); +// ob_start(); +// debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); +// debugLog('Exception: ' . $e->getMessage()); +// debugLog(ob_get_clean()); throw new SPException($e->getMessage(), SPException::CRITICAL, $e->getCode(), 0, $e); } diff --git a/lib/SP/Storage/FileCache.php b/lib/SP/Storage/FileCache.php index b699a334..75beb6e2 100644 --- a/lib/SP/Storage/FileCache.php +++ b/lib/SP/Storage/FileCache.php @@ -24,8 +24,6 @@ namespace SP\Storage; -use RuntimeException; - /** * Class FileCache * @@ -37,16 +35,16 @@ class FileCache implements FileStorageInterface * @param string $path * * @return mixed - * @throws \RuntimeException + * @throws FileException */ public function load($path) { if (!file_exists($path)) { - throw new RuntimeException(sprintf(__('No es posible leer/escribir el archivo: %s'), $path)); + throw new FileException(sprintf(__('No es posible leer el archivo (%s)'), $path)); } if (!($data = file_get_contents($path))) { - throw new RuntimeException(sprintf(__('Error al leer datos del archivo: %s'), $path)); + throw new FileException(sprintf(__('Error al leer datos del archivo: %s'), $path)); } return unserialize($data); } @@ -55,21 +53,22 @@ class FileCache implements FileStorageInterface * @param string $path * @param mixed $data * @return FileStorageInterface + * @throws FileException */ public function save($path, $data) { $dir = dirname($path); if (!is_dir($dir) && mkdir($dir, 0700, true) === false) { - throw new RuntimeException(sprintf(__('No es posible crear el directorio: %s'), $dir)); + throw new FileException(sprintf(__('No es posible crear el directorio (%s)'), $dir)); } if (file_exists($path) && !is_writable($path)) { - throw new RuntimeException(sprintf(__('No es posible leer/escribir el archivo: %s'), $path)); + throw new FileException(sprintf(__('No es posible escribir en el archivo (%s)'), $path)); } if (!file_put_contents($path, serialize($data))) { - throw new RuntimeException(sprintf(__('Error al escribir datos en el archivo: %s'), $path)); + throw new FileException(sprintf(__('No es posible escribir en el archivo (%s)'), $path)); } return $this; @@ -79,15 +78,16 @@ class FileCache implements FileStorageInterface * @param string $path * * @return FileStorageInterface + * @throws FileException */ public function delete($path) { if (file_exists($path) && !is_writable($path)) { - throw new RuntimeException(sprintf(__('No es posible leer/escribir el archivo: %s'), $path)); + throw new FileException(sprintf(__('No es posible abrir el archivo (%s)'), $path)); } if (!unlink($path)) { - throw new RuntimeException(sprintf(__('Error al eliminar el archivo: %s'), $path)); + throw new FileException(sprintf(__('Error al eliminar el archivo (%s)'), $path)); } return $this; diff --git a/lib/SP/Storage/FileHandler.php b/lib/SP/Storage/FileHandler.php index 79ea57d0..19642fbf 100644 --- a/lib/SP/Storage/FileHandler.php +++ b/lib/SP/Storage/FileHandler.php @@ -60,7 +60,7 @@ class FileHandler } if (fwrite($this->handle, $data) === false) { - throw new FileException(sprintf(__u('No es posible escribir en el archivo (%s)'), $this->file)); + throw new FileException(sprintf(__('No es posible escribir en el archivo (%s)'), $this->file)); } return $this; @@ -74,7 +74,7 @@ class FileHandler public function open($mode) { if (($this->handle = fopen($this->file, $mode)) === false) { - throw new FileException(sprintf(__u('No es posible abrir el archivo (%s)'), $this->file)); + throw new FileException(sprintf(__('No es posible abrir el archivo (%s)'), $this->file)); } return $this->handle; @@ -86,7 +86,7 @@ class FileHandler public function close() { if (fclose($this->handle) === false) { - throw new FileException(sprintf(__u('No es posible cerrar el archivo (%s)'), $this->file)); + throw new FileException(sprintf(__('No es posible cerrar el archivo (%s)'), $this->file)); } return $this; diff --git a/lib/SP/Util/Checks.php b/lib/SP/Util/Checks.php index 19a9c3a1..a79d5be4 100644 --- a/lib/SP/Util/Checks.php +++ b/lib/SP/Util/Checks.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -25,7 +25,6 @@ namespace SP\Util; use Klein\Klein; -use SP\Http\Request; /** * Class Checks utilidades de comprobación @@ -169,10 +168,11 @@ class Checks /** * Comprobar si la petición es en formato JSON * + * @param Klein $router * @return bool */ - public static function isJson() + public static function isJson(Klein $router) { - return strpos(Request::getRequestHeaders('Accept'), 'application/json') === 0; + return $router->request()->headers()->get('Accept') === 'application/json'; } } diff --git a/lib/SP/Util/HttpUtil.php b/lib/SP/Util/HttpUtil.php index 28f3fe74..12e230fb 100644 --- a/lib/SP/Util/HttpUtil.php +++ b/lib/SP/Util/HttpUtil.php @@ -24,7 +24,6 @@ namespace SP\Util; -use SP\Bootstrap; use SP\Config\ConfigData; use SP\Http\Request; @@ -38,14 +37,11 @@ class HttpUtil /** * Comprobar y forzar (si es necesario) la conexión HTTPS * - * @throws \Psr\Container\ContainerExceptionInterface + * @param ConfigData $configData */ - public static function checkHttps() + public static function checkHttps(ConfigData $configData) { - /** @var ConfigData $ConfigData */ - $ConfigData = Bootstrap::getContainer()->get(ConfigData::class); - - if ($ConfigData->isHttpsEnabled() && !Checks::httpsEnabled()) { + if ($configData->isHttpsEnabled() && !Checks::httpsEnabled()) { $port = ((int)$_SERVER['SERVER_PORT'] !== 443) ? ':' . $_SERVER['SERVER_PORT'] : ''; $host = str_replace('http', 'https', self::getHttpHost()); diff --git a/lib/SP/Util/Util.php b/lib/SP/Util/Util.php index f907043c..60569b54 100644 --- a/lib/SP/Util/Util.php +++ b/lib/SP/Util/Util.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -27,9 +27,7 @@ namespace SP\Util; use Defuse\Crypto\Core; use Defuse\Crypto\Encoding; use SP\Bootstrap; -use SP\Config\Config; use SP\Config\ConfigData; -use SP\Config\ConfigDB; use SP\Core\Exceptions\SPException; use SP\Core\Init; use SP\Core\Install\Installer; @@ -701,37 +699,25 @@ class Util /** * Bloquear la aplicación * - * @param bool $setMaintenance + * @param int $userId + * @param string $subject + * @return bool */ - public static function lockApp($setMaintenance = true) + public static function lockApp($userId, $subject) { - ConfigDB::setValue('lock', SessionFactory::getUserData()->getId(), false); + $data = ['time' => time(), 'userId' => (int)$userId, 'subject' => $subject]; - /** @var Config $Config */ - $Config = Bootstrap::getContainer()['config']; - - if ($setMaintenance) { - $Config->getConfigData()->setMaintenance(true); - $Config->saveConfig(null, false); - } + return file_put_contents(LOCK_FILE, json_encode($data)); } /** * Desbloquear la aplicación * - * @param bool $unsetMaintenance + * @return bool */ - public static function unlockApp($unsetMaintenance = true) + public static function unlockApp() { - ConfigDB::setValue('lock', 0, false); - - /** @var Config $Config */ - $Config = Bootstrap::getContainer()['config']; - - if ($unsetMaintenance) { - $Config->getConfigData()->setMaintenance(false); - $Config->saveConfig(null, false); - } + return unlink(LOCK_FILE); } /** @@ -741,7 +727,13 @@ class Util */ public static function getAppLock() { - return (int)ConfigDB::getValue('lock', 0); + if (file_exists(LOCK_FILE) + && ($data = file_get_contents(LOCK_FILE)) !== false + ) { + return json_decode($data) ?: false; + } + + return false; } /**