mirror of
https://github.com/nuxsmin/sysPass.git
synced 2026-03-03 07:04:07 +01:00
chore: Use Aura.SqlQuery for building queries.
Refactor AccountRepository and AccountSearchRepository to use Aura.SqlQuery. This will allow to mock queries when testing. Signed-off-by: Rubén D <nuxsmin@syspass.org>
This commit is contained in:
@@ -25,16 +25,36 @@
|
||||
namespace SP\Modules\Api\Controllers\Account;
|
||||
|
||||
use Exception;
|
||||
use Klein\Klein;
|
||||
use SP\Core\Acl\Acl;
|
||||
use SP\Core\Acl\ActionsInterface;
|
||||
use SP\Domain\Account\Services\AccountSearchFilter;
|
||||
use SP\Core\Application;
|
||||
use SP\Domain\Account\AccountSearchServiceInterface;
|
||||
use SP\Domain\Account\Search\AccountSearchConstants;
|
||||
use SP\Domain\Account\Search\AccountSearchFilter;
|
||||
use SP\Domain\Api\ApiServiceInterface;
|
||||
use SP\Domain\Api\Services\ApiResponse;
|
||||
use SP\Mvc\Model\QueryCondition;
|
||||
use SP\Modules\Api\Controllers\ControllerBase;
|
||||
|
||||
/**
|
||||
* Class SearchController
|
||||
*/
|
||||
final class SearchController extends AccountBase
|
||||
final class SearchController extends ControllerBase
|
||||
{
|
||||
private AccountSearchServiceInterface $accountSearchService;
|
||||
|
||||
public function __construct(
|
||||
Application $application,
|
||||
Klein $router,
|
||||
ApiServiceInterface $apiService,
|
||||
Acl $acl,
|
||||
AccountSearchServiceInterface $accountSearchService
|
||||
) {
|
||||
parent::__construct($application, $router, $apiService, $acl);
|
||||
|
||||
$this->accountSearchService = $accountSearchService;
|
||||
}
|
||||
|
||||
/**
|
||||
* searchAction
|
||||
*/
|
||||
@@ -46,7 +66,9 @@ final class SearchController extends AccountBase
|
||||
$accountSearchFilter = $this->buildAccountSearchFilter();
|
||||
|
||||
$this->returnResponse(
|
||||
ApiResponse::makeSuccess($this->accountService->getByFilter($accountSearchFilter)->getDataAsArray())
|
||||
ApiResponse::makeSuccess(
|
||||
$this->accountSearchService->getByFilter($accountSearchFilter)->getDataAsArray()
|
||||
)
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
processException($e);
|
||||
@@ -56,40 +78,33 @@ final class SearchController extends AccountBase
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \SP\Domain\Account\Services\AccountSearchFilter
|
||||
* @return \SP\Domain\Account\Search\AccountSearchFilter
|
||||
* @throws \SP\Domain\Common\Services\ServiceException
|
||||
*/
|
||||
private function buildAccountSearchFilter(): AccountSearchFilter
|
||||
{
|
||||
$accountSearchFilter = new AccountSearchFilter();
|
||||
$accountSearchFilter->setCleanTxtSearch($this->apiService->getParamString('text'));
|
||||
$accountSearchFilter->setCategoryId($this->apiService->getParamInt('categoryId'));
|
||||
$accountSearchFilter->setClientId($this->apiService->getParamInt('clientId'));
|
||||
$filter = AccountSearchFilter::build($this->apiService->getParamString('text'))
|
||||
->setCategoryId($this->apiService->getParamInt('categoryId'))
|
||||
->setClientId($this->apiService->getParamInt('clientId'))
|
||||
->setTagsId(array_map('intval', $this->apiService->getParamArray('tagsId', false, [])))
|
||||
->setLimitCount($this->apiService->getParamInt('count', false, 50))
|
||||
->setSortOrder(
|
||||
$this->apiService->getParamInt('order', false, AccountSearchConstants::SORT_DEFAULT)
|
||||
);
|
||||
|
||||
$tagsId = array_map('intval', $this->apiService->getParamArray('tagsId', false, []));
|
||||
|
||||
if (count($tagsId) !== 0) {
|
||||
$accountSearchFilter->setTagsId($tagsId);
|
||||
}
|
||||
|
||||
$op = $this->apiService->getParamString('op');
|
||||
$op = $this->apiService->getParamString('op', false, AccountSearchConstants::FILTER_CHAIN_AND);
|
||||
|
||||
if ($op !== null) {
|
||||
switch ($op) {
|
||||
case 'and':
|
||||
$accountSearchFilter->setFilterOperator(QueryCondition::CONDITION_AND);
|
||||
case AccountSearchConstants::FILTER_CHAIN_AND:
|
||||
$filter->setFilterOperator(AccountSearchConstants::FILTER_CHAIN_AND);
|
||||
break;
|
||||
case 'or':
|
||||
$accountSearchFilter->setFilterOperator(QueryCondition::CONDITION_OR);
|
||||
case AccountSearchConstants::FILTER_CHAIN_OR:
|
||||
$filter->setFilterOperator(AccountSearchConstants::FILTER_CHAIN_OR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$accountSearchFilter->setLimitCount($this->apiService->getParamInt('count', false, 50));
|
||||
$accountSearchFilter->setSortOrder(
|
||||
$this->apiService->getParamInt('order', false, AccountSearchFilter::SORT_DEFAULT)
|
||||
);
|
||||
|
||||
return $accountSearchFilter;
|
||||
return $filter;
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ use SP\Core\Acl\ActionsInterface;
|
||||
use SP\Core\Application;
|
||||
use SP\Domain\Account\AccountSearchServiceInterface;
|
||||
use SP\Domain\Account\AccountServiceInterface;
|
||||
use SP\Domain\Account\Services\AccountSearchFilter;
|
||||
use SP\Domain\Account\Search\AccountSearchFilter;
|
||||
use SP\Html\DataGrid\DataGridInterface;
|
||||
use SP\Http\JsonResponse;
|
||||
use SP\Modules\Web\Controllers\ControllerBase;
|
||||
@@ -51,6 +51,10 @@ final class SearchController extends ControllerBase
|
||||
private AccountSearchServiceInterface $accountSearchService;
|
||||
private AccountGrid $accountGrid;
|
||||
|
||||
/**
|
||||
* @throws \SP\Core\Exceptions\SessionTimeout
|
||||
* @throws \SP\Domain\Auth\Services\AuthException
|
||||
*/
|
||||
public function __construct(
|
||||
Application $application,
|
||||
WebControllerHelper $webControllerHelper,
|
||||
@@ -67,7 +71,6 @@ final class SearchController extends ControllerBase
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws \JsonException
|
||||
* @throws \SP\Core\Exceptions\ConstraintException
|
||||
* @throws \SP\Core\Exceptions\QueryException
|
||||
* @throws \SP\Core\Exceptions\SPException
|
||||
@@ -100,19 +103,12 @@ final class SearchController extends ControllerBase
|
||||
{
|
||||
$itemSearchData = $this->getSearchData($this->configData->getAccountCount(), $this->request);
|
||||
|
||||
$filter = new AccountSearchFilter();
|
||||
$filter->setLimitCount($itemSearchData->getLimitCount());
|
||||
$filter->setLimitStart($itemSearchData->getLimitStart());
|
||||
|
||||
if (!empty($itemSearchData->getSeachString())) {
|
||||
$filter->setStringFilters(
|
||||
$this->accountSearchService->analyzeQueryFilters($itemSearchData->getSeachString())
|
||||
);
|
||||
$filter->setCleanTxtSearch($this->accountSearchService->getCleanString());
|
||||
}
|
||||
$filter = AccountSearchFilter::build($itemSearchData->getSeachString())
|
||||
->setLimitCount($itemSearchData->getLimitCount())
|
||||
->setLimitStart($itemSearchData->getLimitStart());
|
||||
|
||||
return $this->accountGrid->updatePager(
|
||||
$this->accountGrid->getGrid($this->accountService->getByFilter($filter)),
|
||||
$this->accountGrid->getGrid($this->accountSearchService->getByFilter($filter)),
|
||||
$itemSearchData
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* @author nuxsmin
|
||||
* @link https://syspass.org
|
||||
* @copyright 2012-2021, Rubén Domínguez nuxsmin@$syspass.org
|
||||
* @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org
|
||||
*
|
||||
* This file is part of sysPass.
|
||||
*
|
||||
@@ -35,7 +35,8 @@ use SP\Core\Exceptions\SPException;
|
||||
use SP\DataModel\ProfileData;
|
||||
use SP\DataModel\UserPreferencesData;
|
||||
use SP\Domain\Account\AccountSearchServiceInterface;
|
||||
use SP\Domain\Account\Services\AccountSearchFilter;
|
||||
use SP\Domain\Account\Search\AccountSearchConstants;
|
||||
use SP\Domain\Account\Search\AccountSearchFilter;
|
||||
use SP\Domain\Account\Services\AccountSearchItem;
|
||||
use SP\Domain\Category\CategoryServiceInterface;
|
||||
use SP\Domain\Client\ClientServiceInterface;
|
||||
@@ -178,7 +179,7 @@ final class AccountSearchHelper extends HelperBase
|
||||
}
|
||||
|
||||
$dataGrid = $this->getGrid();
|
||||
$dataGrid->getData()->setData($this->accountSearchService->processSearchResults($this->accountSearchFilter));
|
||||
$dataGrid->getData()->setData($this->accountSearchService->getByFilter($this->accountSearchFilter));
|
||||
$dataGrid->updatePager();
|
||||
$dataGrid->setTime(round(getElapsedTime($this->queryTimeStart), 5));
|
||||
|
||||
@@ -275,35 +276,35 @@ final class AccountSearchHelper extends HelperBase
|
||||
$gridSortCustomer = new DataGridSort();
|
||||
$gridSortCustomer->setName(__('Client'))
|
||||
->setTitle(__('Sort by Client'))
|
||||
->setSortKey(AccountSearchFilter::SORT_CLIENT)
|
||||
->setSortKey(AccountSearchConstants::SORT_CLIENT)
|
||||
->setIconUp($icons->getIconUp())
|
||||
->setIconDown($icons->getIconDown());
|
||||
|
||||
$gridSortName = new DataGridSort();
|
||||
$gridSortName->setName(__('Name'))
|
||||
->setTitle(__('Sort by Name'))
|
||||
->setSortKey(AccountSearchFilter::SORT_NAME)
|
||||
->setSortKey(AccountSearchConstants::SORT_NAME)
|
||||
->setIconUp($icons->getIconUp())
|
||||
->setIconDown($icons->getIconDown());
|
||||
|
||||
$gridSortCategory = new DataGridSort();
|
||||
$gridSortCategory->setName(__('Category'))
|
||||
->setTitle(__('Sort by Category'))
|
||||
->setSortKey(AccountSearchFilter::SORT_CATEGORY)
|
||||
->setSortKey(AccountSearchConstants::SORT_CATEGORY)
|
||||
->setIconUp($icons->getIconUp())
|
||||
->setIconDown($icons->getIconDown());
|
||||
|
||||
$gridSortLogin = new DataGridSort();
|
||||
$gridSortLogin->setName(__('User'))
|
||||
->setTitle(__('Sort by Username'))
|
||||
->setSortKey(AccountSearchFilter::SORT_LOGIN)
|
||||
->setSortKey(AccountSearchConstants::SORT_LOGIN)
|
||||
->setIconUp($icons->getIconUp())
|
||||
->setIconDown($icons->getIconDown());
|
||||
|
||||
$gridSortUrl = new DataGridSort();
|
||||
$gridSortUrl->setName(__('URL / IP'))
|
||||
->setTitle(__('Sort by URL / IP'))
|
||||
->setSortKey(AccountSearchFilter::SORT_URL)
|
||||
->setSortKey(AccountSearchConstants::SORT_URL)
|
||||
->setIconUp($icons->getIconUp())
|
||||
->setIconDown($icons->getIconDown());
|
||||
|
||||
@@ -350,7 +351,7 @@ final class AccountSearchHelper extends HelperBase
|
||||
/**
|
||||
* Set search filters
|
||||
*
|
||||
* @return AccountSearchFilter
|
||||
* @return \SP\Domain\Account\Search\AccountSearchFilter
|
||||
*/
|
||||
private function getFilters(): AccountSearchFilter
|
||||
{
|
||||
|
||||
@@ -41,7 +41,8 @@
|
||||
"league/fractal": "^0.19.2",
|
||||
"symfony/console": "^v5.1.2",
|
||||
"symfony/lock": "^v5.0",
|
||||
"ocramius/proxy-manager": "~2.0"
|
||||
"ocramius/proxy-manager": "~2.0",
|
||||
"aura/sqlquery": "~2.8"
|
||||
},
|
||||
"require-dev": {
|
||||
"roave/security-advisories": "dev-latest",
|
||||
|
||||
874
composer.lock
generated
874
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -27,7 +27,7 @@ namespace SP\Core\Context;
|
||||
use SP\Core\Crypt\Vault;
|
||||
use SP\DataModel\Dto\AccountCache;
|
||||
use SP\DataModel\ProfileData;
|
||||
use SP\Domain\Account\Services\AccountSearchFilter;
|
||||
use SP\Domain\Account\Search\AccountSearchFilter;
|
||||
use SP\Domain\User\Services\UserLoginResponse;
|
||||
|
||||
/**
|
||||
@@ -189,7 +189,7 @@ class SessionContext extends ContextBase
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AccountSearchFilter $searchFilters
|
||||
* @param \SP\Domain\Account\Search\AccountSearchFilter $searchFilters
|
||||
*/
|
||||
public function setSearchFilters(AccountSearchFilter $searchFilters): void
|
||||
{
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
namespace SP\Core\Definitions;
|
||||
|
||||
use Aura\SqlQuery\QueryFactory;
|
||||
use Monolog\Logger;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use Psr\Container\ContainerInterface;
|
||||
@@ -188,6 +189,8 @@ final class CoreDefinitions
|
||||
$c->get(NotificationHandler::class)
|
||||
);
|
||||
}),
|
||||
QueryFactory::class => create(QueryFactory::class)
|
||||
->constructor('mysql', QueryFactory::COMMON),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ use SP\DataModel\ItemData;
|
||||
use SP\DataModel\ItemSearchData;
|
||||
use SP\Domain\Account\Services\AccountPasswordRequest;
|
||||
use SP\Domain\Common\Services\ServiceException;
|
||||
use SP\Infrastructure\Common\Repositories\NoSuchItemException;
|
||||
use SP\Infrastructure\Database\QueryResult;
|
||||
|
||||
/**
|
||||
@@ -46,8 +47,7 @@ interface AccountHistoryServiceInterface
|
||||
/**
|
||||
* Returns the item for given id
|
||||
*
|
||||
* @throws SPException
|
||||
* @throws SPException
|
||||
* @throws NoSuchItemException
|
||||
*/
|
||||
public function getById(int $id): AccountHistoryData;
|
||||
|
||||
|
||||
@@ -28,9 +28,8 @@ namespace SP\Domain\Account;
|
||||
use SP\Core\Exceptions\ConstraintException;
|
||||
use SP\Core\Exceptions\QueryException;
|
||||
use SP\Core\Exceptions\SPException;
|
||||
use SP\Domain\Account\Services\AccountSearchFilter;
|
||||
use SP\Domain\Account\Search\AccountSearchFilter;
|
||||
use SP\Infrastructure\Database\QueryResult;
|
||||
use SP\Mvc\Model\QueryCondition;
|
||||
|
||||
/**
|
||||
* Class AccountSearchService para la gestión de búsquedas de cuentas
|
||||
@@ -45,13 +44,13 @@ interface AccountSearchServiceInterface
|
||||
* @throws QueryException
|
||||
* @throws SPException
|
||||
*/
|
||||
public function processSearchResults(AccountSearchFilter $accountSearchFilter): QueryResult;
|
||||
public function getByFilter(AccountSearchFilter $accountSearchFilter): QueryResult;
|
||||
|
||||
/**
|
||||
* Analizar la cadena de consulta por eqituetas especiales y devolver un objeto
|
||||
* QueryCondition con los filtros
|
||||
*/
|
||||
public function analyzeQueryFilters(string $string): QueryCondition;
|
||||
public function analyzeQueryFilters(string $string): void;
|
||||
|
||||
public function getCleanString(): ?string;
|
||||
}
|
||||
@@ -28,9 +28,10 @@ namespace SP\Domain\Account\In;
|
||||
use SP\Core\Exceptions\ConstraintException;
|
||||
use SP\Core\Exceptions\QueryException;
|
||||
use SP\Core\Exceptions\SPException;
|
||||
use SP\DataModel\AccountHistoryData;
|
||||
use SP\Domain\Account\Search\AccountSearchFilter;
|
||||
use SP\Domain\Account\Services\AccountPasswordRequest;
|
||||
use SP\Domain\Account\Services\AccountRequest;
|
||||
use SP\Domain\Account\Services\AccountSearchFilter;
|
||||
use SP\Domain\Common\In\RepositoryInterface;
|
||||
use SP\Domain\Common\Out\SimpleModel;
|
||||
use SP\Infrastructure\Database\QueryResult;
|
||||
@@ -53,22 +54,21 @@ interface AccountRepositoryInterface extends RepositoryInterface
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param QueryCondition $queryCondition
|
||||
*
|
||||
* @return QueryResult
|
||||
* @throws \SP\Core\Exceptions\ConstraintException
|
||||
* @throws \SP\Core\Exceptions\QueryException
|
||||
*/
|
||||
public function getPasswordForId(int $id, QueryCondition $queryCondition): QueryResult;
|
||||
public function getPasswordForId(int $id): QueryResult;
|
||||
|
||||
/**
|
||||
* @param QueryCondition $queryCondition
|
||||
* @param int $id
|
||||
*
|
||||
* @return QueryResult
|
||||
* @throws ConstraintException
|
||||
* @throws QueryException
|
||||
* @throws \SP\Core\Exceptions\ConstraintException
|
||||
* @throws \SP\Core\Exceptions\QueryException
|
||||
*/
|
||||
public function getPasswordHistoryForId(QueryCondition $queryCondition): QueryResult;
|
||||
public function getPasswordHistoryForId(int $id): QueryResult;
|
||||
|
||||
/**
|
||||
* Incrementa el contador de vista de clave de una cuenta en la BBDD
|
||||
@@ -106,14 +106,14 @@ interface AccountRepositoryInterface extends RepositoryInterface
|
||||
/**
|
||||
* Restaurar una cuenta desde el histórico.
|
||||
*
|
||||
* @param int $historyId El Id del registro en el histórico
|
||||
* @param \SP\DataModel\AccountHistoryData $accountHistoryData
|
||||
* @param int $userId User's Id
|
||||
*
|
||||
* @return bool
|
||||
* @throws ConstraintException
|
||||
* @throws QueryException
|
||||
* @throws \SP\Core\Exceptions\ConstraintException
|
||||
* @throws \SP\Core\Exceptions\QueryException
|
||||
*/
|
||||
public function editRestore(int $historyId, int $userId): bool;
|
||||
public function editRestore(AccountHistoryData $accountHistoryData, int $userId): bool;
|
||||
|
||||
/**
|
||||
* Updates an item for bulk action
|
||||
@@ -148,35 +148,22 @@ interface AccountRepositoryInterface extends RepositoryInterface
|
||||
public function getDataForLink(int $id): QueryResult;
|
||||
|
||||
/**
|
||||
* Obtener las cuentas de una búsqueda.
|
||||
*
|
||||
* @param AccountSearchFilter $accountSearchFilter
|
||||
* @param QueryCondition $queryFilterUser
|
||||
* @param int|null $accountId
|
||||
*
|
||||
* @return QueryResult
|
||||
* @throws ConstraintException
|
||||
* @throws QueryException
|
||||
* @throws SPException
|
||||
* @throws \SP\Core\Exceptions\ConstraintException
|
||||
* @throws \SP\Core\Exceptions\QueryException
|
||||
*/
|
||||
public function getByFilter(AccountSearchFilter $accountSearchFilter, QueryCondition $queryFilterUser): QueryResult;
|
||||
public function getForUser(?int $accountId = null): QueryResult;
|
||||
|
||||
/**
|
||||
* @param QueryCondition $queryFilter
|
||||
* @param int $accountId
|
||||
*
|
||||
* @return QueryResult
|
||||
* @throws ConstraintException
|
||||
* @throws QueryException
|
||||
* @throws \SP\Core\Exceptions\ConstraintException
|
||||
* @throws \SP\Core\Exceptions\QueryException
|
||||
*/
|
||||
public function getForUser(QueryCondition $queryFilter): QueryResult;
|
||||
|
||||
/**
|
||||
* @param QueryCondition $queryFilter
|
||||
*
|
||||
* @return QueryResult
|
||||
* @throws ConstraintException
|
||||
* @throws QueryException
|
||||
*/
|
||||
public function getLinked(QueryCondition $queryFilter): QueryResult;
|
||||
public function getLinked(int $accountId): QueryResult;
|
||||
|
||||
/**
|
||||
* Obtener los datos relativos a la clave de todas las cuentas.
|
||||
|
||||
56
lib/SP/Domain/Account/Search/AccountSearchConstants.php
Normal file
56
lib/SP/Domain/Account/Search/AccountSearchConstants.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/*
|
||||
* sysPass
|
||||
*
|
||||
* @author nuxsmin
|
||||
* @link https://syspass.org
|
||||
* @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org
|
||||
*
|
||||
* This file is part of sysPass.
|
||||
*
|
||||
* sysPass is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sysPass is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace SP\Domain\Account\Search;
|
||||
|
||||
/**
|
||||
* Interface AccountSearchConstants
|
||||
*/
|
||||
interface AccountSearchConstants
|
||||
{
|
||||
public const FILTER_OWNER = 'owner';
|
||||
public const FILTER_MAIN_GROUP = 'mainGroup';
|
||||
public const FILTER_CATEGORY_NAME = 'categoryName';
|
||||
public const FILTER_FILE_NAME = 'fileName';
|
||||
public const FILTER_ACCOUNT_ID = 'accountId';
|
||||
public const FILTER_USER_NAME = 'userName';
|
||||
public const FILTER_ACCOUNT_NAME_REGEX = 'accountNameRegex';
|
||||
public const FILTER_CLIENT_NAME = 'clientName';
|
||||
public const FILTER_GROUP_NAME = 'groupName';
|
||||
public const FILTER_CHAIN_AND = 'and';
|
||||
public const FILTER_CHAIN_OR = 'or';
|
||||
public const FILTER_IS_PRIVATE = 'is:private';
|
||||
public const FILTER_NOT_PRIVATE = 'not:private';
|
||||
public const FILTER_IS_EXPIRED = 'is:expired';
|
||||
public const FILTER_NOT_EXPIRED = 'is:expired';
|
||||
|
||||
public const SORT_DIR_ASC = 0;
|
||||
public const SORT_DIR_DESC = 1;
|
||||
public const SORT_CATEGORY = 2;
|
||||
public const SORT_DEFAULT = 0;
|
||||
public const SORT_LOGIN = 3;
|
||||
public const SORT_URL = 4;
|
||||
public const SORT_NAME = 1;
|
||||
public const SORT_CLIENT = 5;
|
||||
}
|
||||
@@ -22,7 +22,7 @@
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace SP\Domain\Account\Services;
|
||||
namespace SP\Domain\Account\Search;
|
||||
|
||||
use SP\Mvc\Model\QueryCondition;
|
||||
|
||||
@@ -30,21 +30,19 @@ use SP\Mvc\Model\QueryCondition;
|
||||
/**
|
||||
* Class AccountSearchFilter
|
||||
*
|
||||
* @package SP\Account
|
||||
* @package SP\Domain\Account\Filters
|
||||
*/
|
||||
final class AccountSearchFilter
|
||||
{
|
||||
/**
|
||||
* Constantes de ordenación
|
||||
* @param string $txtSearch
|
||||
*
|
||||
* @return \SP\Domain\Account\Search\AccountSearchFilter
|
||||
*/
|
||||
public const SORT_DIR_ASC = 0;
|
||||
public const SORT_DIR_DESC = 1;
|
||||
public const SORT_LOGIN = 3;
|
||||
public const SORT_URL = 4;
|
||||
public const SORT_CATEGORY = 2;
|
||||
public const SORT_CLIENT = 5;
|
||||
public const SORT_NAME = 1;
|
||||
public const SORT_DEFAULT = 0;
|
||||
public static function build(string $txtSearch): AccountSearchFilter
|
||||
{
|
||||
return (new self())->setTxtSearch($txtSearch);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var int|null El número de registros de la última consulta
|
||||
@@ -55,18 +53,17 @@ final class AccountSearchFilter
|
||||
/**
|
||||
* @var string|null Search string without special filters
|
||||
*/
|
||||
private ?string $cleanTxtSearch = null;
|
||||
private ?int $clientId = null;
|
||||
private ?int $categoryId = null;
|
||||
private ?array $tagsId = null;
|
||||
private int $sortOrder = self::SORT_DEFAULT;
|
||||
private int $sortKey = self::SORT_DIR_ASC;
|
||||
private int $limitStart = 0;
|
||||
private ?int $limitCount = null;
|
||||
private ?bool $sortViews = null;
|
||||
private bool $searchFavorites = false;
|
||||
private ?QueryCondition $stringFilters = null;
|
||||
private ?string $filterOperator = null;
|
||||
private ?string $cleanTxtSearch = null;
|
||||
private ?int $clientId = null;
|
||||
private ?int $categoryId = null;
|
||||
private ?array $tagsId = null;
|
||||
private int $sortOrder = AccountSearchConstants::SORT_DEFAULT;
|
||||
private int $sortKey = AccountSearchConstants::SORT_DIR_ASC;
|
||||
private int $limitStart = 0;
|
||||
private ?int $limitCount = null;
|
||||
private ?bool $sortViews = null;
|
||||
private bool $searchFavorites = false;
|
||||
private ?string $filterOperator = null;
|
||||
|
||||
public function isSearchFavorites(): bool
|
||||
{
|
||||
@@ -181,57 +178,6 @@ final class AccountSearchFilter
|
||||
return null !== $this->tagsId && count($this->tagsId) !== 0;
|
||||
}
|
||||
|
||||
public function getStringFilters(): QueryCondition
|
||||
{
|
||||
return $this->stringFilters ?? new QueryCondition();
|
||||
}
|
||||
|
||||
public function setStringFilters(?QueryCondition $stringFilters): void
|
||||
{
|
||||
$this->stringFilters = $stringFilters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve la cadena de ordenación de la consulta
|
||||
*/
|
||||
public function getOrderString(): string
|
||||
{
|
||||
switch ($this->sortKey) {
|
||||
case self::SORT_NAME:
|
||||
$orderKey[] = 'Account.name';
|
||||
break;
|
||||
case self::SORT_CATEGORY:
|
||||
$orderKey[] = 'Account.categoryName';
|
||||
break;
|
||||
case self::SORT_LOGIN:
|
||||
$orderKey[] = 'Account.login';
|
||||
break;
|
||||
case self::SORT_URL:
|
||||
$orderKey[] = 'Account.url';
|
||||
break;
|
||||
case self::SORT_CLIENT:
|
||||
$orderKey[] = 'Account.clientName';
|
||||
break;
|
||||
case self::SORT_DEFAULT:
|
||||
default:
|
||||
$orderKey[] = 'Account.clientName, Account.name';
|
||||
break;
|
||||
}
|
||||
|
||||
if ($this->isSortViews() && !$this->getSortKey()) {
|
||||
array_unshift($orderKey, 'Account.countView DESC');
|
||||
$this->setSortOrder(self::SORT_DIR_DESC);
|
||||
}
|
||||
|
||||
$orderDir = $this->sortOrder === self::SORT_DIR_ASC ? 'ASC' : 'DESC';
|
||||
|
||||
return sprintf(
|
||||
'%s %s',
|
||||
implode(',', $orderKey),
|
||||
$orderDir
|
||||
);
|
||||
}
|
||||
|
||||
public function isSortViews(): bool
|
||||
{
|
||||
return $this->sortViews ?? false;
|
||||
@@ -292,7 +238,7 @@ final class AccountSearchFilter
|
||||
$this->limitCount = null;
|
||||
$this->sortViews = null;
|
||||
$this->searchFavorites = false;
|
||||
$this->sortOrder = self::SORT_DEFAULT;
|
||||
$this->sortKey = self::SORT_DIR_ASC;
|
||||
$this->sortOrder = AccountSearchConstants::SORT_DEFAULT;
|
||||
$this->sortKey = AccountSearchConstants::SORT_DIR_ASC;
|
||||
}
|
||||
}
|
||||
148
lib/SP/Domain/Account/Search/AccountSearchTokenizer.php
Normal file
148
lib/SP/Domain/Account/Search/AccountSearchTokenizer.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
/*
|
||||
* sysPass
|
||||
*
|
||||
* @author nuxsmin
|
||||
* @link https://syspass.org
|
||||
* @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org
|
||||
*
|
||||
* This file is part of sysPass.
|
||||
*
|
||||
* sysPass is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sysPass is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace SP\Domain\Account\Search;
|
||||
|
||||
use SP\Util\Filter;
|
||||
|
||||
/**
|
||||
* Class AccountSearchTokenizer
|
||||
*/
|
||||
final class AccountSearchTokenizer
|
||||
{
|
||||
private const SEARCH_REGEX = /** @lang RegExp */
|
||||
'/(?<search>(?<!:)\b[^:]+\b(?!:))|(?<filter_subject>[a-zа-я_]+):(?!\s]*)"?(?<filter_condition>[^":]+)"?/u';
|
||||
|
||||
private const FILTERS = [
|
||||
'condition' => [
|
||||
'subject' => ['is', 'not'],
|
||||
'condition' => ['expired', 'private'],
|
||||
],
|
||||
'items' => [
|
||||
'subject' => [
|
||||
'id' => AccountSearchConstants::FILTER_ACCOUNT_ID,
|
||||
'user' => AccountSearchConstants::FILTER_USER_NAME,
|
||||
'group' => AccountSearchConstants::FILTER_GROUP_NAME,
|
||||
'file' => AccountSearchConstants::FILTER_FILE_NAME,
|
||||
'owner' => AccountSearchConstants::FILTER_OWNER,
|
||||
'maingroup' => AccountSearchConstants::FILTER_MAIN_GROUP,
|
||||
'client' => AccountSearchConstants::FILTER_CLIENT_NAME,
|
||||
'category' => AccountSearchConstants::FILTER_CATEGORY_NAME,
|
||||
'name_regex' => AccountSearchConstants::FILTER_ACCOUNT_NAME_REGEX,
|
||||
],
|
||||
'condition' => null,
|
||||
],
|
||||
'operator' => [
|
||||
'subject' => ['op'],
|
||||
'condition' => [AccountSearchConstants::FILTER_CHAIN_AND, AccountSearchConstants::FILTER_CHAIN_OR],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* @param string $search
|
||||
*
|
||||
* @return AccountSearchTokens|null
|
||||
*/
|
||||
public function tokenizeFrom(string $search): ?AccountSearchTokens
|
||||
{
|
||||
$match = preg_match_all(self::SEARCH_REGEX, $search, $filters);
|
||||
|
||||
if (empty($match)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$filtersAndValues = array_filter(array_combine($filters['filter_subject'], $filters['filter_condition']));
|
||||
|
||||
return new AccountSearchTokens(
|
||||
Filter::safeSearchString(trim($filters['search'][0] ?? '')),
|
||||
$this->getConditions($filtersAndValues),
|
||||
$this->getItems($filtersAndValues),
|
||||
$this->getOperator($filtersAndValues)[0],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $filters
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getConditions(array $filters): array
|
||||
{
|
||||
return array_filter(
|
||||
array_map(
|
||||
static function ($subject, $condition) {
|
||||
if (in_array($subject, self::FILTERS['condition']['subject'], true)
|
||||
&& in_array($condition, self::FILTERS['condition']['condition'], true)
|
||||
) {
|
||||
return sprintf("%s:%s", $subject, $condition);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
$filters['filter_subject'],
|
||||
$filters['filter_condition']
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $filtersAndValues
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getItems(array $filtersAndValues): array
|
||||
{
|
||||
$items = array_filter(
|
||||
$filtersAndValues,
|
||||
static function ($value, $key) {
|
||||
return in_array($key, array_keys(self::FILTERS['items']['subject']), true) && !empty($value);
|
||||
},
|
||||
ARRAY_FILTER_USE_BOTH
|
||||
);
|
||||
|
||||
return array_combine(
|
||||
array_map(function ($key) {
|
||||
return self::FILTERS['items']['subject'][$key];
|
||||
}, array_keys($items)),
|
||||
array_values($items)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $filtersAndValues
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getOperator(array $filtersAndValues): array
|
||||
{
|
||||
return array_filter(
|
||||
$filtersAndValues,
|
||||
static function ($value, $key) {
|
||||
return in_array($key, self::FILTERS['operator']['subject'], true)
|
||||
&& in_array($value, self::FILTERS['operator']['condition'], true);
|
||||
},
|
||||
ARRAY_FILTER_USE_BOTH
|
||||
);
|
||||
}
|
||||
}
|
||||
70
lib/SP/Domain/Account/Search/AccountSearchTokens.php
Normal file
70
lib/SP/Domain/Account/Search/AccountSearchTokens.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/*
|
||||
* sysPass
|
||||
*
|
||||
* @author nuxsmin
|
||||
* @link https://syspass.org
|
||||
* @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org
|
||||
*
|
||||
* This file is part of sysPass.
|
||||
*
|
||||
* sysPass is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sysPass is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace SP\Domain\Account\Search;
|
||||
|
||||
/**
|
||||
* Class AccountSearchTokens
|
||||
*/
|
||||
final class AccountSearchTokens
|
||||
{
|
||||
private string $search;
|
||||
private array $conditions;
|
||||
private array $items;
|
||||
private string $operator;
|
||||
|
||||
/**
|
||||
* @param string $search
|
||||
* @param array $conditions
|
||||
* @param array $items
|
||||
* @param string $operator
|
||||
*/
|
||||
public function __construct(string $search, array $conditions, array $items, string $operator)
|
||||
{
|
||||
$this->search = $search;
|
||||
$this->conditions = $conditions;
|
||||
$this->items = $items;
|
||||
$this->operator = $operator;
|
||||
}
|
||||
|
||||
public function getConditions(): array
|
||||
{
|
||||
return $this->conditions;
|
||||
}
|
||||
|
||||
public function getItems(): array
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
public function getOperator(): string
|
||||
{
|
||||
return $this->operator;
|
||||
}
|
||||
|
||||
public function getSearch(): string
|
||||
{
|
||||
return $this->search;
|
||||
}
|
||||
}
|
||||
@@ -24,128 +24,137 @@
|
||||
|
||||
namespace SP\Domain\Account\Services;
|
||||
|
||||
use Aura\SqlQuery\Common\SelectInterface;
|
||||
use Aura\SqlQuery\QueryFactory;
|
||||
use SP\Core\Context\ContextInterface;
|
||||
use SP\DataModel\ProfileData;
|
||||
use SP\Domain\Account\Search\AccountSearchConstants;
|
||||
use SP\Domain\Config\In\ConfigDataInterface;
|
||||
use SP\Domain\User\Services\UserLoginResponse;
|
||||
use SP\Mvc\Model\QueryCondition;
|
||||
|
||||
defined('APP_ROOT') || die();
|
||||
|
||||
/**
|
||||
* Class AccountUtil con utilidades para la gestión de cuentas
|
||||
* Class AccountFilterUser
|
||||
*/
|
||||
final class AccountFilterUser
|
||||
{
|
||||
private ConfigDataInterface $configData;
|
||||
private ContextInterface $context;
|
||||
private ?ProfileData $userProfile = null;
|
||||
private ?UserLoginResponse $userData = null;
|
||||
private QueryFactory $queryFactory;
|
||||
|
||||
public function __construct(
|
||||
ContextInterface $context,
|
||||
ConfigDataInterface $configData
|
||||
ConfigDataInterface $configData,
|
||||
QueryFactory $queryFactory
|
||||
) {
|
||||
$this->context = $context;
|
||||
$this->configData = $configData;
|
||||
$this->queryFactory = $queryFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve el filtro para la consulta SQL de cuentas que un usuario puede acceder
|
||||
*/
|
||||
public function getFilterHistory(bool $useGlobalSearch = false): QueryCondition
|
||||
public function buildFilterHistory(bool $useGlobalSearch = false, ?SelectInterface $query = null): SelectInterface
|
||||
{
|
||||
$this->setUp();
|
||||
$userData = $this->context->getUserData();
|
||||
$userProfile = $this->context->getUserProfile();
|
||||
|
||||
$queryFilter = new QueryCondition();
|
||||
if ($query === null) {
|
||||
$query = $this->queryFactory->newSelect()->from('AccountHistory');
|
||||
}
|
||||
|
||||
if (!$this->userData->getIsAdminApp()
|
||||
&& !$this->userData->getIsAdminAcc()
|
||||
&& !($this->configData->isGlobalSearch() && $useGlobalSearch && $this->userProfile->isAccGlobalSearch())
|
||||
) {
|
||||
// Filtro usuario y grupo
|
||||
$filter = '(AccountHistory.userId = ?
|
||||
OR AccountHistory.userGroupId = ?
|
||||
OR AccountHistory.accountId IN (SELECT accountId AS accountId FROM AccountToUser WHERE accountId = AccountHistory.accountId AND userId = ? UNION ALL SELECT accountId FROM AccountToUserGroup WHERE accountId = AccountHistory.accountId AND userGroupId = ?)
|
||||
OR AccountHistory.userGroupId IN (SELECT userGroupId FROM UserToUserGroup WHERE userGroupId = AccountHistory.userGroupId AND userId = ?))';
|
||||
|
||||
$params = [
|
||||
$this->userData->getId(),
|
||||
$this->userData->getUserGroupId(),
|
||||
$this->userData->getId(),
|
||||
$this->userData->getUserGroupId(),
|
||||
$this->userData->getId(),
|
||||
if ($this->isFilterByAdminAndGlobalSearch($userData, $useGlobalSearch, $userProfile)) {
|
||||
$where = [
|
||||
'AccountHistory.userId = :userId',
|
||||
'AccountHistory.userGroupId = :userGroupId',
|
||||
'AccountHistory.accountId IN (SELECT accountId AS accountId FROM AccountToUser WHERE accountId = AccountHistory.accountId AND userId = :userId UNION ALL SELECT accountId FROM AccountToUserGroup WHERE accountId = AccountHistory.accountId AND userGroupId = :userGroupId',
|
||||
'AccountHistory.userGroupId IN (SELECT userGroupId FROM UserToUserGroup WHERE userGroupId = AccountHistory.userGroupId AND userId = :userId)',
|
||||
];
|
||||
|
||||
if ($this->configData->isAccountFullGroupAccess()) {
|
||||
// Filtro de grupos secundarios en grupos que incluyen al usuario
|
||||
$filter .= PHP_EOL
|
||||
.'OR AccountHistory.accountId = (SELECT accountId FROM AccountToUserGroup aug INNER JOIN UserToUserGroup uug ON uug.userGroupId = aug.userGroupId WHERE aug.accountId = AccountHistory.accountId AND uug.userId = ? LIMIT 1)';
|
||||
$params[] = $this->userData->getId();
|
||||
$where[] =
|
||||
'AccountHistory.accountId = (SELECT accountId FROM AccountToUserGroup aug INNER JOIN UserToUserGroup uug ON uug.userGroupId = aug.userGroupId WHERE aug.accountId = AccountHistory.accountId AND uug.userId = :userId LIMIT 1)';
|
||||
}
|
||||
|
||||
$queryFilter->addFilter($filter, $params);
|
||||
$query->where(sprintf('(%s)', join(sprintf(' %s ', AccountSearchConstants::FILTER_CHAIN_OR), $where)));
|
||||
}
|
||||
|
||||
$queryFilter->addFilter(
|
||||
'(AccountHistory.isPrivate IS NULL OR AccountHistory.isPrivate = 0 OR (AccountHistory.isPrivate = 1 AND AccountHistory.userId = ?)) AND (AccountHistory.isPrivateGroup IS NULL OR AccountHistory.isPrivateGroup = 0 OR (AccountHistory.isPrivateGroup = 1 AND AccountHistory.userGroupId = ?))',
|
||||
[$this->userData->getId(), $this->userData->getUserGroupId()]
|
||||
$query->where(
|
||||
'(AccountHistory.isPrivate IS NULL OR AccountHistory.isPrivate = 0 OR (AccountHistory.isPrivate = 1 AND AccountHistory.userId = :userId))'
|
||||
);
|
||||
$query->where(
|
||||
'(AccountHistory.isPrivateGroup IS NULL OR AccountHistory.isPrivateGroup = 0 OR (AccountHistory.isPrivateGroup = 1 AND AccountHistory.userGroupId = :userGroupId))'
|
||||
);
|
||||
|
||||
return $queryFilter;
|
||||
}
|
||||
$query->bindValues([
|
||||
'userId' => $userData->getId(),
|
||||
'userGroupId' => $userData->getUserGroupId(),
|
||||
]);
|
||||
|
||||
/**
|
||||
* setUp
|
||||
*/
|
||||
private function setUp(): void
|
||||
{
|
||||
$this->userData = $this->context->getUserData();
|
||||
$this->userProfile = $this->context->getUserProfile();
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve el filtro para la consulta SQL de cuentas que un usuario puede acceder
|
||||
*/
|
||||
public function getFilter(bool $useGlobalSearch = false): QueryCondition
|
||||
public function buildFilter(bool $useGlobalSearch = false, ?SelectInterface $query = null): SelectInterface
|
||||
{
|
||||
$this->setUp();
|
||||
$userData = $this->context->getUserData();
|
||||
$userProfile = $this->context->getUserProfile();
|
||||
|
||||
$queryFilter = new QueryCondition();
|
||||
if ($query === null) {
|
||||
$query = $this->queryFactory->newSelect()->from('Account');
|
||||
}
|
||||
|
||||
if (!$this->userData->getIsAdminApp()
|
||||
&& !$this->userData->getIsAdminAcc()
|
||||
&& !($this->configData->isGlobalSearch() && $useGlobalSearch && $this->userProfile->isAccGlobalSearch())
|
||||
) {
|
||||
// Filtro usuario y grupo
|
||||
$filter = '(Account.userId = ?
|
||||
OR Account.userGroupId = ?
|
||||
OR Account.id IN (SELECT accountId AS accountId FROM AccountToUser WHERE accountId = Account.id AND userId = ? UNION ALL SELECT accountId FROM AccountToUserGroup WHERE accountId = Account.id AND userGroupId = ?)
|
||||
OR Account.userGroupId IN (SELECT userGroupId FROM UserToUserGroup WHERE userGroupId = Account.userGroupId AND userId = ?))';
|
||||
|
||||
$params = [
|
||||
$this->userData->getId(),
|
||||
$this->userData->getUserGroupId(),
|
||||
$this->userData->getId(),
|
||||
$this->userData->getUserGroupId(),
|
||||
$this->userData->getId(),
|
||||
if ($this->isFilterByAdminAndGlobalSearch($userData, $useGlobalSearch, $userProfile)) {
|
||||
$where = [
|
||||
'Account.userId = :userId',
|
||||
'Account.userGroupId = :userGroupId',
|
||||
'Account.id IN (SELECT accountId AS accountId FROM AccountToUser WHERE accountId = Account.id AND userId = :userId UNION ALL SELECT accountId FROM AccountToUserGroup WHERE accountId = Account.id AND userGroupId = :userGroupId)',
|
||||
'Account.userGroupId IN (SELECT userGroupId FROM UserToUserGroup WHERE userGroupId = Account.userGroupId AND userId = :userId)',
|
||||
];
|
||||
|
||||
if ($this->configData->isAccountFullGroupAccess()) {
|
||||
// Filtro de grupos secundarios en grupos que incluyen al usuario
|
||||
$filter .= PHP_EOL
|
||||
.'OR Account.id = (SELECT accountId FROM AccountToUserGroup aug INNER JOIN UserToUserGroup uug ON uug.userGroupId = aug.userGroupId WHERE aug.accountId = Account.id AND uug.userId = ? LIMIT 1)';
|
||||
$params[] = $this->userData->getId();
|
||||
$where[] =
|
||||
'Account.id = (SELECT accountId FROM AccountToUserGroup aug INNER JOIN UserToUserGroup uug ON uug.userGroupId = aug.userGroupId WHERE aug.accountId = Account.id AND uug.userId = :userId LIMIT 1)';
|
||||
}
|
||||
|
||||
$queryFilter->addFilter($filter, $params);
|
||||
$query->where(sprintf('(%s)', join(sprintf(' %s ', AccountSearchConstants::FILTER_CHAIN_OR), $where)));
|
||||
}
|
||||
|
||||
$queryFilter->addFilter(
|
||||
'(Account.isPrivate IS NULL OR Account.isPrivate = 0 OR (Account.isPrivate = 1 AND Account.userId = ?)) AND (Account.isPrivateGroup IS NULL OR Account.isPrivateGroup = 0 OR (Account.isPrivateGroup = 1 AND Account.userGroupId = ?))',
|
||||
[$this->userData->getId(), $this->userData->getUserGroupId()]
|
||||
$query->where(
|
||||
'Account.isPrivate IS NULL OR Account.isPrivate = 0 OR (Account.isPrivate = 1 AND Account.userId = :userId)'
|
||||
);
|
||||
$query->where(
|
||||
'Account.isPrivateGroup IS NULL OR Account.isPrivateGroup = 0 OR (Account.isPrivateGroup = 1 AND Account.userGroupId = :userGroupId)'
|
||||
);
|
||||
|
||||
return $queryFilter;
|
||||
$query->bindValues([
|
||||
'userId' => $userData->getId(),
|
||||
'userGroupId' => $userData->getUserGroupId(),
|
||||
]);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \SP\Domain\User\Services\UserLoginResponse $userData
|
||||
* @param bool $useGlobalSearch
|
||||
* @param \SP\DataModel\ProfileData|null $userProfile
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isFilterByAdminAndGlobalSearch(
|
||||
UserLoginResponse $userData,
|
||||
bool $useGlobalSearch,
|
||||
?ProfileData $userProfile
|
||||
): bool {
|
||||
return !$userData->getIsAdminApp()
|
||||
&& !$userData->getIsAdminAcc()
|
||||
&& !($this->configData->isGlobalSearch() && $useGlobalSearch && $userProfile->isAccGlobalSearch());
|
||||
}
|
||||
}
|
||||
@@ -68,8 +68,7 @@ final class AccountHistoryService extends Service implements AccountHistoryServi
|
||||
/**
|
||||
* Returns the item for given id
|
||||
*
|
||||
* @throws SPException
|
||||
* @throws SPException
|
||||
* @throws NoSuchItemException
|
||||
*/
|
||||
public function getById(int $id): AccountHistoryData
|
||||
{
|
||||
|
||||
@@ -36,19 +36,21 @@ use SP\DataModel\Dto\AccountCache;
|
||||
use SP\Domain\Account\AccountAclServiceInterface;
|
||||
use SP\Domain\Account\AccountSearchServiceInterface;
|
||||
use SP\Domain\Account\AccountToFavoriteServiceInterface;
|
||||
use SP\Domain\Account\In\AccountRepositoryInterface;
|
||||
use SP\Domain\Account\In\AccountToTagRepositoryInterface;
|
||||
use SP\Domain\Account\In\AccountToUserGroupRepositoryInterface;
|
||||
use SP\Domain\Account\In\AccountToUserRepositoryInterface;
|
||||
use SP\Domain\Account\Search\AccountSearchConstants;
|
||||
use SP\Domain\Account\Search\AccountSearchFilter;
|
||||
use SP\Domain\Account\Search\AccountSearchTokenizer;
|
||||
use SP\Domain\Common\Services\Service;
|
||||
use SP\Domain\Config\In\ConfigDataInterface;
|
||||
use SP\Domain\User\UserGroupServiceInterface;
|
||||
use SP\Domain\User\UserServiceInterface;
|
||||
use SP\Infrastructure\Account\Repositories\AccountSearchRepositoryInterface;
|
||||
use SP\Infrastructure\Database\QueryResult;
|
||||
use SP\Infrastructure\File\FileCache;
|
||||
use SP\Infrastructure\File\FileCacheInterface;
|
||||
use SP\Infrastructure\File\FileException;
|
||||
use SP\Mvc\Model\QueryCondition;
|
||||
use SP\Util\Filter;
|
||||
|
||||
defined('APP_ROOT') || die();
|
||||
@@ -58,24 +60,6 @@ defined('APP_ROOT') || die();
|
||||
*/
|
||||
final class AccountSearchService extends Service implements AccountSearchServiceInterface
|
||||
{
|
||||
/**
|
||||
* Regex filters for special searching
|
||||
*/
|
||||
private const FILTERS = [
|
||||
'condition' => [
|
||||
'subject' => ['is', 'not'],
|
||||
'condition' => ['expired', 'private'],
|
||||
],
|
||||
'items' => [
|
||||
'subject' => ['id', 'user', 'group', 'file', 'owner', 'maingroup', 'client', 'category', 'name_regex'],
|
||||
'condition' => null,
|
||||
],
|
||||
'operator' => [
|
||||
'subject' => ['op'],
|
||||
'condition' => ['and', 'or'],
|
||||
],
|
||||
];
|
||||
|
||||
private const COLORS_CACHE_FILE = CACHE_PATH.DIRECTORY_SEPARATOR.'colors.cache';
|
||||
|
||||
/**
|
||||
@@ -100,7 +84,6 @@ final class AccountSearchService extends Service implements AccountSearchService
|
||||
'673AB7',
|
||||
'3F51B5',
|
||||
];
|
||||
private AccountFilterUser $accountFilterUser;
|
||||
private AccountAclServiceInterface $accountAclService;
|
||||
private ConfigDataInterface $configData;
|
||||
private AccountToTagRepositoryInterface $accountToTagRepository;
|
||||
@@ -110,7 +93,7 @@ final class AccountSearchService extends Service implements AccountSearchService
|
||||
private UserServiceInterface $userService;
|
||||
private UserGroupServiceInterface $userGroupService;
|
||||
private FileCacheInterface $colorCache;
|
||||
private AccountRepositoryInterface $accountRepository;
|
||||
private AccountSearchRepositoryInterface $accountSearchRepository;
|
||||
private ?array $accountColor = null;
|
||||
private ?string $cleanString = null;
|
||||
private ?string $filterOperator = null;
|
||||
@@ -124,8 +107,7 @@ final class AccountSearchService extends Service implements AccountSearchService
|
||||
AccountToFavoriteServiceInterface $accountToFavoriteService,
|
||||
UserServiceInterface $userService,
|
||||
UserGroupServiceInterface $userGroupService,
|
||||
AccountRepositoryInterface $accountRepository,
|
||||
AccountFilterUser $accountFilterUser
|
||||
AccountSearchRepositoryInterface $accountSearchRepository,
|
||||
) {
|
||||
parent::__construct($application);
|
||||
$this->accountAclService = $accountAclService;
|
||||
@@ -135,8 +117,7 @@ final class AccountSearchService extends Service implements AccountSearchService
|
||||
$this->accountToTagRepository = $accountToTagRepository;
|
||||
$this->accountToUserRepository = $accountToUserRepository;
|
||||
$this->accountToUserGroupRepository = $accountToUserGroupRepository;
|
||||
$this->accountRepository = $accountRepository;
|
||||
$this->accountFilterUser = $accountFilterUser;
|
||||
$this->accountSearchRepository = $accountSearchRepository;
|
||||
|
||||
// TODO: use IoC
|
||||
$this->colorCache = new FileCache(self::COLORS_CACHE_FILE);
|
||||
@@ -168,16 +149,13 @@ final class AccountSearchService extends Service implements AccountSearchService
|
||||
* @throws QueryException
|
||||
* @throws SPException
|
||||
*/
|
||||
public function processSearchResults(
|
||||
AccountSearchFilter $accountSearchFilter
|
||||
): QueryResult {
|
||||
public function getByFilter(AccountSearchFilter $accountSearchFilter): QueryResult
|
||||
{
|
||||
if (!empty($accountSearchFilter->getTxtSearch())) {
|
||||
$accountSearchFilter->setStringFilters($this->analyzeQueryFilters($accountSearchFilter->getTxtSearch()));
|
||||
$this->analyzeQueryFilters($accountSearchFilter->getTxtSearch());
|
||||
}
|
||||
|
||||
if ($this->filterOperator !== null
|
||||
|| $accountSearchFilter->getFilterOperator() === null
|
||||
) {
|
||||
if ($this->filterOperator !== null || $accountSearchFilter->getFilterOperator() === null) {
|
||||
$accountSearchFilter->setFilterOperator($this->filterOperator);
|
||||
}
|
||||
|
||||
@@ -185,224 +163,65 @@ final class AccountSearchService extends Service implements AccountSearchService
|
||||
$accountSearchFilter->setCleanTxtSearch($this->cleanString);
|
||||
}
|
||||
|
||||
$queryResult = $this->accountRepository->getByFilter(
|
||||
$accountSearchFilter,
|
||||
$this->accountFilterUser->getFilter($accountSearchFilter->getGlobalSearch())
|
||||
);
|
||||
$queryResult = $this->accountSearchRepository->getByFilter($accountSearchFilter);
|
||||
|
||||
// Variables de configuración
|
||||
$maxTextLength = $this->configData->isResultsAsCards() ? 40 : 60;
|
||||
|
||||
$accountLinkEnabled = $this->context->getUserData()->getPreferences()->isAccountLink()
|
||||
|| $this->configData->isAccountLink();
|
||||
$favorites = $this->accountToFavoriteService->getForUserId($this->context->getUserData()->getId());
|
||||
|
||||
$accountsData = [];
|
||||
|
||||
/** @var AccountSearchVData $accountSearchData */
|
||||
foreach ($queryResult->getDataAsArray() as $accountSearchData) {
|
||||
$cache = $this->getCacheForAccount($accountSearchData);
|
||||
|
||||
// Obtener la ACL de la cuenta
|
||||
$accountAcl = $this->accountAclService->getAcl(
|
||||
ActionsInterface::ACCOUNT_SEARCH,
|
||||
AccountAclDto::makeFromAccountSearch(
|
||||
$accountSearchData,
|
||||
$cache->getUsers(),
|
||||
$cache->getUserGroups()
|
||||
)
|
||||
);
|
||||
|
||||
// Propiedades de búsqueda de cada cuenta
|
||||
$accountsSearchItem = new AccountSearchItem(
|
||||
$accountSearchData,
|
||||
$accountAcl,
|
||||
$this->configData
|
||||
);
|
||||
|
||||
if (!$accountSearchData->getIsPrivate()) {
|
||||
$accountsSearchItem->setUsers($cache->getUsers());
|
||||
$accountsSearchItem->setUserGroups($cache->getUserGroups());
|
||||
}
|
||||
|
||||
$accountsSearchItem->setTags(
|
||||
$this->accountToTagRepository
|
||||
->getTagsByAccountId($accountSearchData->getId())
|
||||
->getDataAsArray()
|
||||
);
|
||||
$accountsSearchItem->setTextMaxLength($maxTextLength);
|
||||
$accountsSearchItem->setColor(
|
||||
$this->pickAccountColor($accountSearchData->getClientId())
|
||||
);
|
||||
$accountsSearchItem->setLink($accountLinkEnabled);
|
||||
$accountsSearchItem->setFavorite(
|
||||
isset($favorites[$accountSearchData->getId()])
|
||||
);
|
||||
|
||||
$accountsData[] = $accountsSearchItem;
|
||||
}
|
||||
|
||||
return QueryResult::fromResults($accountsData, $queryResult->getTotalNumRows());
|
||||
return QueryResult::fromResults($this->buildAccountsData($queryResult), $queryResult->getTotalNumRows());
|
||||
}
|
||||
|
||||
/**
|
||||
* Analizar la cadena de consulta por eqituetas especiales y devolver un objeto
|
||||
* QueryCondition con los filtros
|
||||
*/
|
||||
public function analyzeQueryFilters(string $string): QueryCondition
|
||||
public function analyzeQueryFilters(string $string): void
|
||||
{
|
||||
$this->cleanString = null;
|
||||
$this->filterOperator = null;
|
||||
$tokenizer = new AccountSearchTokenizer();
|
||||
$tokens = $tokenizer->tokenizeFrom($string);
|
||||
|
||||
$queryCondition = new QueryCondition();
|
||||
$this->cleanString = $tokens->getSearch();
|
||||
$this->filterOperator = $tokens->getOperator();
|
||||
|
||||
$match = preg_match_all(
|
||||
'/(?<search>(?<!:)\b[^:]+\b(?!:))|(?<filter_subject>[a-zа-я_]+):(?!\s]*)"?(?<filter_condition>[^":]+)"?/u',
|
||||
$string,
|
||||
$filters
|
||||
);
|
||||
|
||||
if ($match !== false && $match > 0) {
|
||||
if (!empty($filters['search'][0])) {
|
||||
$this->cleanString = Filter::safeSearchString(trim($filters['search'][0]));
|
||||
}
|
||||
|
||||
$filtersAndValues = array_filter(
|
||||
array_combine(
|
||||
$filters['filter_subject'],
|
||||
$filters['filter_condition']
|
||||
)
|
||||
);
|
||||
|
||||
if (!empty($filtersAndValues)) {
|
||||
$filtersItem = array_filter(
|
||||
$filtersAndValues,
|
||||
static function ($value, $key) {
|
||||
return in_array($key, self::FILTERS['items']['subject'], true)
|
||||
&& $value !== '';
|
||||
},
|
||||
ARRAY_FILTER_USE_BOTH
|
||||
);
|
||||
|
||||
if (!empty($filtersItem)) {
|
||||
$this->processFilterItems($filtersItem, $queryCondition);
|
||||
}
|
||||
|
||||
$filtersOperator = array_filter(
|
||||
$filtersAndValues,
|
||||
static function ($value, $key) {
|
||||
return in_array($key, self::FILTERS['operator']['subject'], true)
|
||||
&& in_array($value, self::FILTERS['operator']['condition'], true);
|
||||
},
|
||||
ARRAY_FILTER_USE_BOTH
|
||||
);
|
||||
|
||||
if (!empty($filtersOperator)) {
|
||||
$this->processFilterOperator($filtersOperator);
|
||||
}
|
||||
|
||||
$filtersCondition = array_filter(
|
||||
array_map(
|
||||
static function ($subject, $condition) {
|
||||
if (in_array($subject, self::FILTERS['condition']['subject'], true)
|
||||
&& in_array($condition, self::FILTERS['condition']['condition'], true)
|
||||
) {
|
||||
return $subject.':'.$condition;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
$filters['filter_subject'],
|
||||
$filters['filter_condition']
|
||||
)
|
||||
);
|
||||
|
||||
if (count($filtersCondition) !== 0) {
|
||||
$this->processFilterIs($filtersCondition, $queryCondition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $queryCondition;
|
||||
$this->processFilterItems($tokens->getItems());
|
||||
$this->processFilterConditions($tokens->getConditions());
|
||||
}
|
||||
|
||||
private function processFilterItems(
|
||||
array $filters,
|
||||
QueryCondition $queryCondition
|
||||
): void {
|
||||
private function processFilterItems(array $filters): void
|
||||
{
|
||||
foreach ($filters as $filter => $text) {
|
||||
try {
|
||||
switch ($filter) {
|
||||
case 'user':
|
||||
case AccountSearchConstants::FILTER_USER_NAME:
|
||||
$userData = $this->userService->getByLogin(Filter::safeSearchString($text));
|
||||
|
||||
if (null !== $userData) {
|
||||
$queryCondition->addFilter(
|
||||
'Account.userId = ? OR Account.userGroupId = ? OR Account.id IN
|
||||
(SELECT AccountToUser.accountId FROM AccountToUser WHERE AccountToUser.accountId = Account.id AND AccountToUser.userId = ?
|
||||
UNION
|
||||
SELECT AccountToUserGroup.accountId FROM AccountToUserGroup WHERE AccountToUserGroup.accountId = Account.id AND AccountToUserGroup.userGroupId = ?)',
|
||||
[
|
||||
$userData->getId(),
|
||||
$userData->getUserGroupId(),
|
||||
$userData->getId(),
|
||||
$userData->getUserGroupId(),
|
||||
]
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'owner':
|
||||
$text = '%'.Filter::safeSearchString($text).'%';
|
||||
$queryCondition->addFilter(
|
||||
'Account.userLogin LIKE ? OR Account.userName LIKE ?',
|
||||
[$text, $text]
|
||||
$this->accountSearchRepository->withFilterForUser(
|
||||
$userData->getId(),
|
||||
$userData->getUserGroupId()
|
||||
);
|
||||
break;
|
||||
case 'group':
|
||||
case AccountSearchConstants::FILTER_OWNER:
|
||||
$this->accountSearchRepository->withFilterForOwner($text);
|
||||
break;
|
||||
case AccountSearchConstants::FILTER_GROUP_NAME:
|
||||
$userGroupData = $this->userGroupService->getByName(Filter::safeSearchString($text));
|
||||
|
||||
if (is_object($userGroupData)) {
|
||||
$queryCondition->addFilter(
|
||||
'Account.userGroupId = ? OR Account.id IN (SELECT AccountToUserGroup.accountId FROM AccountToUserGroup WHERE AccountToUserGroup.accountId = id AND AccountToUserGroup.userGroupId = ?)',
|
||||
[$userGroupData->getId(), $userGroupData->getId()]
|
||||
);
|
||||
}
|
||||
$this->accountSearchRepository->withFilterForGroup($userGroupData->getId());
|
||||
break;
|
||||
case 'maingroup':
|
||||
$queryCondition->addFilter(
|
||||
'Account.userGroupName LIKE ?',
|
||||
['%'.Filter::safeSearchString($text).'%']
|
||||
);
|
||||
case AccountSearchConstants::FILTER_MAIN_GROUP:
|
||||
$this->accountSearchRepository->withFilterForMainGroup($text);
|
||||
break;
|
||||
case 'file':
|
||||
$queryCondition->addFilter(
|
||||
'Account.id IN (SELECT AccountFile.accountId FROM AccountFile WHERE AccountFile.name LIKE ?)',
|
||||
['%'.$text.'%']
|
||||
);
|
||||
case AccountSearchConstants::FILTER_FILE_NAME:
|
||||
$this->accountSearchRepository->withFilterForFile($text);
|
||||
break;
|
||||
case 'id':
|
||||
$queryCondition->addFilter(
|
||||
'Account.id = ?',
|
||||
[(int)$text]
|
||||
);
|
||||
case AccountSearchConstants::FILTER_ACCOUNT_ID:
|
||||
$this->accountSearchRepository->withFilterForAccountId((int)$text);
|
||||
break;
|
||||
case 'client':
|
||||
$queryCondition->addFilter(
|
||||
'Account.clientName LIKE ?',
|
||||
['%'.Filter::safeSearchString($text).'%']
|
||||
);
|
||||
case AccountSearchConstants::FILTER_CLIENT_NAME:
|
||||
$this->accountSearchRepository->withFilterForClient($text);
|
||||
break;
|
||||
case 'category':
|
||||
$queryCondition->addFilter(
|
||||
'Account.categoryName LIKE ?',
|
||||
['%'.Filter::safeSearchString($text).'%']
|
||||
);
|
||||
case AccountSearchConstants::FILTER_CATEGORY_NAME:
|
||||
$this->accountSearchRepository->withFilterForCategory($text);
|
||||
break;
|
||||
case 'name_regex':
|
||||
$queryCondition->addFilter(
|
||||
'Account.name REGEXP ?',
|
||||
[$text]
|
||||
);
|
||||
case AccountSearchConstants::FILTER_ACCOUNT_NAME_REGEX:
|
||||
$this->accountSearchRepository->withFilterForAccountNameRegex($text);
|
||||
break;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
@@ -411,46 +230,24 @@ final class AccountSearchService extends Service implements AccountSearchService
|
||||
}
|
||||
}
|
||||
|
||||
private function processFilterOperator(array $filters): void
|
||||
private function processFilterConditions(array $filters,): void
|
||||
{
|
||||
switch ($filters['op']) {
|
||||
case 'and':
|
||||
$this->filterOperator = QueryCondition::CONDITION_AND;
|
||||
break;
|
||||
case 'or':
|
||||
$this->filterOperator = QueryCondition::CONDITION_OR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function processFilterIs(
|
||||
array $filters,
|
||||
QueryCondition $queryCondition
|
||||
): void {
|
||||
foreach ($filters as $filter) {
|
||||
switch ($filter) {
|
||||
case 'is:expired':
|
||||
$queryCondition->addFilter(
|
||||
'Account.passDateChange > 0 AND UNIX_TIMESTAMP() > Account.passDateChange',
|
||||
[]
|
||||
case AccountSearchConstants::FILTER_IS_EXPIRED:
|
||||
$this->accountSearchRepository->withFilterForIsExpired();
|
||||
break;
|
||||
case AccountSearchConstants::FILTER_NOT_EXPIRED:
|
||||
$this->accountSearchRepository->withFilterForIsNotExpired();
|
||||
break;
|
||||
case AccountSearchConstants::FILTER_IS_PRIVATE:
|
||||
$this->accountSearchRepository->withFilterForIsPrivate(
|
||||
$this->context->getUserData()->getId(),
|
||||
$this->context->getUserData()->getUserGroupId()
|
||||
);
|
||||
break;
|
||||
case 'not:expired':
|
||||
$queryCondition->addFilter(
|
||||
'Account.passDateChange = 0 OR Account.passDateChange IS NULL OR UNIX_TIMESTAMP() < Account.passDateChange',
|
||||
[]
|
||||
);
|
||||
break;
|
||||
case 'is:private':
|
||||
$queryCondition->addFilter(
|
||||
'(Account.isPrivate = 1 AND Account.userId = ?) OR (Account.isPrivateGroup = 1 AND Account.userGroupId = ?)',
|
||||
[$this->context->getUserData()->getId(), $this->context->getUserData()->getUserGroupId()]
|
||||
);
|
||||
break;
|
||||
case 'not:private':
|
||||
$queryCondition->addFilter(
|
||||
'(Account.isPrivate = 0 OR Account.isPrivate IS NULL) AND (Account.isPrivateGroup = 0 OR Account.isPrivateGroup IS NULL)'
|
||||
);
|
||||
case AccountSearchConstants::FILTER_NOT_PRIVATE:
|
||||
$this->accountSearchRepository->withFilterForIsNotPrivate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -462,9 +259,8 @@ final class AccountSearchService extends Service implements AccountSearchService
|
||||
* @throws ConstraintException
|
||||
* @throws QueryException
|
||||
*/
|
||||
protected function getCacheForAccount(
|
||||
AccountSearchVData $accountSearchData
|
||||
): AccountCache {
|
||||
private function getCacheForAccount(AccountSearchVData $accountSearchData): AccountCache
|
||||
{
|
||||
$accountId = $accountSearchData->getId();
|
||||
|
||||
/** @var AccountCache[] $cache */
|
||||
@@ -522,4 +318,65 @@ final class AccountSearchService extends Service implements AccountSearchService
|
||||
{
|
||||
return $this->cleanString;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \SP\Infrastructure\Database\QueryResult $queryResult
|
||||
*
|
||||
* @return array
|
||||
* @throws \SP\Core\Exceptions\ConstraintException
|
||||
* @throws \SP\Core\Exceptions\QueryException
|
||||
*/
|
||||
private function buildAccountsData(QueryResult $queryResult): array
|
||||
{
|
||||
$maxTextLength = $this->configData->isResultsAsCards() ? 40 : 60;
|
||||
$accountLinkEnabled = $this->context->getUserData()->getPreferences()->isAccountLink()
|
||||
|| $this->configData->isAccountLink();
|
||||
$favorites = $this->accountToFavoriteService->getForUserId($this->context->getUserData()->getId());
|
||||
$accountsData = [];
|
||||
|
||||
/** @var AccountSearchVData $accountSearchData */
|
||||
foreach ($queryResult->getDataAsArray() as $accountSearchData) {
|
||||
$cache = $this->getCacheForAccount($accountSearchData);
|
||||
|
||||
// Obtener la ACL de la cuenta
|
||||
$accountAcl = $this->accountAclService->getAcl(
|
||||
ActionsInterface::ACCOUNT_SEARCH,
|
||||
AccountAclDto::makeFromAccountSearch(
|
||||
$accountSearchData,
|
||||
$cache->getUsers(),
|
||||
$cache->getUserGroups()
|
||||
)
|
||||
);
|
||||
|
||||
// Propiedades de búsqueda de cada cuenta
|
||||
$accountsSearchItem = new AccountSearchItem(
|
||||
$accountSearchData,
|
||||
$accountAcl,
|
||||
$this->configData
|
||||
);
|
||||
|
||||
if (!$accountSearchData->getIsPrivate()) {
|
||||
$accountsSearchItem->setUsers($cache->getUsers());
|
||||
$accountsSearchItem->setUserGroups($cache->getUserGroups());
|
||||
}
|
||||
|
||||
$accountsSearchItem->setTags(
|
||||
$this->accountToTagRepository
|
||||
->getTagsByAccountId($accountSearchData->getId())
|
||||
->getDataAsArray()
|
||||
);
|
||||
$accountsSearchItem->setTextMaxLength($maxTextLength);
|
||||
$accountsSearchItem->setColor(
|
||||
$this->pickAccountColor($accountSearchData->getClientId())
|
||||
);
|
||||
$accountsSearchItem->setLink($accountLinkEnabled);
|
||||
$accountsSearchItem->setFavorite(
|
||||
isset($favorites[$accountSearchData->getId()])
|
||||
);
|
||||
|
||||
$accountsData[] = $accountsSearchItem;
|
||||
}
|
||||
|
||||
return $accountsData;
|
||||
}
|
||||
}
|
||||
@@ -164,9 +164,7 @@ final class AccountService extends Service implements AccountServiceInterface
|
||||
*/
|
||||
public function getPasswordForId(int $id): AccountPassData
|
||||
{
|
||||
$queryFilter = $this->accountFilterUser->getFilter();
|
||||
|
||||
$result = $this->accountRepository->getPasswordForId($id, $queryFilter);
|
||||
$result = $this->accountRepository->getPasswordForId($id);
|
||||
|
||||
if ($result->getNumRows() === 0) {
|
||||
throw new NoSuchItemException(__u('Account not found'));
|
||||
@@ -418,7 +416,7 @@ final class AccountService extends Service implements AccountServiceInterface
|
||||
*/
|
||||
public function update(AccountRequest $accountRequest): void
|
||||
{
|
||||
$this->transactionAware(
|
||||
$this->accountRepository->transactionAware(
|
||||
function () use ($accountRequest) {
|
||||
$userData = $this->context->getUserData();
|
||||
$userProfile = $this->context->getUserProfile() ?? new ProfileData();
|
||||
@@ -535,7 +533,7 @@ final class AccountService extends Service implements AccountServiceInterface
|
||||
*/
|
||||
public function updateBulk(AccountBulkRequest $request): void
|
||||
{
|
||||
$this->transactionAware(
|
||||
$this->accountRepository->transactionAware(
|
||||
function () use ($request) {
|
||||
foreach ($request->getItemsId() as $itemId) {
|
||||
$accountRequest = $request->getAccountRequestForId($itemId);
|
||||
@@ -557,16 +555,18 @@ final class AccountService extends Service implements AccountServiceInterface
|
||||
*/
|
||||
public function editPassword(AccountRequest $accountRequest): void
|
||||
{
|
||||
$this->transactionAware(function () use ($accountRequest) {
|
||||
$this->addHistory($accountRequest->id);
|
||||
$this->accountRepository->transactionAware(
|
||||
function () use ($accountRequest) {
|
||||
$this->addHistory($accountRequest->id);
|
||||
|
||||
$pass = $this->getPasswordEncrypted($accountRequest->pass);
|
||||
$pass = $this->getPasswordEncrypted($accountRequest->pass);
|
||||
|
||||
$accountRequest->pass = $pass['pass'];
|
||||
$accountRequest->key = $pass['key'];
|
||||
$accountRequest->pass = $pass['pass'];
|
||||
$accountRequest->key = $pass['key'];
|
||||
|
||||
$this->accountRepository->editPassword($accountRequest);
|
||||
});
|
||||
$this->accountRepository->editPassword($accountRequest);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -585,14 +585,17 @@ final class AccountService extends Service implements AccountServiceInterface
|
||||
* @param int $accountId
|
||||
*
|
||||
* @throws \SP\Domain\Common\Services\ServiceException
|
||||
* @throws \SP\Infrastructure\Common\Repositories\NoSuchItemException
|
||||
*/
|
||||
public function editRestore(int $historyId, int $accountId): void
|
||||
{
|
||||
$this->transactionAware(
|
||||
$accountHistoryData = $this->accountHistoryService->getById($historyId);
|
||||
|
||||
$this->accountRepository->transactionAware(
|
||||
function () use ($historyId, $accountId) {
|
||||
$this->addHistory($accountId);
|
||||
|
||||
if (!$this->accountRepository->editRestore($historyId, $this->context->getUserData()->getId())) {
|
||||
if (!$this->accountRepository->editRestore($accountHistoryData, $this->context->getUserData()->getId())) {
|
||||
throw new ServiceException(__u('Error on restoring the account'));
|
||||
}
|
||||
}
|
||||
@@ -604,13 +607,15 @@ final class AccountService extends Service implements AccountServiceInterface
|
||||
*/
|
||||
public function delete(int $id): AccountService
|
||||
{
|
||||
$this->transactionAware(function () use ($id) {
|
||||
$this->addHistory($id, 1);
|
||||
$this->accountRepository->transactionAware(
|
||||
function () use ($id) {
|
||||
$this->addHistory($id, 1);
|
||||
|
||||
if ($this->accountRepository->delete($id) === 0) {
|
||||
throw new NoSuchItemException(__u('Account not found'));
|
||||
if ($this->accountRepository->delete($id) === 0) {
|
||||
throw new NoSuchItemException(__u('Account not found'));
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -634,18 +639,9 @@ final class AccountService extends Service implements AccountServiceInterface
|
||||
* @throws QueryException
|
||||
* @throws ConstraintException
|
||||
*/
|
||||
public function getForUser(int $accountId = null): array
|
||||
public function getForUser(?int $accountId = null): array
|
||||
{
|
||||
$queryFilter = $this->accountFilterUser->getFilter();
|
||||
|
||||
if (null !== $accountId) {
|
||||
$queryFilter->addFilter(
|
||||
'Account.id <> ? AND (Account.parentId = 0 OR Account.parentId IS NULL)',
|
||||
[$accountId]
|
||||
);
|
||||
}
|
||||
|
||||
return $this->accountRepository->getForUser($queryFilter)->getDataAsArray();
|
||||
return $this->accountRepository->getForUser($accountId)->getDataAsArray();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -654,11 +650,7 @@ final class AccountService extends Service implements AccountServiceInterface
|
||||
*/
|
||||
public function getLinked(int $accountId): array
|
||||
{
|
||||
$queryFilter = $this->accountFilterUser->getFilter();
|
||||
|
||||
$queryFilter->addFilter('Account.parentId = ?', [$accountId]);
|
||||
|
||||
return $this->accountRepository->getLinked($queryFilter)->getDataAsArray();
|
||||
return $this->accountRepository->getLinked($accountId)->getDataAsArray();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -668,10 +660,7 @@ final class AccountService extends Service implements AccountServiceInterface
|
||||
*/
|
||||
public function getPasswordHistoryForId(int $id): AccountPassData
|
||||
{
|
||||
$queryFilter = $this->accountFilterUser->getFilterHistory();
|
||||
$queryFilter->addFilter('AccountHistory.id = ?', [$id]);
|
||||
|
||||
$result = $this->accountRepository->getPasswordHistoryForId($queryFilter);
|
||||
$result = $this->accountRepository->getPasswordHistoryForId($id);
|
||||
|
||||
if ($result->getNumRows() === 0) {
|
||||
throw new NoSuchItemException(__u('The account doesn\'t exist'));
|
||||
@@ -739,22 +728,4 @@ final class AccountService extends Service implements AccountServiceInterface
|
||||
{
|
||||
return $this->accountRepository->getAccountsPassData()->getDataAsArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtener las cuentas de una búsqueda.
|
||||
*
|
||||
* @param \SP\Domain\Account\Services\AccountSearchFilter $accountSearchFilter
|
||||
*
|
||||
* @return \SP\Infrastructure\Database\QueryResult
|
||||
* @throws \SP\Core\Exceptions\ConstraintException
|
||||
* @throws \SP\Core\Exceptions\QueryException
|
||||
* @throws \SP\Core\Exceptions\SPException
|
||||
*/
|
||||
public function getByFilter(AccountSearchFilter $accountSearchFilter): QueryResult
|
||||
{
|
||||
return $this->accountRepository->getByFilter(
|
||||
$accountSearchFilter,
|
||||
$this->accountFilterUser->getFilter($accountSearchFilter->getGlobalSearch())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -185,6 +185,6 @@ final class ClientService extends Service implements ClientServiceInterface
|
||||
*/
|
||||
public function getAllForUser(): array
|
||||
{
|
||||
return $this->clientRepository->getAllForFilter($this->accountFilterUser->getFilter())->getDataAsArray();
|
||||
return $this->clientRepository->getAllForFilter($this->accountFilterUser->buildFilter())->getDataAsArray();
|
||||
}
|
||||
}
|
||||
@@ -24,29 +24,25 @@
|
||||
|
||||
namespace SP\Infrastructure\Account\Repositories;
|
||||
|
||||
use Aura\SqlQuery\QueryFactory;
|
||||
use RuntimeException;
|
||||
use SP\Core\Context\ContextInterface;
|
||||
use SP\Core\Events\EventDispatcherInterface;
|
||||
use SP\Core\Exceptions\ConstraintException;
|
||||
use SP\Core\Exceptions\QueryException;
|
||||
use SP\Core\Exceptions\SPException;
|
||||
use SP\DataModel\AccountExtData;
|
||||
use SP\DataModel\AccountSearchVData;
|
||||
use SP\DataModel\AccountVData;
|
||||
use SP\DataModel\ItemData;
|
||||
use SP\DataModel\AccountHistoryData;
|
||||
use SP\DataModel\ItemSearchData;
|
||||
use SP\Domain\Account\In\AccountRepositoryInterface;
|
||||
use SP\Domain\Account\Out\AccountData;
|
||||
use SP\Domain\Account\Out\AccountPassData;
|
||||
use SP\Domain\Account\Services\AccountFilterUser;
|
||||
use SP\Domain\Account\Services\AccountPasswordRequest;
|
||||
use SP\Domain\Account\Services\AccountRequest;
|
||||
use SP\Domain\Account\Services\AccountSearchFilter;
|
||||
use SP\Domain\Common\Out\SimpleModel;
|
||||
use SP\Infrastructure\Common\Repositories\Repository;
|
||||
use SP\Infrastructure\Common\Repositories\RepositoryItemTrait;
|
||||
use SP\Infrastructure\Database\DatabaseInterface;
|
||||
use SP\Infrastructure\Database\QueryData;
|
||||
use SP\Infrastructure\Database\QueryResult;
|
||||
use SP\Mvc\Model\QueryAssignment;
|
||||
use SP\Mvc\Model\QueryCondition;
|
||||
use SP\Mvc\Model\QueryJoin;
|
||||
|
||||
/**
|
||||
* Class AccountRepository
|
||||
@@ -57,64 +53,77 @@ final class AccountRepository extends Repository implements AccountRepositoryInt
|
||||
{
|
||||
use RepositoryItemTrait;
|
||||
|
||||
private AccountFilterUser $accountFilterUser;
|
||||
|
||||
public function __construct(
|
||||
DatabaseInterface $database,
|
||||
ContextInterface $session,
|
||||
QueryFactory $queryFactory,
|
||||
EventDispatcherInterface $eventDispatcher,
|
||||
AccountFilterUser $accountFilterUser
|
||||
) {
|
||||
parent::__construct($database, $session, $eventDispatcher, $queryFactory);
|
||||
|
||||
$this->accountFilterUser = $accountFilterUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Devolver el número total de cuentas
|
||||
*/
|
||||
public function getTotalNumAccounts(): SimpleModel
|
||||
{
|
||||
$queryData = new QueryData();
|
||||
$queryData->setMapClassName(SimpleModel::class);
|
||||
$queryData->setQuery(AccountRepositorySql::TOTAL_NUM_ACCOUNTS);
|
||||
$query = $this->queryFactory
|
||||
->newSelect()
|
||||
->cols(['SUM(n) AS num'])
|
||||
->fromSubSelect('SELECT COUNT(*) AS n FROM Account UNION SELECT COUNT(*) AS n FROM AccountHistory', 'a');
|
||||
|
||||
return $this->db->doSelect($queryData)->getData();
|
||||
return $this->db->doSelect(QueryData::build($query))->getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param QueryCondition $queryCondition
|
||||
*
|
||||
* @return QueryResult
|
||||
*/
|
||||
public function getPasswordForId(int $id, QueryCondition $queryCondition): QueryResult
|
||||
public function getPasswordForId(int $id): QueryResult
|
||||
{
|
||||
$queryCondition->addFilter('Account.id = ?', [$id]);
|
||||
$query = $this->accountFilterUser
|
||||
->buildFilter()
|
||||
->cols([
|
||||
'Account.id,',
|
||||
'Account.name',
|
||||
'Account.login',
|
||||
'Account.pass',
|
||||
'Account.key',
|
||||
'Account.parentId',
|
||||
])
|
||||
->where('Account.id = :id', ['id' => $id])
|
||||
->limit(1);
|
||||
|
||||
$queryData = new QueryData();
|
||||
$queryData->setMapClassName(AccountPassData::class);
|
||||
$queryData->setLimit(1);
|
||||
$queryData->setSelect('Account.id, Account.name, Account.login, Account.pass, Account.key, Account.parentId');
|
||||
$queryData->setFrom('Account');
|
||||
$queryData->setWhere($queryCondition->getFilters());
|
||||
$queryData->setParams($queryCondition->getParams());
|
||||
|
||||
return $this->db->doSelect($queryData);
|
||||
return $this->db->doSelect(QueryData::build($query));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryCondition $queryCondition
|
||||
* @param int $id
|
||||
*
|
||||
* @return QueryResult
|
||||
*/
|
||||
public function getPasswordHistoryForId(QueryCondition $queryCondition): QueryResult
|
||||
public function getPasswordHistoryForId(int $id): QueryResult
|
||||
{
|
||||
$query = /** @lang SQL */
|
||||
'SELECT
|
||||
AccountHistory.id,
|
||||
AccountHistory.name,
|
||||
AccountHistory.login,
|
||||
AccountHistory.pass,
|
||||
AccountHistory.key,
|
||||
AccountHistory.parentId,
|
||||
AccountHistory.mPassHash
|
||||
FROM AccountHistory
|
||||
WHERE '.$queryCondition->getFilters();
|
||||
$query = $this->accountFilterUser
|
||||
->buildFilterHistory()
|
||||
->cols([
|
||||
'AccountHistory.id,',
|
||||
'AccountHistory.name',
|
||||
'AccountHistory.login',
|
||||
'AccountHistory.pass',
|
||||
'AccountHistory.key',
|
||||
'AccountHistory.parentId',
|
||||
'AccountHistory.mPassHash',
|
||||
])
|
||||
->where('AccountHistory.id = :id', ['id' => $id]);
|
||||
|
||||
$queryData = new QueryData();
|
||||
$queryData->setMapClassName(AccountPassData::class);
|
||||
$queryData->setQuery($query);
|
||||
$queryData->setParams($queryCondition->getParams());
|
||||
|
||||
return $this->db->doSelect($queryData);
|
||||
return $this->db->doSelect(QueryData::build($query));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,11 +137,13 @@ final class AccountRepository extends Repository implements AccountRepositoryInt
|
||||
*/
|
||||
public function incrementDecryptCounter(int $id): bool
|
||||
{
|
||||
$queryData = new QueryData();
|
||||
$queryData->setQuery(AccountRepositorySql::INCREMENT_DECRYPT_COUNTER);
|
||||
$queryData->addParam($id);
|
||||
$query = $this->queryFactory
|
||||
->newUpdate()
|
||||
->table('Account')
|
||||
->set('countDecrypt', '(countDecrypt + 1)')
|
||||
->where('id = :id', ['id' => $id]);
|
||||
|
||||
return $this->db->doQuery($queryData)->getAffectedNumRows() === 1;
|
||||
return $this->db->doQuery(QueryData::build($query))->getAffectedNumRows() === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,26 +157,30 @@ final class AccountRepository extends Repository implements AccountRepositoryInt
|
||||
*/
|
||||
public function create($itemData): int
|
||||
{
|
||||
$queryData = new QueryData();
|
||||
$queryData->setQuery(AccountRepositorySql::CREATE);
|
||||
$queryData->setParams([
|
||||
$itemData->clientId,
|
||||
$itemData->categoryId,
|
||||
$itemData->name,
|
||||
$itemData->login,
|
||||
$itemData->url,
|
||||
$itemData->pass,
|
||||
$itemData->key,
|
||||
$itemData->notes,
|
||||
$itemData->userId,
|
||||
$itemData->userGroupId,
|
||||
$itemData->userId,
|
||||
$itemData->isPrivate,
|
||||
$itemData->isPrivateGroup,
|
||||
$itemData->passDateChange,
|
||||
$itemData->parentId,
|
||||
]);
|
||||
$queryData->setOnErrorMessage(__u('Error while creating the account'));
|
||||
$query = $this->queryFactory
|
||||
->newInsert()
|
||||
->into('Account')
|
||||
->cols([
|
||||
'clientId' => $itemData->clientId,
|
||||
'categoryId' => $itemData->categoryId,
|
||||
'name' => $itemData->name,
|
||||
'login' => $itemData->login,
|
||||
'url' => $itemData->url,
|
||||
'pass' => $itemData->pass,
|
||||
'key' => $itemData->key,
|
||||
'notes' => $itemData->notes,
|
||||
'userId' => $itemData->userId,
|
||||
'userGroupId' => $itemData->userGroupId,
|
||||
'userEditId' => $itemData->userId,
|
||||
'isPrivate' => $itemData->isPrivate,
|
||||
'isPrivateGroup' => $itemData->isPrivateGroup,
|
||||
'passDateChange' => $itemData->passDateChange,
|
||||
'parentId' => $itemData->parentId,
|
||||
])
|
||||
->set('dateAdd', 'NOW()')
|
||||
->set('passDate', 'UNIX_TIMESTAMP()');
|
||||
|
||||
$queryData = QueryData::build($query)->setOnErrorMessage(__u('Error while creating the account'));
|
||||
|
||||
return $this->db->doQuery($queryData)->getLastId();
|
||||
}
|
||||
@@ -181,16 +196,20 @@ final class AccountRepository extends Repository implements AccountRepositoryInt
|
||||
*/
|
||||
public function editPassword(AccountRequest $accountRequest): int
|
||||
{
|
||||
$queryData = new QueryData();
|
||||
$queryData->setQuery(AccountRepositorySql::EDIT_PASSWORD);
|
||||
$queryData->setParams([
|
||||
$accountRequest->pass,
|
||||
$accountRequest->key,
|
||||
$accountRequest->userEditId,
|
||||
$accountRequest->passDateChange,
|
||||
$accountRequest->id,
|
||||
]);
|
||||
$queryData->setOnErrorMessage(__u('Error while updating the password'));
|
||||
$query = $this->queryFactory
|
||||
->newUpdate()
|
||||
->table('Account')
|
||||
->cols([
|
||||
'pass' => $accountRequest->pass,
|
||||
'key' => $accountRequest->key,
|
||||
'userEditId' => $accountRequest->userEditId,
|
||||
'passDateChange' => $accountRequest->passDateChange,
|
||||
])
|
||||
->set('dateEdit', 'NOW()')
|
||||
->set('passDate', 'UNIX_TIMESTAMP()')
|
||||
->where('id = :id', ['id' => $accountRequest->id]);
|
||||
|
||||
$queryData = QueryData::build($query)->setOnErrorMessage(__u('Error while updating the password'));
|
||||
|
||||
return $this->db->doQuery($queryData)->getAffectedNumRows();
|
||||
}
|
||||
@@ -206,10 +225,13 @@ final class AccountRepository extends Repository implements AccountRepositoryInt
|
||||
*/
|
||||
public function updatePassword(AccountPasswordRequest $request): bool
|
||||
{
|
||||
$queryData = new QueryData();
|
||||
$queryData->setQuery(AccountRepositorySql::UPDATE_PASSWORD);
|
||||
$queryData->setParams([$request->pass, $request->key, $request->id]);
|
||||
$queryData->setOnErrorMessage(__u('Error while updating the password'));
|
||||
$query = $this->queryFactory
|
||||
->newUpdate()
|
||||
->table('Account')
|
||||
->cols(['pass' => $request->pass, 'key' => $request->key])
|
||||
->where('id = :id', ['id' => $request->id]);
|
||||
|
||||
$queryData = QueryData::build($query)->setOnErrorMessage(__u('Error while updating the password'));
|
||||
|
||||
return $this->db->doQuery($queryData)->getAffectedNumRows() === 1;
|
||||
}
|
||||
@@ -217,19 +239,39 @@ final class AccountRepository extends Repository implements AccountRepositoryInt
|
||||
/**
|
||||
* Restaurar una cuenta desde el histórico.
|
||||
*
|
||||
* @param int $historyId El Id del registro en el histórico
|
||||
* @param \SP\DataModel\AccountHistoryData $accountHistoryData
|
||||
* @param int $userId User's Id
|
||||
*
|
||||
* @return bool
|
||||
* @throws ConstraintException
|
||||
* @throws QueryException
|
||||
* @throws \SP\Core\Exceptions\ConstraintException
|
||||
* @throws \SP\Core\Exceptions\QueryException
|
||||
*/
|
||||
public function editRestore(int $historyId, int $userId): bool
|
||||
public function editRestore(AccountHistoryData $accountHistoryData, int $userId): bool
|
||||
{
|
||||
$queryData = new QueryData();
|
||||
$queryData->setQuery(AccountRepositorySql::EDIT_RESTORE);
|
||||
$queryData->setParams([$historyId, $userId]);
|
||||
$queryData->setOnErrorMessage(__u('Error on restoring the account'));
|
||||
$query = $this->queryFactory
|
||||
->newUpdate()
|
||||
->table('Account')
|
||||
->cols([
|
||||
'clientId' => $accountHistoryData->getClientId(),
|
||||
'categoryId' => $accountHistoryData->getCategoryId(),
|
||||
'name' => $accountHistoryData->getName(),
|
||||
'login' => $accountHistoryData->getLogin(),
|
||||
'url' => $accountHistoryData->getUrl(),
|
||||
'notes' => $accountHistoryData->getNotes(),
|
||||
'userGroupId' => $accountHistoryData->getUserGroupId(),
|
||||
'userEditId' => $userId,
|
||||
'pass' => $accountHistoryData->getPass(),
|
||||
'key' => $accountHistoryData->getKey(),
|
||||
'passDate' => $accountHistoryData->getPassDate(),
|
||||
'passDateChange' => $accountHistoryData->getPassDateChange(),
|
||||
'parentId' => $accountHistoryData->getParentId(),
|
||||
'isPrivate' => $accountHistoryData->getIsPrivate(),
|
||||
'isPrivateGroup' => $accountHistoryData->getIsPrivateGroup(),
|
||||
])
|
||||
->set('dateEdit', 'NOW()')
|
||||
->where('id = :id', ['id' => $accountHistoryData->getAccountId()]);
|
||||
|
||||
$queryData = QueryData::build($query)->setOnErrorMessage(__u('Error on restoring the account'));
|
||||
|
||||
return $this->db->doQuery($queryData)->getAffectedNumRows() === 1;
|
||||
}
|
||||
@@ -245,10 +287,12 @@ final class AccountRepository extends Repository implements AccountRepositoryInt
|
||||
*/
|
||||
public function delete(int $id): int
|
||||
{
|
||||
$queryData = new QueryData();
|
||||
$queryData->setQuery(AccountRepositorySql::DELETE);
|
||||
$queryData->addParam($id);
|
||||
$queryData->setOnErrorMessage(__u('Error while deleting the account'));
|
||||
$query = $this->queryFactory
|
||||
->newDelete()
|
||||
->from('Account')
|
||||
->where('id = :id', ['id' => $id]);
|
||||
|
||||
$queryData = QueryData::build($query)->setOnErrorMessage(__u('Error while deleting the account'));
|
||||
|
||||
return $this->db->doQuery($queryData)->getAffectedNumRows();
|
||||
}
|
||||
@@ -263,53 +307,34 @@ final class AccountRepository extends Repository implements AccountRepositoryInt
|
||||
*/
|
||||
public function update($itemData): int
|
||||
{
|
||||
$queryAssignment = new QueryAssignment();
|
||||
|
||||
$queryAssignment->setFields([
|
||||
'clientId',
|
||||
'categoryId',
|
||||
'name',
|
||||
'login',
|
||||
'url',
|
||||
'notes',
|
||||
'userEditId',
|
||||
'dateEdit = NOW()',
|
||||
'passDateChange',
|
||||
'isPrivate',
|
||||
'isPrivateGroup',
|
||||
'parentId',
|
||||
], [
|
||||
$itemData->clientId,
|
||||
$itemData->categoryId,
|
||||
$itemData->name,
|
||||
$itemData->login,
|
||||
$itemData->url,
|
||||
$itemData->notes,
|
||||
$itemData->userEditId,
|
||||
$itemData->passDateChange,
|
||||
$itemData->isPrivate,
|
||||
$itemData->isPrivateGroup,
|
||||
$itemData->parentId,
|
||||
]);
|
||||
|
||||
|
||||
$queryData = new QueryData();
|
||||
$query = $this->queryFactory
|
||||
->newUpdate()
|
||||
->table('Account')
|
||||
->where('id = :id', ['id' => $itemData->id])
|
||||
->cols([
|
||||
'clientId' => $itemData->clientId,
|
||||
'categoryId' => $itemData->categoryId,
|
||||
'name' => $itemData->name,
|
||||
'login' => $itemData->login,
|
||||
'url' => $itemData->url,
|
||||
'notes' => $itemData->notes,
|
||||
'userEditId' => $itemData->userEditId,
|
||||
'passDateChange' => $itemData->passDateChange,
|
||||
'isPrivate' => $itemData->isPrivate,
|
||||
'isPrivateGroup' => $itemData->isPrivateGroup,
|
||||
'parentId' => $itemData->parentId,
|
||||
])
|
||||
->set('dateEdit', 'NOW()');
|
||||
|
||||
if ($itemData->changeUserGroup) {
|
||||
$queryAssignment->addField('userGroupId', $itemData->userGroupId);
|
||||
$query->col('userGroupId', $itemData->userGroupId);
|
||||
}
|
||||
|
||||
if ($itemData->changeOwner) {
|
||||
$queryAssignment->addField('userId', $itemData->userId);
|
||||
$query->col('userId', $itemData->userId);
|
||||
}
|
||||
|
||||
$query = /** @lang SQL */
|
||||
'UPDATE Account SET '.$queryAssignment->getAssignments().' WHERE id = ?';
|
||||
|
||||
$queryData->setQuery($query);
|
||||
$queryData->setParams($queryAssignment->getValues());
|
||||
$queryData->addParam($itemData->id);
|
||||
$queryData->setOnErrorMessage(__u('Error while updating the account'));
|
||||
$queryData = QueryData::build($query)->setOnErrorMessage(__u('Error while updating the account'));
|
||||
|
||||
return $this->db->doQuery($queryData)->getAffectedNumRows();
|
||||
}
|
||||
@@ -324,16 +349,14 @@ final class AccountRepository extends Repository implements AccountRepositoryInt
|
||||
*/
|
||||
public function updateBulk(AccountRequest $itemData): int
|
||||
{
|
||||
$queryAssignment = new QueryAssignment();
|
||||
|
||||
$queryAssignment->setFields([
|
||||
'userEditId',
|
||||
'dateEdit = NOW()',
|
||||
], [
|
||||
$itemData->userEditId,
|
||||
]);
|
||||
|
||||
$queryData = new QueryData();
|
||||
$query = $this->queryFactory
|
||||
->newUpdate()
|
||||
->table('Account')
|
||||
->where('id = :id', ['id' => $itemData->id])
|
||||
->cols([
|
||||
'userEditId' => $itemData->userEditId,
|
||||
])
|
||||
->set('dateEdit', 'NOW()');
|
||||
|
||||
$optional = ['clientId', 'categoryId', 'userId', 'userGroupId', 'passDateChange'];
|
||||
|
||||
@@ -341,7 +364,7 @@ final class AccountRepository extends Repository implements AccountRepositoryInt
|
||||
|
||||
foreach ($optional as $field) {
|
||||
if (isset($itemData->{$field}) && !empty($itemData->{$field})) {
|
||||
$queryAssignment->addField($field, $itemData->{$field});
|
||||
$query->col($field, $itemData->{$field});
|
||||
$optionalCount++;
|
||||
} else {
|
||||
logger(sprintf('Field \'%s\' not found in $itemData', $field), 'ERROR');
|
||||
@@ -352,13 +375,7 @@ final class AccountRepository extends Repository implements AccountRepositoryInt
|
||||
return 0;
|
||||
}
|
||||
|
||||
$query = /** @lang SQL */
|
||||
'UPDATE Account SET '.$queryAssignment->getAssignments().' WHERE id = ?';
|
||||
|
||||
$queryData->setQuery($query);
|
||||
$queryData->setParams($queryAssignment->getValues());
|
||||
$queryData->addParam($itemData->id);
|
||||
$queryData->setOnErrorMessage(__u('Error while updating the account'));
|
||||
$queryData = QueryData::build($query)->setOnErrorMessage(__u('Error while updating the account'));
|
||||
|
||||
return $this->db->doQuery($queryData)->getAffectedNumRows();
|
||||
}
|
||||
@@ -372,11 +389,13 @@ final class AccountRepository extends Repository implements AccountRepositoryInt
|
||||
*/
|
||||
public function getById(int $id): QueryResult
|
||||
{
|
||||
$queryData = new QueryData();
|
||||
$queryData->setQuery(AccountRepositorySql::EDIT_BY_ID);
|
||||
$queryData->setMapClassName(AccountVData::class);
|
||||
$queryData->addParam($id);
|
||||
$queryData->setOnErrorMessage(__u('Error while retrieving account\'s data'));
|
||||
$query = $this->queryFactory
|
||||
->newSelect()
|
||||
->from('account_data_v')
|
||||
->where('id = :id', ['id' => $id])
|
||||
->limit(1);
|
||||
|
||||
$queryData = QueryData::build($query)->setOnErrorMessage(__u('Error while retrieving account\'s data'));
|
||||
|
||||
return $this->db->doSelect($queryData);
|
||||
}
|
||||
@@ -388,11 +407,9 @@ final class AccountRepository extends Repository implements AccountRepositoryInt
|
||||
*/
|
||||
public function getAll(): QueryResult
|
||||
{
|
||||
$queryData = new QueryData();
|
||||
$queryData->setMapClassName(AccountData::class);
|
||||
$queryData->setQuery(AccountRepositorySql::GET_ALL);
|
||||
$query = $this->queryFactory->newSelect()->from('Account');
|
||||
|
||||
return $this->db->doSelect($queryData);
|
||||
return $this->db->doSelect(QueryData::build($query));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -420,11 +437,11 @@ final class AccountRepository extends Repository implements AccountRepositoryInt
|
||||
return 0;
|
||||
}
|
||||
|
||||
$queryData = new QueryData();
|
||||
|
||||
$queryData->setQuery('DELETE FROM Account WHERE id IN ('.$this->getParamsFromArray($ids).')');
|
||||
$queryData->setParams($ids);
|
||||
$queryData->setOnErrorMessage(__u('Error while deleting the accounts'));
|
||||
$query = $this->queryFactory
|
||||
->newDelete()
|
||||
->from('Account')
|
||||
->where('id IN (:id)', $ids);
|
||||
$queryData = QueryData::build($query)->setOnErrorMessage(__u('Error while deleting the accounts'));
|
||||
|
||||
return $this->db->doQuery($queryData)->getAffectedNumRows();
|
||||
}
|
||||
@@ -468,34 +485,41 @@ final class AccountRepository extends Repository implements AccountRepositoryInt
|
||||
*/
|
||||
public function search(ItemSearchData $itemSearchData): QueryResult
|
||||
{
|
||||
$queryData = new QueryData();
|
||||
$queryData->setSelect('id, name, clientName, categoryName, userName, userGroupName');
|
||||
$queryData->setFrom('account_search_v');
|
||||
$queryData->setOrder('name, clientName');
|
||||
$query = $this->queryFactory
|
||||
->newSelect()
|
||||
->from('account_search_v')
|
||||
->cols([
|
||||
'id',
|
||||
'name',
|
||||
'clientName',
|
||||
'categoryName',
|
||||
'userName',
|
||||
'userGroupName',
|
||||
|
||||
])
|
||||
->orderBy(['name ASC', 'clientName ASC'])
|
||||
->limit($itemSearchData->getLimitCount())
|
||||
->offset($itemSearchData->getLimitStart());
|
||||
|
||||
if (!empty($itemSearchData->getSeachString())) {
|
||||
$queryData->setWhere(
|
||||
'name LIKE ?
|
||||
OR clientName LIKE ?
|
||||
OR categoryName LIKE ?
|
||||
OR userName LIKE ?
|
||||
OR userGroupName LIKE ?'
|
||||
);
|
||||
$query->where('name LIKE :name')
|
||||
->orWhere('clientName LIKE :clientName')
|
||||
->orWhere('categoryName LIKE :categoryName')
|
||||
->orWhere('userName LIKE :userName')
|
||||
->orWhere('userGroupName LIKE :userGroupName');
|
||||
|
||||
$search = '%'.$itemSearchData->getSeachString().'%';
|
||||
$queryData->addParam($search);
|
||||
$queryData->addParam($search);
|
||||
$queryData->addParam($search);
|
||||
$queryData->addParam($search);
|
||||
$queryData->addParam($search);
|
||||
|
||||
$query->bindValues([
|
||||
'name' => $search,
|
||||
'clientName' => $search,
|
||||
'categoryName' => $search,
|
||||
'userName' => $search,
|
||||
'userGroupName' => $search,
|
||||
]);
|
||||
}
|
||||
|
||||
$queryData->setLimit(
|
||||
'?,?',
|
||||
[$itemSearchData->getLimitStart(), $itemSearchData->getLimitCount()]
|
||||
);
|
||||
|
||||
return $this->db->doSelect($queryData, true);
|
||||
return $this->db->doSelect(QueryData::build($query), true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -509,11 +533,13 @@ final class AccountRepository extends Repository implements AccountRepositoryInt
|
||||
*/
|
||||
public function incrementViewCounter(int $id): bool
|
||||
{
|
||||
$queryData = new QueryData();
|
||||
$queryData->setQuery(AccountRepositorySql::INCREMENT_VIEW_COUNTER);
|
||||
$queryData->addParam($id);
|
||||
$query = $this->queryFactory
|
||||
->newUpdate()
|
||||
->table('Account')
|
||||
->set('countView', '(countView + 1)')
|
||||
->where('id = :id', ['id' => $id]);
|
||||
|
||||
return $this->db->doQuery($queryData)->getAffectedNumRows() === 1;
|
||||
return $this->db->doQuery(QueryData::build($query))->getAffectedNumRows() === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -525,164 +551,76 @@ final class AccountRepository extends Repository implements AccountRepositoryInt
|
||||
*/
|
||||
public function getDataForLink(int $id): QueryResult
|
||||
{
|
||||
$queryData = new QueryData();
|
||||
$queryData->setQuery(AccountRepositorySql::GET_DATA_FOR_LINK);
|
||||
$queryData->setMapClassName(AccountExtData::class);
|
||||
$queryData->addParam($id);
|
||||
$queryData->setOnErrorMessage(__u('Error while retrieving account\'s data'));
|
||||
$query = $this->queryFactory
|
||||
->newSelect()
|
||||
->from('Account')
|
||||
->join('INNER', 'Client', 'Account.clientId = Client.id')
|
||||
->join('INNER', 'Category', 'Account.categoryId = Category.id')
|
||||
->cols([
|
||||
'Account.name',
|
||||
'Account.login',
|
||||
'Account.pass',
|
||||
'Account.key',
|
||||
'Account.url',
|
||||
'Account.notes',
|
||||
'Client.name AS clientName',
|
||||
'Category.name AS categoryName',
|
||||
])
|
||||
->where('Account.id = :id', ['id' => $id]);
|
||||
|
||||
$queryData = QueryData::build($query)->setOnErrorMessage(__u('Error while retrieving account\'s data'));
|
||||
|
||||
return $this->db->doSelect($queryData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtener las cuentas de una búsqueda.
|
||||
*
|
||||
* @param AccountSearchFilter $accountSearchFilter
|
||||
* @param QueryCondition $queryFilterUser
|
||||
* @param int|null $accountId
|
||||
*
|
||||
* @return QueryResult
|
||||
*/
|
||||
public function getByFilter(
|
||||
AccountSearchFilter $accountSearchFilter,
|
||||
QueryCondition $queryFilterUser
|
||||
): QueryResult {
|
||||
$queryFilters = new QueryCondition();
|
||||
|
||||
// Sets the search text depending on if special search filters are being used
|
||||
$searchText = $accountSearchFilter->getCleanTxtSearch();
|
||||
|
||||
if (!empty($searchText)) {
|
||||
$queryFilters->addFilter(
|
||||
'Account.name LIKE ? OR Account.login LIKE ? OR Account.url LIKE ? OR Account.notes LIKE ?',
|
||||
array_fill(0, 4, '%'.$searchText.'%')
|
||||
);
|
||||
}
|
||||
|
||||
// Gets special search filters
|
||||
$stringFilters = $accountSearchFilter->getStringFilters();
|
||||
|
||||
if ($stringFilters->hasFilters()) {
|
||||
$queryFilters->addFilter(
|
||||
$stringFilters->getFilters($accountSearchFilter->getFilterOperator()),
|
||||
$stringFilters->getParams()
|
||||
);
|
||||
}
|
||||
|
||||
if ($accountSearchFilter->getCategoryId() !== null) {
|
||||
$queryFilters->addFilter(
|
||||
'Account.categoryId = ?',
|
||||
[$accountSearchFilter->getCategoryId()]
|
||||
);
|
||||
}
|
||||
|
||||
if ($accountSearchFilter->getClientId() !== null) {
|
||||
$queryFilters->addFilter(
|
||||
'Account.clientId = ?',
|
||||
[$accountSearchFilter->getClientId()]
|
||||
);
|
||||
}
|
||||
|
||||
$where = [];
|
||||
|
||||
if ($queryFilterUser->hasFilters()) {
|
||||
$where[] = $queryFilterUser->getFilters();
|
||||
}
|
||||
|
||||
$queryData = new QueryData();
|
||||
$queryJoins = new QueryJoin();
|
||||
|
||||
if ($accountSearchFilter->isSearchFavorites() === true) {
|
||||
$queryJoins->addJoin(
|
||||
'INNER JOIN AccountToFavorite ON (AccountToFavorite.accountId = Account.id AND AccountToFavorite.userId = ?)',
|
||||
[$this->context->getUserData()->getId()]
|
||||
);
|
||||
}
|
||||
|
||||
if ($accountSearchFilter->hasTags()) {
|
||||
$queryJoins->addJoin('INNER JOIN AccountToTag ON AccountToTag.accountId = Account.id');
|
||||
$queryFilters->addFilter(
|
||||
'AccountToTag.tagId IN ('.$this->getParamsFromArray($accountSearchFilter->getTagsId()).')',
|
||||
$accountSearchFilter->getTagsId()
|
||||
);
|
||||
|
||||
if (QueryCondition::CONDITION_AND === $accountSearchFilter->getFilterOperator()) {
|
||||
$groupBy = sprintf(
|
||||
'Account.id HAVING COUNT(DISTINCT AccountToTag.tagId) = %d',
|
||||
count($accountSearchFilter->getTagsId())
|
||||
);
|
||||
|
||||
$queryData->setGroupBy($groupBy);
|
||||
}
|
||||
}
|
||||
|
||||
if ($queryFilters->hasFilters()) {
|
||||
$where[] = $queryFilters->getFilters($accountSearchFilter->getFilterOperator());
|
||||
}
|
||||
|
||||
$queryData->setWhere($where);
|
||||
$queryData->setParams(
|
||||
array_merge(
|
||||
$queryJoins->getParams(),
|
||||
$queryFilterUser->getParams(),
|
||||
$queryFilters->getParams()
|
||||
public function getForUser(?int $accountId = null): QueryResult
|
||||
{
|
||||
$query = $this->accountFilterUser
|
||||
->buildFilter()
|
||||
->cols(
|
||||
[
|
||||
'Account.id',
|
||||
'Account.name',
|
||||
'C.name AS clientName',
|
||||
]
|
||||
)
|
||||
);
|
||||
$queryData->setSelect('DISTINCT Account.*');
|
||||
$queryData->setFrom('account_search_v Account '.$queryJoins->getJoins());
|
||||
$queryData->setOrder($accountSearchFilter->getOrderString());
|
||||
->join('LEFT', 'Client AS C', 'Account.clientId = C.id')
|
||||
->orderBy(['Account.name ASC']);
|
||||
|
||||
if ($accountSearchFilter->getLimitCount() > 0) {
|
||||
$queryLimit = '?, ?';
|
||||
|
||||
$queryData->addParam($accountSearchFilter->getLimitStart());
|
||||
$queryData->addParam($accountSearchFilter->getLimitCount());
|
||||
$queryData->setLimit($queryLimit);
|
||||
if ($accountId) {
|
||||
$query->where('Account.id <> :id', ['id' => $accountId]);
|
||||
$query->where('Account.parentId = 0 OR Account.parentId IS NULL');
|
||||
}
|
||||
|
||||
$queryData->setMapClassName(AccountSearchVData::class);
|
||||
|
||||
return $this->db->doSelect($queryData, true);
|
||||
return $this->db->doSelect(QueryData::build($query));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryCondition $queryFilter
|
||||
* @param int $accountId
|
||||
*
|
||||
* @return QueryResult
|
||||
*/
|
||||
public function getForUser(QueryCondition $queryFilter): QueryResult
|
||||
public function getLinked(int $accountId): QueryResult
|
||||
{
|
||||
$query = /** @lang SQL */
|
||||
'SELECT Account.id, Account.name, C.name AS clientName
|
||||
FROM Account
|
||||
LEFT JOIN Client C ON Account.clientId = C.id
|
||||
WHERE '.$queryFilter->getFilters().' ORDER BY name';
|
||||
$query = $this->accountFilterUser
|
||||
->buildFilter()
|
||||
->cols(
|
||||
[
|
||||
'Account.id',
|
||||
'Account.name',
|
||||
'Client.name AS clientName',
|
||||
]
|
||||
)
|
||||
->join('INNER', 'Client', 'Account.clientId = Client.id')
|
||||
->where('Account.parentId = :parentId', ['parentId' => $accountId])
|
||||
->orderBy(['Account.name ASC']);
|
||||
|
||||
$queryData = new QueryData();
|
||||
$queryData->setMapClassName(ItemData::class);
|
||||
$queryData->setQuery($query);
|
||||
$queryData->setParams($queryFilter->getParams());
|
||||
|
||||
return $this->db->doSelect($queryData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryCondition $queryFilter
|
||||
*
|
||||
* @return QueryResult
|
||||
*/
|
||||
public function getLinked(QueryCondition $queryFilter): QueryResult
|
||||
{
|
||||
$query = /** @lang SQL */
|
||||
'SELECT Account.id, Account.name, Client.name AS clientName
|
||||
FROM Account
|
||||
INNER JOIN Client ON Account.clientId = Client.id
|
||||
WHERE '.$queryFilter->getFilters().' ORDER BY Account.name';
|
||||
|
||||
$queryData = new QueryData();
|
||||
$queryData->setQuery($query);
|
||||
$queryData->setParams($queryFilter->getParams());
|
||||
|
||||
return $this->db->doSelect($queryData);
|
||||
return $this->db->doSelect(QueryData::build($query));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -692,9 +630,19 @@ final class AccountRepository extends Repository implements AccountRepositoryInt
|
||||
*/
|
||||
public function getAccountsPassData(): QueryResult
|
||||
{
|
||||
$queryData = new QueryData();
|
||||
$queryData->setQuery(AccountRepositorySql::GET_ACCOUNT_PASS_DATA);
|
||||
$query = $this->queryFactory
|
||||
->newSelect()
|
||||
->from('Account')
|
||||
->cols(
|
||||
[
|
||||
'id',
|
||||
'name',
|
||||
'pass',
|
||||
'key',
|
||||
]
|
||||
)
|
||||
->where('BIT_LENGTH(pass) > 0');
|
||||
|
||||
return $this->db->doSelect($queryData);
|
||||
return $this->db->doSelect(QueryData::build($query));
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* sysPass
|
||||
*
|
||||
* @author nuxsmin
|
||||
* @link https://syspass.org
|
||||
* @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org
|
||||
*
|
||||
* This file is part of sysPass.
|
||||
*
|
||||
* sysPass is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sysPass is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace SP\Infrastructure\Account\Repositories;
|
||||
|
||||
|
||||
/**
|
||||
* Class AccountRepositorySql
|
||||
*/
|
||||
final class AccountRepositorySql
|
||||
{
|
||||
public const TOTAL_NUM_ACCOUNTS = 'SELECT SUM(n) AS num FROM
|
||||
(SELECT COUNT(*) AS n FROM Account UNION SELECT COUNT(*) AS n FROM AccountHistory) a';
|
||||
|
||||
public const INCREMENT_DECRYPT_COUNTER = 'UPDATE Account SET countDecrypt = (countDecrypt + 1) WHERE id = ? LIMIT 1';
|
||||
|
||||
public const CREATE = 'INSERT INTO Account SET
|
||||
clientId = ?,
|
||||
categoryId = ?,
|
||||
`name` = ?,
|
||||
login = ?,
|
||||
url = ?,
|
||||
pass = ?,
|
||||
`key` = ?,
|
||||
notes = ?,
|
||||
dateAdd = NOW(),
|
||||
userId = ?,
|
||||
userGroupId = ?,
|
||||
userEditId = ?,
|
||||
isPrivate = ?,
|
||||
isPrivateGroup = ?,
|
||||
passDate = UNIX_TIMESTAMP(),
|
||||
passDateChange = ?,
|
||||
parentId = ?';
|
||||
|
||||
public const EDIT_PASSWORD = 'UPDATE Account SET
|
||||
pass = ?,
|
||||
`key` = ?,
|
||||
userEditId = ?,
|
||||
dateEdit = NOW(),
|
||||
passDate = UNIX_TIMESTAMP(),
|
||||
passDateChange = ?
|
||||
WHERE id = ?';
|
||||
|
||||
public const UPDATE_PASSWORD = 'UPDATE Account SET
|
||||
pass = ?,
|
||||
`key` = ?
|
||||
WHERE id = ?';
|
||||
|
||||
public const EDIT_RESTORE = 'UPDATE Account dst,
|
||||
(SELECT * FROM AccountHistory AH WHERE AH.id = ?) src SET
|
||||
dst.clientId = src.clientId,
|
||||
dst.categoryId = src.categoryId,
|
||||
dst.name = src.name,
|
||||
dst.login = src.login,
|
||||
dst.url = src.url,
|
||||
dst.notes = src.notes,
|
||||
dst.userGroupId = src.userGroupId,
|
||||
dst.userEditId = ?,
|
||||
dst.dateEdit = NOW(),
|
||||
dst.pass = src.pass,
|
||||
dst.key = src.key,
|
||||
dst.passDate = src.passDate,
|
||||
dst.passDateChange = src.passDateChange,
|
||||
dst.parentId = src.parentId,
|
||||
dst.isPrivate = src.isPrivate,
|
||||
dst.isPrivateGroup = src.isPrivateGroup
|
||||
WHERE dst.id = src.accountId';
|
||||
|
||||
public const DELETE = 'DELETE FROM Account WHERE id = ? LIMIT 1';
|
||||
|
||||
public const EDIT_BY_ID = 'SELECT * FROM account_data_v WHERE id = ? LIMIT 1';
|
||||
|
||||
public const GET_ALL = 'SELECT * FROM Account ORDER BY id';
|
||||
|
||||
public const INCREMENT_VIEW_COUNTER = 'UPDATE Account SET countView = (countView + 1) WHERE id = ? LIMIT 1';
|
||||
|
||||
public const GET_DATA_FOR_LINK = 'SELECT Account.id,
|
||||
Account.name,
|
||||
Account.login,
|
||||
Account.pass,
|
||||
Account.key,
|
||||
Account.url,
|
||||
Account.notes,
|
||||
Client.name AS clientName,
|
||||
Category.name AS categoryName
|
||||
FROM Account
|
||||
INNER JOIN Client ON Account.clientId = Client.id
|
||||
INNER JOIN Category ON Account.categoryId = Category.id
|
||||
WHERE Account.id = ? LIMIT 1';
|
||||
|
||||
public const GET_ACCOUNT_PASS_DATA = 'SELECT id, `name`, pass, `key` FROM Account WHERE BIT_LENGTH(pass) > 0';
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
<?php
|
||||
/*
|
||||
* sysPass
|
||||
*
|
||||
* @author nuxsmin
|
||||
* @link https://syspass.org
|
||||
* @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org
|
||||
*
|
||||
* This file is part of sysPass.
|
||||
*
|
||||
* sysPass is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sysPass is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace SP\Infrastructure\Account\Repositories;
|
||||
|
||||
|
||||
use Aura\SqlQuery\Common\SelectInterface;
|
||||
use Aura\SqlQuery\QueryFactory;
|
||||
use SP\Core\Context\ContextInterface;
|
||||
use SP\Core\Events\EventDispatcherInterface;
|
||||
use SP\DataModel\AccountSearchVData;
|
||||
use SP\Domain\Account\Search\AccountSearchConstants;
|
||||
use SP\Domain\Account\Search\AccountSearchFilter;
|
||||
use SP\Domain\Account\Services\AccountFilterUser;
|
||||
use SP\Infrastructure\Common\Repositories\Repository;
|
||||
use SP\Infrastructure\Database\DatabaseInterface;
|
||||
use SP\Infrastructure\Database\QueryData;
|
||||
use SP\Infrastructure\Database\QueryResult;
|
||||
use SP\Util\Filter;
|
||||
|
||||
/**
|
||||
* Class AccountSearchRepository
|
||||
*/
|
||||
final class AccountSearchRepository extends Repository implements AccountSearchRepositoryInterface
|
||||
{
|
||||
protected SelectInterface $query;
|
||||
private AccountFilterUser $accountFilterUser;
|
||||
|
||||
public function __construct(
|
||||
DatabaseInterface $database,
|
||||
ContextInterface $session,
|
||||
EventDispatcherInterface $eventDispatcher,
|
||||
QueryFactory $queryFactory,
|
||||
AccountFilterUser $accountFilterUser
|
||||
) {
|
||||
parent::__construct($database, $session, $eventDispatcher, $queryFactory);
|
||||
|
||||
$this->accountFilterUser = $accountFilterUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtener las cuentas de una búsqueda.
|
||||
*
|
||||
* @param AccountSearchFilter $accountSearchFilter
|
||||
*
|
||||
* @return QueryResult
|
||||
*/
|
||||
public function getByFilter(AccountSearchFilter $accountSearchFilter): QueryResult
|
||||
{
|
||||
$this->accountFilterUser->buildFilter($accountSearchFilter->getGlobalSearch(), $this->query);
|
||||
|
||||
// Sets the search text depending on whether special search filters are being used
|
||||
$searchText = $accountSearchFilter->getCleanTxtSearch();
|
||||
|
||||
if (!empty($searchText)) {
|
||||
$searchTextLike = '%'.$searchText.'%';
|
||||
|
||||
$this->query
|
||||
->where(
|
||||
'(Account.name LIKE :name OR Account.login LIKE :login OR Account.url LIKE :url OR Account.notes LIKE :notes)',
|
||||
[
|
||||
'name' => $searchTextLike,
|
||||
'login' => $searchTextLike,
|
||||
'url' => $searchTextLike,
|
||||
'notes' => $searchTextLike,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if ($accountSearchFilter->getCategoryId() !== null) {
|
||||
$this->query
|
||||
->where(
|
||||
'Account.categoryId = :categoryId',
|
||||
[
|
||||
'categoryId' => $accountSearchFilter->getCategoryId(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if ($accountSearchFilter->getClientId() !== null) {
|
||||
$this->query
|
||||
->where(
|
||||
'Account.categoryId = :clientId',
|
||||
[
|
||||
'categoryId' => $accountSearchFilter->getClientId(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if ($accountSearchFilter->isSearchFavorites() === true) {
|
||||
$this->query
|
||||
->join(
|
||||
'INNER',
|
||||
'AccountToFavorite',
|
||||
'AccountToFavorite.accountId = Account.id AND AccountToFavorite.userId = :userId',
|
||||
[
|
||||
'userId' => $this->context->getUserData()->getId(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if ($accountSearchFilter->hasTags()) {
|
||||
$this->query->join(
|
||||
'INNER',
|
||||
'AccountToTag',
|
||||
'AccountToTag.accountId = Account.id'
|
||||
);
|
||||
|
||||
$this->query
|
||||
->where(
|
||||
'AccountToTag.tagId IN (:tagId)',
|
||||
[
|
||||
'tagId' => $accountSearchFilter->getTagsId(),
|
||||
]
|
||||
);
|
||||
|
||||
if (AccountSearchConstants::FILTER_CHAIN_AND === $accountSearchFilter->getFilterOperator()) {
|
||||
$this->query
|
||||
->groupBy(['Account.id'])
|
||||
->having(
|
||||
'COUNT(DISTINCT AccountToTag.tagId) = :tagsCount',
|
||||
[
|
||||
'tagsCount' => count($accountSearchFilter->getTagsId()),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->setOrder($accountSearchFilter);
|
||||
|
||||
if ($accountSearchFilter->getLimitCount() > 0) {
|
||||
$this->query->limit($accountSearchFilter->getLimitCount());
|
||||
$this->query->offset($accountSearchFilter->getLimitStart());
|
||||
}
|
||||
|
||||
return $this->db->doSelect(
|
||||
QueryData::build($this->query)->setMapClassName(AccountSearchVData::class),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve la cadena de ordenación de la consulta
|
||||
*/
|
||||
private function setOrder(AccountSearchFilter $filter): void
|
||||
{
|
||||
$orderKey = match ($filter->getSortKey()) {
|
||||
AccountSearchConstants::SORT_NAME => 'Account.name',
|
||||
AccountSearchConstants::SORT_CATEGORY => 'Account.categoryName',
|
||||
AccountSearchConstants::SORT_LOGIN => 'Account.login',
|
||||
AccountSearchConstants::SORT_URL => 'Account.url',
|
||||
AccountSearchConstants::SORT_CLIENT => 'Account.clientName',
|
||||
default => 'Account.clientName, Account.name',
|
||||
};
|
||||
|
||||
if ($filter->isSortViews() && !$filter->getSortKey()) {
|
||||
$this->query->orderBy(['Account.countView DESC']);
|
||||
} else {
|
||||
$sortOrder = match ($filter->getSortOrder()) {
|
||||
AccountSearchConstants::SORT_DIR_ASC => 'ASC',
|
||||
AccountSearchConstants::SORT_DIR_DESC => 'DESC',
|
||||
};
|
||||
|
||||
$this->query->orderBy([
|
||||
sprintf('%s %s', $orderKey, $sortOrder),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $userId
|
||||
* @param int $userGroupId
|
||||
*
|
||||
* @return \Aura\SqlQuery\Common\SelectInterface
|
||||
*/
|
||||
public function withFilterForUser(int $userId, int $userGroupId): SelectInterface
|
||||
{
|
||||
$where = [
|
||||
'Account.userId = :userId',
|
||||
'Account.userGroupId = :userGroupId',
|
||||
'Account.id IN (SELECT AccountToUser.accountId FROM AccountToUser WHERE AccountToUser.accountId = Account.id AND AccountToUser.userId = :userId
|
||||
UNION
|
||||
SELECT AccountToUserGroup.accountId FROM AccountToUserGroup WHERE AccountToUserGroup.accountId = Account.id AND AccountToUserGroup.userGroupId = :userGroupId)',
|
||||
];
|
||||
|
||||
return $this->query
|
||||
->where(sprintf('(%s)', join(sprintf(' %s ', AccountSearchConstants::FILTER_CHAIN_OR), $where)))
|
||||
->bindValues([
|
||||
'userId' => $userId,
|
||||
'userGroupId' => $userGroupId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $userGroupId
|
||||
*
|
||||
* @return \Aura\SqlQuery\Common\SelectInterface
|
||||
*/
|
||||
public function withFilterForGroup(int $userGroupId): SelectInterface
|
||||
{
|
||||
return $this->query
|
||||
->where('Account.userGroupId = :userGroupId')
|
||||
->orWhere(
|
||||
'(Account.id IN (SELECT AccountToUserGroup.accountId FROM AccountToUserGroup WHERE AccountToUserGroup.accountId = id AND AccountToUserGroup.userGroupId = :userGroupId))'
|
||||
)
|
||||
->bindValues([
|
||||
'userGroupId' => $userGroupId,
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $userGroupName
|
||||
*
|
||||
* @return \Aura\SqlQuery\Common\SelectInterface
|
||||
*/
|
||||
public function withFilterForMainGroup(string $userGroupName): SelectInterface
|
||||
{
|
||||
$userGroupNameLike = '%'.Filter::safeSearchString($userGroupName).'%';
|
||||
|
||||
return $this->query
|
||||
->where('Account.userGroupName LIKE :userGroupName')
|
||||
->bindValues([
|
||||
'userGroupName' => $userGroupNameLike,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $owner
|
||||
*
|
||||
* @return \Aura\SqlQuery\Common\SelectInterface
|
||||
*/
|
||||
public function withFilterForOwner(string $owner): SelectInterface
|
||||
{
|
||||
$ownerLike = '%'.Filter::safeSearchString($owner).'%';
|
||||
|
||||
return $this->query
|
||||
->where('(Account.userLogin LIKE :userLogin OR Account.userName LIKE :userName)')
|
||||
->bindValues([
|
||||
'userLogin' => $ownerLike,
|
||||
'userName' => $ownerLike,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileName
|
||||
*
|
||||
* @return \Aura\SqlQuery\Common\SelectInterface
|
||||
*/
|
||||
public function withFilterForFile(string $fileName): SelectInterface
|
||||
{
|
||||
$fileNameLike = '%'.Filter::safeSearchString($fileName).'%';
|
||||
|
||||
return $this->query
|
||||
->where(
|
||||
'(Account.id IN (SELECT AccountFile.accountId FROM AccountFile WHERE AccountFile.name LIKE :fileName))'
|
||||
)
|
||||
->bindValues([
|
||||
'fileName' => $fileNameLike,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $accountId
|
||||
*
|
||||
* @return \Aura\SqlQuery\Common\SelectInterface
|
||||
*/
|
||||
public function withFilterForAccountId(int $accountId): SelectInterface
|
||||
{
|
||||
return $this->query
|
||||
->where('Account.id = :id')
|
||||
->bindValues([
|
||||
'id' => $accountId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $clientName
|
||||
*
|
||||
* @return \Aura\SqlQuery\Common\SelectInterface
|
||||
*/
|
||||
public function withFilterForClient(string $clientName): SelectInterface
|
||||
{
|
||||
$clientNameLike = '%'.Filter::safeSearchString($clientName).'%';
|
||||
|
||||
return $this->query
|
||||
->where('Account.clientName LIKE :clientName')
|
||||
->bindValues([
|
||||
'clientName' => $clientNameLike,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $categoryName
|
||||
*
|
||||
* @return \Aura\SqlQuery\Common\SelectInterface
|
||||
*/
|
||||
public function withFilterForCategory(string $categoryName): SelectInterface
|
||||
{
|
||||
$categoryNameLike = '%'.Filter::safeSearchString($categoryName).'%';
|
||||
|
||||
return $this->query
|
||||
->where('Account.categoryName LIKE :categoryName')
|
||||
->bindValues([
|
||||
'categoryName' => $categoryNameLike,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accountName
|
||||
*
|
||||
* @return \Aura\SqlQuery\Common\SelectInterface
|
||||
*/
|
||||
public function withFilterForAccountNameRegex(string $accountName): SelectInterface
|
||||
{
|
||||
return $this->query
|
||||
->where('Account.name REGEXP :name')
|
||||
->bindValues([
|
||||
'name' => $accountName,
|
||||
]);
|
||||
}
|
||||
|
||||
public function withFilterForIsExpired(): SelectInterface
|
||||
{
|
||||
return $this->query
|
||||
->where('(Account.passDateChange > 0 AND UNIX_TIMESTAMP() > Account.passDateChange)');
|
||||
}
|
||||
|
||||
public function withFilterForIsNotExpired(): SelectInterface
|
||||
{
|
||||
return $this->query
|
||||
->where(
|
||||
'(Account.passDateChange = 0 OR Account.passDateChange IS NULL OR UNIX_TIMESTAMP() < Account.passDateChange)'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $userId
|
||||
* @param int $userGroupId
|
||||
*
|
||||
* @return \Aura\SqlQuery\Common\SelectInterface
|
||||
*/
|
||||
public function withFilterForIsPrivate(int $userId, int $userGroupId): SelectInterface
|
||||
{
|
||||
return $this->query
|
||||
->where(
|
||||
'(Account.isPrivate = 1 AND Account.userId = :userId) OR (Account.isPrivateGroup = 1 AND Account.userGroupId = :userGroupId)'
|
||||
)
|
||||
->bindValues([
|
||||
'userId' => $userId,
|
||||
'userGroupId' => $userGroupId,
|
||||
]);
|
||||
}
|
||||
|
||||
public function withFilterForIsNotPrivate(): SelectInterface
|
||||
{
|
||||
return $this->query
|
||||
->where(
|
||||
'(Account.isPrivate = 0 OR Account.isPrivate IS NULL) AND (Account.isPrivateGroup = 0 OR Account.isPrivateGroup IS NULL)'
|
||||
);
|
||||
}
|
||||
|
||||
protected function initialize()
|
||||
{
|
||||
$this->query = $this->queryFactory
|
||||
->newSelect()
|
||||
->from('account_search_v AS Account')
|
||||
->distinct();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
/*
|
||||
* sysPass
|
||||
*
|
||||
* @author nuxsmin
|
||||
* @link https://syspass.org
|
||||
* @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org
|
||||
*
|
||||
* This file is part of sysPass.
|
||||
*
|
||||
* sysPass is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sysPass is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace SP\Infrastructure\Account\Repositories;
|
||||
|
||||
|
||||
use Aura\SqlQuery\Common\SelectInterface;
|
||||
use SP\Domain\Account\Search\AccountSearchFilter;
|
||||
use SP\Infrastructure\Database\QueryResult;
|
||||
|
||||
/**
|
||||
* Class AccountSearchRepository
|
||||
*/
|
||||
interface AccountSearchRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Obtener las cuentas de una búsqueda.
|
||||
*
|
||||
* @param AccountSearchFilter $accountSearchFilter
|
||||
*
|
||||
* @return QueryResult
|
||||
*/
|
||||
public function getByFilter(AccountSearchFilter $accountSearchFilter): QueryResult;
|
||||
|
||||
/**
|
||||
* @param int $userId
|
||||
* @param int $userGroupId
|
||||
*
|
||||
* @return \Aura\SqlQuery\Common\SelectInterface
|
||||
*/
|
||||
public function withFilterForUser(int $userId, int $userGroupId): SelectInterface;
|
||||
|
||||
/**
|
||||
* @param int $userGroupId
|
||||
*
|
||||
* @return \Aura\SqlQuery\Common\SelectInterface
|
||||
*/
|
||||
public function withFilterForGroup(int $userGroupId): SelectInterface;
|
||||
|
||||
/**
|
||||
* @param string $userGroupName
|
||||
*
|
||||
* @return \Aura\SqlQuery\Common\SelectInterface
|
||||
*/
|
||||
public function withFilterForMainGroup(string $userGroupName): SelectInterface;
|
||||
|
||||
/**
|
||||
* @param string $owner
|
||||
*
|
||||
* @return \Aura\SqlQuery\Common\SelectInterface
|
||||
*/
|
||||
public function withFilterForOwner(string $owner): SelectInterface;
|
||||
|
||||
/**
|
||||
* @param string $fileName
|
||||
*
|
||||
* @return \Aura\SqlQuery\Common\SelectInterface
|
||||
*/
|
||||
public function withFilterForFile(string $fileName): SelectInterface;
|
||||
|
||||
/**
|
||||
* @param int $accountId
|
||||
*
|
||||
* @return \Aura\SqlQuery\Common\SelectInterface
|
||||
*/
|
||||
public function withFilterForAccountId(int $accountId): SelectInterface;
|
||||
|
||||
/**
|
||||
* @param string $clientName
|
||||
*
|
||||
* @return \Aura\SqlQuery\Common\SelectInterface
|
||||
*/
|
||||
public function withFilterForClient(string $clientName): SelectInterface;
|
||||
|
||||
/**
|
||||
* @param string $categoryName
|
||||
*
|
||||
* @return \Aura\SqlQuery\Common\SelectInterface
|
||||
*/
|
||||
public function withFilterForCategory(string $categoryName): SelectInterface;
|
||||
|
||||
/**
|
||||
* @param string $accountName
|
||||
*
|
||||
* @return \Aura\SqlQuery\Common\SelectInterface
|
||||
*/
|
||||
public function withFilterForAccountNameRegex(string $accountName): SelectInterface;
|
||||
|
||||
public function withFilterForIsExpired(): SelectInterface;
|
||||
|
||||
public function withFilterForIsNotExpired(): SelectInterface;
|
||||
|
||||
/**
|
||||
* @param int $userId
|
||||
* @param int $userGroupId
|
||||
*
|
||||
* @return \Aura\SqlQuery\Common\SelectInterface
|
||||
*/
|
||||
public function withFilterForIsPrivate(int $userId, int $userGroupId): SelectInterface;
|
||||
|
||||
public function withFilterForIsNotPrivate(): SelectInterface;
|
||||
}
|
||||
@@ -24,7 +24,14 @@
|
||||
|
||||
namespace SP\Infrastructure\Common\Repositories;
|
||||
|
||||
use Aura\SqlQuery\QueryFactory;
|
||||
use Closure;
|
||||
use Exception;
|
||||
use SP\Core\Context\ContextInterface;
|
||||
use SP\Core\Events\Event;
|
||||
use SP\Core\Events\EventDispatcherInterface;
|
||||
use SP\Core\Events\EventMessage;
|
||||
use SP\Domain\Common\Services\ServiceException;
|
||||
use SP\Infrastructure\Database\DatabaseInterface;
|
||||
|
||||
/**
|
||||
@@ -34,16 +41,59 @@ use SP\Infrastructure\Database\DatabaseInterface;
|
||||
*/
|
||||
abstract class Repository
|
||||
{
|
||||
protected ContextInterface $context;
|
||||
protected DatabaseInterface $db;
|
||||
protected ContextInterface $context;
|
||||
protected DatabaseInterface $db;
|
||||
protected QueryFactory $queryFactory;
|
||||
protected EventDispatcherInterface $eventDispatcher;
|
||||
|
||||
final public function __construct(DatabaseInterface $database, ContextInterface $session)
|
||||
{
|
||||
public function __construct(
|
||||
DatabaseInterface $database,
|
||||
ContextInterface $session,
|
||||
EventDispatcherInterface $eventDispatcher,
|
||||
QueryFactory $queryFactory
|
||||
) {
|
||||
$this->db = $database;
|
||||
$this->context = $session;
|
||||
$this->queryFactory = $queryFactory;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
|
||||
if (method_exists($this, 'initialize')) {
|
||||
$this->initialize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bubbles a Closure in a database transaction
|
||||
*
|
||||
* @param \Closure $closure
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \SP\Domain\Common\Services\ServiceException
|
||||
* @throws \Exception
|
||||
*/
|
||||
final public function transactionAware(Closure $closure)
|
||||
{
|
||||
if ($this->db->beginTransaction()) {
|
||||
try {
|
||||
$result = $closure->call($this);
|
||||
|
||||
$this->db->endTransaction();
|
||||
|
||||
return $result;
|
||||
} catch (Exception $e) {
|
||||
$this->db->rollbackTransaction();
|
||||
|
||||
logger('Transaction:Rollback');
|
||||
|
||||
$this->eventDispatcher->notifyEvent(
|
||||
'database.rollback',
|
||||
new Event($this, EventMessage::factory()->addDescription(__u('Rollback')))
|
||||
);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
} else {
|
||||
throw new ServiceException(__u('Unable to start a transaction'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
namespace SP\Infrastructure\Database;
|
||||
|
||||
use Aura\SqlQuery\Common\SelectInterface;
|
||||
use Aura\SqlQuery\QueryInterface;
|
||||
use Exception;
|
||||
use PDO;
|
||||
use PDOStatement;
|
||||
@@ -41,12 +43,12 @@ use SP\Core\Exceptions\SPException;
|
||||
*/
|
||||
final class Database implements DatabaseInterface
|
||||
{
|
||||
protected DbStorageInterface $dbHandler;
|
||||
protected int $numRows = 0;
|
||||
protected int $numFields = 0;
|
||||
protected ?array $lastResult = null;
|
||||
protected DbStorageInterface $dbHandler;
|
||||
private ?int $lastId = null;
|
||||
private EventDispatcher $eventDispatcher;
|
||||
private ?int $lastId = null;
|
||||
|
||||
/**
|
||||
* DB constructor.
|
||||
@@ -93,7 +95,7 @@ final class Database implements DatabaseInterface
|
||||
*/
|
||||
public function doSelect(QueryData $queryData, bool $fullCount = false): QueryResult
|
||||
{
|
||||
if ($queryData->getQuery() === '') {
|
||||
if ($queryData->getQuery()->getStatement()) {
|
||||
throw new QueryException($queryData->getOnErrorMessage(), SPException::ERROR, __u('Blank query'));
|
||||
}
|
||||
|
||||
@@ -130,14 +132,14 @@ final class Database implements DatabaseInterface
|
||||
*/
|
||||
public function doQuery(QueryData $queryData): QueryResult
|
||||
{
|
||||
$stmt = $this->prepareQueryData($queryData);
|
||||
$stmt = $this->prepareQueryData($queryData->getQuery());
|
||||
|
||||
$this->eventDispatcher->notifyEvent(
|
||||
'database.query',
|
||||
new Event($this, EventMessage::factory()->addDescription($queryData->getQuery()))
|
||||
new Event($this, EventMessage::factory()->addDescription($queryData->getQuery()->getStatement()))
|
||||
);
|
||||
|
||||
if (preg_match("/^(select|show)\s/i", $queryData->getQuery())) {
|
||||
if ($queryData->getQuery() instanceof SelectInterface) {
|
||||
$this->numFields = $stmt->columnCount();
|
||||
|
||||
return new QueryResult($this->fetch($queryData, $stmt));
|
||||
@@ -149,8 +151,7 @@ final class Database implements DatabaseInterface
|
||||
/**
|
||||
* Asociar los parámetros de la consulta utilizando el tipo adecuado
|
||||
*
|
||||
* @param QueryData $queryData Los datos de la consulta
|
||||
* @param bool $isCount Indica si es una consulta de contador de registros
|
||||
* @param QueryInterface $query Los datos de la consulta
|
||||
* @param array $options
|
||||
*
|
||||
* @return \PDOStatement
|
||||
@@ -158,25 +159,16 @@ final class Database implements DatabaseInterface
|
||||
* @throws \SP\Core\Exceptions\QueryException
|
||||
*/
|
||||
private function prepareQueryData(
|
||||
QueryData $queryData,
|
||||
bool $isCount = false,
|
||||
QueryInterface $query,
|
||||
array $options = []
|
||||
): PDOStatement {
|
||||
$query = $queryData->getQuery();
|
||||
$params = $queryData->getParams();
|
||||
|
||||
if ($isCount === true) {
|
||||
$query = $queryData->getQueryCount();
|
||||
$params = $this->getParamsForCount($queryData);
|
||||
}
|
||||
|
||||
try {
|
||||
$connection = $this->dbHandler->getConnection();
|
||||
|
||||
if (count($params) !== 0) {
|
||||
$stmt = $connection->prepare($query, $options);
|
||||
if (count($query->getBindValues()) !== 0) {
|
||||
$stmt = $connection->prepare($query->getStatement(), $options);
|
||||
|
||||
foreach ($params as $param => $value) {
|
||||
foreach ($query->getBindValues() as $param => $value) {
|
||||
// Si la clave es un número utilizamos marcadores de posición "?" en
|
||||
// la consulta. En caso contrario marcadores de nombre
|
||||
$param = is_int($param) ? $param + 1 : ':'.$param;
|
||||
@@ -223,6 +215,8 @@ final class Database implements DatabaseInterface
|
||||
|
||||
/**
|
||||
* Strips out the unused params from the query count
|
||||
*
|
||||
* TODO: remove??
|
||||
*/
|
||||
private function getParamsForCount(QueryData $queryData): array
|
||||
{
|
||||
@@ -254,11 +248,7 @@ final class Database implements DatabaseInterface
|
||||
*/
|
||||
public function getFullRowCount(QueryData $queryData): int
|
||||
{
|
||||
if ($queryData->getQueryCount() === '') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$queryRes = $this->prepareQueryData($queryData, true);
|
||||
$queryRes = $this->prepareQueryData($queryData->getQueryCount());
|
||||
$num = (int)$queryRes->fetchColumn();
|
||||
$queryRes->closeCursor();
|
||||
|
||||
@@ -290,7 +280,7 @@ final class Database implements DatabaseInterface
|
||||
);
|
||||
}
|
||||
|
||||
return $this->prepareQueryData($queryData, false, $options);
|
||||
return $this->prepareQueryData($queryData->getQuery(), $options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
namespace SP\Infrastructure\Database;
|
||||
|
||||
use Aura\SqlQuery\QueryInterface;
|
||||
use PDOStatement;
|
||||
use SP\Core\Exceptions\ConstraintException;
|
||||
use SP\Core\Exceptions\QueryException;
|
||||
|
||||
@@ -24,6 +24,11 @@
|
||||
|
||||
namespace SP\Infrastructure\Database;
|
||||
|
||||
use Aura\SqlQuery\Common\Select;
|
||||
use Aura\SqlQuery\QueryInterface;
|
||||
use SP\Core\Exceptions\QueryException;
|
||||
use SP\Domain\Common\Out\SimpleModel;
|
||||
|
||||
/**
|
||||
* Class QueryData
|
||||
*
|
||||
@@ -31,18 +36,27 @@ namespace SP\Infrastructure\Database;
|
||||
*/
|
||||
final class QueryData
|
||||
{
|
||||
protected array $params = [];
|
||||
protected ?string $query = null;
|
||||
protected ?string $mapClassName = null;
|
||||
protected bool $useKeyPair = false;
|
||||
protected ?string $select = null;
|
||||
protected ?string $from = null;
|
||||
protected ?string $where = null;
|
||||
protected ?string $groupBy = null;
|
||||
protected ?string $order = null;
|
||||
protected ?string $limit = null;
|
||||
protected ?string $queryCount = null;
|
||||
protected ?string $onErrorMessage = null;
|
||||
protected array $params = [];
|
||||
protected QueryInterface $query;
|
||||
protected ?string $mapClassName = SimpleModel::class;
|
||||
protected bool $useKeyPair = false;
|
||||
protected ?string $select = null;
|
||||
protected ?string $from = null;
|
||||
protected ?string $where = null;
|
||||
protected ?string $groupBy = null;
|
||||
protected ?string $order = null;
|
||||
protected ?string $limit = null;
|
||||
protected ?string $onErrorMessage = null;
|
||||
|
||||
public function __construct(QueryInterface $query)
|
||||
{
|
||||
$this->query = $query;
|
||||
}
|
||||
|
||||
public static function build(QueryInterface $query): QueryData
|
||||
{
|
||||
return new self($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Añadir un parámetro a la consulta
|
||||
@@ -69,28 +83,16 @@ final class QueryData
|
||||
$this->params = $data;
|
||||
}
|
||||
|
||||
public function getQuery(): string
|
||||
public function getQuery(): QueryInterface
|
||||
{
|
||||
if (empty($this->query)) {
|
||||
return $this->select.
|
||||
' '.
|
||||
$this->from.
|
||||
' '.
|
||||
$this->where.
|
||||
' '.
|
||||
$this->groupBy.
|
||||
' '.
|
||||
$this->order.
|
||||
' '.
|
||||
$this->limit;
|
||||
}
|
||||
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
public function setQuery(string $query): void
|
||||
public function setQuery(QueryInterface $query): QueryData
|
||||
{
|
||||
$this->query = $query;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMapClassName(): ?string
|
||||
@@ -98,9 +100,11 @@ final class QueryData
|
||||
return $this->mapClassName;
|
||||
}
|
||||
|
||||
public function setMapClassName(string $mapClassName): void
|
||||
public function setMapClassName(string $mapClassName): QueryData
|
||||
{
|
||||
$this->mapClassName = $mapClassName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isUseKeyPair(): bool
|
||||
@@ -146,13 +150,26 @@ final class QueryData
|
||||
$this->params = array_merge($this->params, $params);
|
||||
}
|
||||
|
||||
public function getQueryCount(): string
|
||||
/**
|
||||
* @throws \SP\Core\Exceptions\QueryException
|
||||
*/
|
||||
public function getQueryCount(): QueryInterface
|
||||
{
|
||||
if (empty($this->queryCount)) {
|
||||
return 'SELECT COUNT(*) '.$this->from.' '.$this->where;
|
||||
if ($this->query instanceof Select) {
|
||||
$countQuery = (clone $this->query)
|
||||
->resetFlags()
|
||||
->resetCols()
|
||||
->resetOrderBy()
|
||||
->resetGroupBy()
|
||||
->resetHaving()
|
||||
->page(0);
|
||||
|
||||
$countQuery->cols(['COUNT(*)']);
|
||||
|
||||
return $countQuery;
|
||||
}
|
||||
|
||||
return $this->queryCount;
|
||||
throw new QueryException(__u('Invalid query type for count'));
|
||||
}
|
||||
|
||||
public function getFrom(): ?string
|
||||
@@ -191,9 +208,11 @@ final class QueryData
|
||||
return $this->onErrorMessage ?: __u('Error while querying');
|
||||
}
|
||||
|
||||
public function setOnErrorMessage(string $onErrorMessage): void
|
||||
public function setOnErrorMessage(string $onErrorMessage): QueryData
|
||||
{
|
||||
$this->onErrorMessage = $onErrorMessage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setGroupBy(string $groupBy): void
|
||||
|
||||
@@ -93,7 +93,7 @@ class AccountRepositoryTest extends UnitaryTestCase
|
||||
->with($callback, false)
|
||||
->willReturn($expected);
|
||||
|
||||
$this->assertEquals($expected, $this->accountRepository->getPasswordForId(1, new QueryCondition()));
|
||||
$this->assertEquals($expected, $this->accountRepository->getPasswordForId(1));
|
||||
}
|
||||
|
||||
public function testGetPasswordHistoryForId(): void
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* sysPass
|
||||
*
|
||||
* @author nuxsmin
|
||||
* @link https://syspass.org
|
||||
* @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org
|
||||
* @author nuxsmin
|
||||
* @link https://syspass.org
|
||||
* @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org
|
||||
*
|
||||
* This file is part of sysPass.
|
||||
*
|
||||
@@ -19,7 +19,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace SP\Tests\Services\Account;
|
||||
@@ -34,7 +34,7 @@ use SP\Core\Exceptions\QueryException;
|
||||
use SP\Core\Exceptions\SPException;
|
||||
use SP\DataModel\UserPreferencesData;
|
||||
use SP\Domain\Account\AccountSearchServiceInterface;
|
||||
use SP\Domain\Account\Services\AccountSearchFilter;
|
||||
use SP\Domain\Account\Search\AccountSearchFilter;
|
||||
use SP\Domain\Account\Services\AccountSearchItem;
|
||||
use SP\Domain\Account\Services\AccountSearchService;
|
||||
use SP\Domain\User\Services\UserLoginResponse;
|
||||
@@ -154,7 +154,7 @@ class AccountSearchServiceTest extends DatabaseTestCase
|
||||
$searchFilter->setCategoryId($id);
|
||||
|
||||
// Comprobar un Id de categoría
|
||||
$result = self::$service->processSearchResults($searchFilter);
|
||||
$result = self::$service->getByFilter($searchFilter);
|
||||
$this->assertInstanceOf(QueryResult::class, $result);
|
||||
|
||||
if ($rows > 0) {
|
||||
@@ -181,7 +181,7 @@ class AccountSearchServiceTest extends DatabaseTestCase
|
||||
$searchFilter->setLimitCount(10);
|
||||
$searchFilter->setCategoryId(10);
|
||||
|
||||
$result = self::$service->processSearchResults($searchFilter);
|
||||
$result = self::$service->getByFilter($searchFilter);
|
||||
$this->assertInstanceOf(QueryResult::class, $result);
|
||||
$this->assertEquals(0, $result->getNumRows());
|
||||
$this->assertCount(0, $result->getDataAsArray());
|
||||
@@ -203,7 +203,7 @@ class AccountSearchServiceTest extends DatabaseTestCase
|
||||
$searchFilter->setLimitCount(10);
|
||||
$searchFilter->setClientId($id);
|
||||
|
||||
$result = self::$service->processSearchResults($searchFilter);
|
||||
$result = self::$service->getByFilter($searchFilter);
|
||||
$this->assertInstanceOf(QueryResult::class, $result);
|
||||
$this->assertEquals($rows, $result->getNumRows());
|
||||
|
||||
@@ -240,7 +240,7 @@ class AccountSearchServiceTest extends DatabaseTestCase
|
||||
$searchFilter->setClientId($clientId);
|
||||
$searchFilter->setCategoryId($categoryId);
|
||||
|
||||
$result = self::$service->processSearchResults($searchFilter);
|
||||
$result = self::$service->getByFilter($searchFilter);
|
||||
$this->assertInstanceOf(QueryResult::class, $result);
|
||||
$this->assertEquals($rows, $result->getNumRows());
|
||||
|
||||
@@ -264,7 +264,7 @@ class AccountSearchServiceTest extends DatabaseTestCase
|
||||
$searchFilter->setLimitCount(10);
|
||||
$searchFilter->setClientId(10);
|
||||
|
||||
$result = self::$service->processSearchResults($searchFilter);
|
||||
$result = self::$service->getByFilter($searchFilter);
|
||||
$this->assertInstanceOf(QueryResult::class, $result);
|
||||
$this->assertEquals(0, $result->getNumRows());
|
||||
$this->assertCount(0, $result->getDataAsArray());
|
||||
@@ -286,7 +286,7 @@ class AccountSearchServiceTest extends DatabaseTestCase
|
||||
$searchFilter->setLimitCount(10);
|
||||
$searchFilter->setTxtSearch($string);
|
||||
|
||||
$result = self::$service->processSearchResults($searchFilter);
|
||||
$result = self::$service->getByFilter($searchFilter);
|
||||
$this->assertInstanceOf(QueryResult::class, $result);
|
||||
|
||||
$this->assertEquals($rows, $result->getNumRows());
|
||||
@@ -315,7 +315,7 @@ class AccountSearchServiceTest extends DatabaseTestCase
|
||||
$searchFilter->setLimitCount(10);
|
||||
$searchFilter->setSearchFavorites(true);
|
||||
|
||||
$result = self::$service->processSearchResults($searchFilter);
|
||||
$result = self::$service->getByFilter($searchFilter);
|
||||
|
||||
$this->assertInstanceOf(QueryResult::class, $result);
|
||||
$this->assertEquals($rows, $result->getNumRows());
|
||||
@@ -347,7 +347,7 @@ class AccountSearchServiceTest extends DatabaseTestCase
|
||||
$searchFilter->setFilterOperator($operator);
|
||||
$searchFilter->setTagsId($tagsId);
|
||||
|
||||
$result = self::$service->processSearchResults($searchFilter);
|
||||
$result = self::$service->getByFilter($searchFilter);
|
||||
$this->assertInstanceOf(QueryResult::class, $result);
|
||||
|
||||
/** @var AccountSearchItem[] $data */
|
||||
|
||||
@@ -40,12 +40,12 @@ use SP\DataModel\AccountVData;
|
||||
use SP\DataModel\ItemSearchData;
|
||||
use SP\DataModel\ProfileData;
|
||||
use SP\Domain\Account\AccountHistoryServiceInterface;
|
||||
use SP\Domain\Account\Search\AccountSearchFilter;
|
||||
use SP\Domain\Account\Out\AccountData;
|
||||
use SP\Domain\Account\Services\AccountBulkRequest;
|
||||
use SP\Domain\Account\Services\AccountHistoryService;
|
||||
use SP\Domain\Account\Services\AccountPasswordRequest;
|
||||
use SP\Domain\Account\Services\AccountRequest;
|
||||
use SP\Domain\Account\Services\AccountSearchFilter;
|
||||
use SP\Domain\Account\Services\AccountService;
|
||||
use SP\Domain\Common\Services\ServiceException;
|
||||
use SP\Domain\User\Services\UserLoginResponse;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* sysPass
|
||||
*
|
||||
* @author nuxsmin
|
||||
* @link https://syspass.org
|
||||
* @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org
|
||||
* @author nuxsmin
|
||||
* @link https://syspass.org
|
||||
* @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org
|
||||
*
|
||||
* This file is part of sysPass.
|
||||
*
|
||||
@@ -19,7 +19,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace SP\Tests\Services\Import;
|
||||
@@ -34,7 +34,7 @@ use SP\Core\Exceptions\ConstraintException;
|
||||
use SP\Core\Exceptions\QueryException;
|
||||
use SP\Core\Exceptions\SPException;
|
||||
use SP\DataModel\AccountSearchVData;
|
||||
use SP\Domain\Account\Services\AccountSearchFilter;
|
||||
use SP\Domain\Account\Search\AccountSearchFilter;
|
||||
use SP\Domain\Account\Services\AccountService;
|
||||
use SP\Domain\Category\Services\CategoryService;
|
||||
use SP\Domain\Client\Services\ClientService;
|
||||
|
||||
Reference in New Issue
Block a user