chore: Refactor AuthTokenRepository

Signed-off-by: Rubén D <nuxsmin@syspass.org>
This commit is contained in:
Rubén D
2023-12-09 11:26:26 +01:00
parent efea16c6f5
commit f519f6b23e
19 changed files with 1030 additions and 390 deletions

View File

@@ -27,7 +27,7 @@ namespace SP\Modules\Web\Controllers\AuthToken;
use SP\Core\Acl\Acl;
use SP\Core\Application;
use SP\DataModel\AuthTokenData;
use SP\DataModel\AuthToken;
use SP\Domain\Auth\Ports\AuthTokenServiceInterface;
use SP\Domain\Auth\Services\AuthTokenService;
use SP\Domain\Common\Services\ServiceException;
@@ -85,7 +85,7 @@ abstract class AuthTokenViewBase extends ControllerBase
$authToken = $authTokenId
? $this->authTokenService->getById($authTokenId)
: new AuthTokenData();
: new AuthToken();
$this->view->assign('authToken', $authToken);

View File

@@ -24,7 +24,7 @@
namespace SP\Modules\Web\Forms;
use SP\DataModel\AuthTokenData;
use SP\DataModel\AuthToken;
use SP\Domain\Auth\Services\AuthTokenService;
use SP\Domain\Core\Acl\AclActionsInterface;
use SP\Domain\Core\Exceptions\ValidationException;
@@ -36,8 +36,8 @@ use SP\Domain\Core\Exceptions\ValidationException;
*/
final class AuthTokenForm extends FormBase implements FormInterface
{
protected ?AuthTokenData $authTokenData = null;
protected bool $refresh = false;
protected ?AuthToken $authTokenData = null;
protected bool $refresh = false;
/**
* Validar el formulario
@@ -74,7 +74,7 @@ final class AuthTokenForm extends FormBase implements FormInterface
{
$this->refresh = $this->request->analyzeBool('refreshtoken', false);
$this->authTokenData = new AuthTokenData();
$this->authTokenData = new AuthToken();
$this->authTokenData->setId($this->itemId);
$this->authTokenData->setUserId($this->request->analyzeInt('users'));
$this->authTokenData->setActionId($this->request->analyzeInt('actions'));
@@ -106,7 +106,7 @@ final class AuthTokenForm extends FormBase implements FormInterface
return $this->refresh;
}
public function getItemData(): ?AuthTokenData
public function getItemData(): ?AuthToken
{
return $this->authTokenData;
}

View File

@@ -23,14 +23,14 @@
*/
/**
* @var AuthTokenData $authToken
* @var AuthToken $authToken
* @var ThemeIconsInterface $icons
* @var ConfigDataInterface $configData
* @var callable $_getvar
* @var TemplateInterface $this
*/
use SP\DataModel\AuthTokenData;
use SP\DataModel\AuthToken;
use SP\Domain\Config\Ports\ConfigDataInterface;
use SP\Domain\Core\UI\ThemeIconsInterface;
use SP\Mvc\View\Components\SelectItem;

View File

@@ -24,28 +24,25 @@
namespace SP\DataModel;
use SP\Domain\Common\Adapters\DataModelInterface;
use SP\Domain\Common\Models\Model;
/**
* Class AuthTokenData
*
* @package SP\DataModel
* Class AuthToken
*/
class AuthTokenData extends Model implements DataModelInterface
class AuthToken extends Model
{
public ?int $userId = null;
public ?string $token = null;
public ?int $createdBy = null;
public ?int $startDate = null;
public ?int $actionId = null;
public ?string $hash = null;
protected ?int $id = null;
protected ?int $userId = null;
protected ?string $token = null;
protected ?int $createdBy = null;
protected ?int $startDate = null;
protected ?int $actionId = null;
protected ?string $hash = null;
protected ?string $vault = null;
public function getId(): ?int
{
return (int)$this->id;
return $this->id;
}
public function getVault(): ?string
@@ -73,11 +70,6 @@ class AuthTokenData extends Model implements DataModelInterface
return $this->startDate;
}
public function getName(): ?string
{
return null;
}
public function getActionId(): ?int
{
return $this->actionId;

View File

@@ -30,7 +30,7 @@ use SP\Core\Context\ContextException;
use SP\Core\Crypt\Crypt;
use SP\Core\Crypt\Hash;
use SP\Core\Crypt\Vault;
use SP\DataModel\AuthTokenData;
use SP\DataModel\AuthToken;
use SP\Domain\Api\Ports\ApiRequestInterface;
use SP\Domain\Api\Ports\ApiServiceInterface;
use SP\Domain\Auth\Ports\AuthTokenServiceInterface;
@@ -66,9 +66,9 @@ final class ApiService extends Service implements ApiServiceInterface
private const STATUS_INITIALIZED = 0;
private const STATUS_INITIALIZING = 1;
private TrackServiceInterface $trackService;
private TrackRequest $trackRequest;
private ?AuthTokenData $authTokenData = null;
private ?string $helpClass = null;
private TrackRequest $trackRequest;
private ?AuthToken $authTokenData = null;
private ?string $helpClass = null;
private ?int $status = null;
/**

View File

@@ -0,0 +1,155 @@
<?php
/*
* sysPass
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2023, 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\Auth\Ports;
use Exception;
use SP\DataModel\AuthToken;
use SP\DataModel\ItemSearchData;
use SP\Domain\Common\Ports\RepositoryInterface;
use SP\Domain\Core\Exceptions\ConstraintException;
use SP\Domain\Core\Exceptions\QueryException;
use SP\Infrastructure\Common\Repositories\DuplicatedItemException;
use SP\Infrastructure\Database\QueryResult;
/**
* Class AuthTokenRepository
*
* @template T of AuthToken
*/
interface AuthTokenRepositoryInterface extends RepositoryInterface
{
/**
* @param int $id
* @return QueryResult
* @throws ConstraintException
* @throws QueryException
*/
public function delete(int $id): QueryResult;
/**
* Returns the item for given id
*
* @param int $authTokenId
*
* @return QueryResult<T>
*/
public function getById(int $authTokenId): QueryResult;
/**
* Returns all the items
*
* @return QueryResult<T>
*/
public function getAll(): QueryResult;
/**
* Deletes all the items for given ids
*
* @param array $authTokensId
*
* @return QueryResult
* @throws ConstraintException
* @throws QueryException
*/
public function deleteByIdBatch(array $authTokensId): QueryResult;
/**
* Searches for items by a given filter
*
* @param ItemSearchData $itemSearchData
*
* @return QueryResult
* @throws ConstraintException
* @throws QueryException
* @throws Exception
*/
public function search(ItemSearchData $itemSearchData): QueryResult;
/**
* Creates an item
*
* @param AuthToken $authToken
*
* @return QueryResult
* @throws ConstraintException
* @throws DuplicatedItemException
* @throws QueryException
*/
public function create(AuthToken $authToken): QueryResult;
/**
* Obtener el token de la API de un usuario
*
* @param int $userId
* @return QueryResult<T>
*/
public function getTokenByUserId(int $userId): QueryResult;
/**
* Updates an item
*
* @param AuthToken $authToken
* @return bool
* @throws ConstraintException
* @throws DuplicatedItemException
* @throws QueryException
*/
public function update(AuthToken $authToken): bool;
/**
* Regenerar el hash de los tokens de un usuario
*
* @param int $userId
* @param string $token
*
* @return int
* @throws ConstraintException
* @throws QueryException
*/
public function refreshTokenByUserId(int $userId, string $token): int;
/**
* Regenerar el hash de los tokens de un usuario
*
* @param int $userId
* @param string $vault
* @param string $hash
*
* @return int
* @throws ConstraintException
* @throws QueryException
*/
public function refreshVaultByUserId(int $userId, string $vault, string $hash): int;
/**
* Devolver los datos de un token
*
* @param $actionId int El id de la accion
* @param $token string El token de seguridad
*
* @return QueryResult<T>
*/
public function getTokenByToken(int $actionId, string $token): QueryResult;
}

View File

@@ -4,7 +4,7 @@
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org
* @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
@@ -28,7 +28,7 @@ namespace SP\Domain\Auth\Ports;
use Defuse\Crypto\Exception\CryptoException;
use Defuse\Crypto\Exception\EnvironmentIsBrokenException;
use Exception;
use SP\DataModel\AuthTokenData;
use SP\DataModel\AuthToken;
use SP\DataModel\ItemSearchData;
use SP\Domain\Auth\Services\AuthTokenService;
use SP\Domain\Common\Services\ServiceException;
@@ -56,7 +56,7 @@ interface AuthTokenServiceInterface
* @throws ConstraintException
* @throws QueryException
*/
public function getById(int $id): AuthTokenData;
public function getById(int $id): AuthToken;
/**
* @throws ConstraintException
@@ -81,12 +81,12 @@ interface AuthTokenServiceInterface
* @throws ConstraintException
* @throws QueryException
*/
public function create(AuthTokenData $itemData): int;
public function create(AuthToken $itemData): int;
/**
* @throws Exception
*/
public function refreshAndUpdate(AuthTokenData $itemData): void;
public function refreshAndUpdate(AuthToken $itemData): void;
/**
* @throws CryptoException
@@ -97,14 +97,14 @@ interface AuthTokenServiceInterface
* @throws NoSuchItemException
* @throws ServiceException
*/
public function update(AuthTokenData $itemData, ?string $token = null): void;
public function update(AuthToken $itemData, ?string $token = null): void;
/**
* @throws SPException
* @throws ConstraintException
* @throws QueryException
*/
public function updateRaw(AuthTokenData $itemData): void;
public function updateRaw(AuthToken $itemData): void;
/**
* Devolver los datos de un token
@@ -116,7 +116,7 @@ interface AuthTokenServiceInterface
public function getTokenByToken(int $actionId, string $token);
/**
* @return AuthTokenData[]
* @return AuthToken[]
* @throws ConstraintException
* @throws QueryException
*/

View File

@@ -31,8 +31,9 @@ use SP\Core\Acl\Acl;
use SP\Core\Application;
use SP\Core\Crypt\Hash;
use SP\Core\Crypt\Vault;
use SP\DataModel\AuthTokenData;
use SP\DataModel\AuthToken;
use SP\DataModel\ItemSearchData;
use SP\Domain\Auth\Ports\AuthTokenRepositoryInterface;
use SP\Domain\Auth\Ports\AuthTokenServiceInterface;
use SP\Domain\Common\Services\Service;
use SP\Domain\Common\Services\ServiceException;
@@ -71,7 +72,7 @@ final class AuthTokenService extends Service implements AuthTokenServiceInterfac
private AuthTokenRepository $authTokenRepository;
public function __construct(Application $application, AuthTokenRepository $authTokenRepository)
public function __construct(Application $application, AuthTokenRepositoryInterface $authTokenRepository)
{
parent::__construct($application);
@@ -132,7 +133,7 @@ final class AuthTokenService extends Service implements AuthTokenServiceInterfac
* @throws ConstraintException
* @throws QueryException
*/
public function getById(int $id): AuthTokenData
public function getById(int $id): AuthToken
{
return $this->authTokenRepository->getById($id)->getData();
}
@@ -179,7 +180,7 @@ final class AuthTokenService extends Service implements AuthTokenServiceInterfac
* @throws ConstraintException
* @throws QueryException
*/
public function create(AuthTokenData $itemData): int
public function create(AuthToken $itemData): int
{
return $this->authTokenRepository->create($this->injectSecureData($itemData));
}
@@ -193,7 +194,7 @@ final class AuthTokenService extends Service implements AuthTokenServiceInterfac
* @throws ConstraintException
* @throws QueryException
*/
private function injectSecureData(AuthTokenData $authTokenData, ?string $token = null): AuthTokenData
private function injectSecureData(AuthToken $authTokenData, ?string $token = null): AuthToken
{
if ($token === null) {
$token = $this->authTokenRepository
@@ -254,7 +255,7 @@ final class AuthTokenService extends Service implements AuthTokenServiceInterfac
/**
* @throws Exception
*/
public function refreshAndUpdate(AuthTokenData $itemData): void
public function refreshAndUpdate(AuthToken $itemData): void
{
$this->transactionAware(
function () use ($itemData) {
@@ -287,7 +288,7 @@ final class AuthTokenService extends Service implements AuthTokenServiceInterfac
* @throws NoSuchItemException
* @throws ServiceException
*/
public function update(AuthTokenData $itemData, ?string $token = null): void
public function update(AuthToken $itemData, ?string $token = null): void
{
if ($this->authTokenRepository->update($this->injectSecureData($itemData, $token)) === 0) {
throw new NoSuchItemException(__u('Token not found'));
@@ -299,7 +300,7 @@ final class AuthTokenService extends Service implements AuthTokenServiceInterfac
* @throws ConstraintException
* @throws QueryException
*/
public function updateRaw(AuthTokenData $itemData): void
public function updateRaw(AuthToken $itemData): void
{
if ($this->authTokenRepository->update($itemData) === 0) {
throw new NoSuchItemException(__u('Token not found'));
@@ -325,7 +326,7 @@ final class AuthTokenService extends Service implements AuthTokenServiceInterfac
}
/**
* @return AuthTokenData[]
* @return AuthToken[]
* @throws ConstraintException
* @throws QueryException
*/

View File

@@ -29,7 +29,7 @@ use JsonException;
use JsonSerializable;
/**
* Class DataModel
* Class Model
*/
abstract class Model implements JsonSerializable, ArrayAccess
{

View File

@@ -25,6 +25,8 @@
namespace SP\Domain\Common\Ports;
use Closure;
use Exception;
use SP\Domain\Common\Services\ServiceException;
use SP\Infrastructure\Database\QueryResult;
/**
@@ -37,12 +39,12 @@ interface RepositoryInterface
/**
* Bubbles a Closure in a database transaction
*
* @param \Closure $closure
* @param Closure $closure
* @param object $newThis
*
* @return mixed
* @throws \SP\Domain\Common\Services\ServiceException
* @throws \Exception
* @throws ServiceException
* @throws Exception
*/
public function transactionAware(Closure $closure, object $newThis): mixed;
@@ -54,7 +56,7 @@ interface RepositoryInterface
* @param string|null $where
* @param array|null $bindValues
*
* @return \SP\Infrastructure\Database\QueryResult
* @return QueryResult
*/
public function getAny(
array $columns,

View File

@@ -4,7 +4,7 @@
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org
* @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
@@ -24,10 +24,10 @@
namespace SP\Infrastructure\Auth\Repositories;
use RuntimeException;
use SP\DataModel\AuthTokenData;
use Exception;
use SP\DataModel\AuthToken;
use SP\DataModel\ItemSearchData;
use SP\Domain\Common\Ports\RepositoryInterface;
use SP\Domain\Auth\Ports\AuthTokenRepositoryInterface;
use SP\Domain\Core\Exceptions\ConstraintException;
use SP\Domain\Core\Exceptions\QueryException;
use SP\Infrastructure\Common\Repositories\DuplicatedItemException;
@@ -36,61 +36,56 @@ use SP\Infrastructure\Common\Repositories\RepositoryItemTrait;
use SP\Infrastructure\Database\QueryData;
use SP\Infrastructure\Database\QueryResult;
use function SP\__u;
/**
* Class AuthTokenRepository
*
* @package SP\Infrastructure\Common\Repositories\ApiToken
* @template T of AuthToken
*/
final class AuthTokenRepository extends Repository implements RepositoryInterface
final class AuthTokenRepository extends Repository implements AuthTokenRepositoryInterface
{
use RepositoryItemTrait;
public const TABLE = 'AuthToken';
/**
* Deletes an item
*
* @param int $id
*
* @return int
* @param int $id
* @return QueryResult
* @throws ConstraintException
* @throws QueryException
*/
public function delete(int $id): int
public function delete(int $id): QueryResult
{
$queryData = new QueryData();
$queryData->setQuery('DELETE FROM AuthToken WHERE id = ? LIMIT 1');
$queryData->addParam($id);
$queryData->setOnErrorMessage(__u('Internal error'));
$query = $this->queryFactory
->newDelete()
->from(self::TABLE)
->where('id = :id')
->bindValues(['id' => $id]);
return $this->db->doQuery($queryData)->getAffectedNumRows();
$queryData = QueryData::build($query)->setOnErrorMessage(__u('Internal error'));
return $this->db->doQuery($queryData);
}
/**
* Returns the item for given id
*
* @param int $id
* @param int $authTokenId
*
* @return QueryResult
* @throws ConstraintException
* @throws QueryException
* @return QueryResult<T>
*/
public function getById(int $id): QueryResult
public function getById(int $authTokenId): QueryResult
{
$query = /** @lang SQL */
'SELECT id,
userId,
actionId,
createdBy,
startDate,
vault,
token,
`hash`
FROM AuthToken
WHERE id = ? LIMIT 1';
$query = $this->queryFactory
->newSelect()
->from(self::TABLE)
->cols(AuthToken::getCols())
->where('id = :id')
->bindValues(['id' => $authTokenId])
->limit(1);
$queryData = new QueryData();
$queryData->setMapClassName(AuthTokenData::class);
$queryData->setQuery($query);
$queryData->addParam($id);
$queryData = QueryData::buildWithMapper($query, AuthToken::class);
return $this->db->doSelect($queryData);
}
@@ -98,302 +93,237 @@ final class AuthTokenRepository extends Repository implements RepositoryInterfac
/**
* Returns all the items
*
* @return QueryResult
*
* @throws ConstraintException
* @throws QueryException
* @return QueryResult<T>
*/
public function getAll(): QueryResult
{
$query = /** @lang SQL */
'SELECT id,
userId,
actionId,
createdBy,
startDate,
vault,
token,
`hash`
FROM AuthToken
ORDER BY actionId, userId';
$query = $this->queryFactory
->newSelect()
->from(self::TABLE)
->cols(AuthToken::getCols());
$queryData = new QueryData();
$queryData->setMapClassName(AuthTokenData::class);
$queryData->setQuery($query);
return $this->db->doSelect($queryData);
}
/**
* Returns all the items for given ids
*
* @param array $ids
*
* @return void
*/
public function getByIdBatch(array $ids): QueryResult
{
throw new RuntimeException('Not implemented');
return $this->db->doSelect(QueryData::buildWithMapper($query, AuthToken::class));
}
/**
* Deletes all the items for given ids
*
* @param array $ids
*
* @return int
* @throws ConstraintException
* @throws QueryException
*/
public function deleteByIdBatch(array $ids): int
{
if (count($ids) === 0) {
return 0;
}
$queryData = new QueryData();
$queryData->setQuery('DELETE FROM AuthToken WHERE id IN ('.$this->buildParamsFromArray($ids).')');
$queryData->setParams($ids);
$queryData->setOnErrorMessage(__u('Internal error'));
return $this->db->doQuery($queryData)->getAffectedNumRows();
}
/**
* Checks whether the item is in use or not
*
* @param $id int
*
* @return void
*/
public function checkInUse(int $id): bool
{
throw new RuntimeException('Not implemented');
}
/**
* Searches for items by a given filter
*
* @param ItemSearchData $itemSearchData
* @param array $authTokensId
*
* @return QueryResult
* @throws ConstraintException
* @throws QueryException
*/
public function search(ItemSearchData $itemSearchData): QueryResult
public function deleteByIdBatch(array $authTokensId): QueryResult
{
$queryData = new QueryData();
$queryData->setSelect(
'AuthToken.id,
AuthToken.userId,
AuthToken.actionId,
AuthToken.token,
CONCAT(User.name, \' (\', User.login, \')\') AS userLogin'
);
$queryData->setFrom(
'AuthToken
INNER JOIN User ON AuthToken.userid = User.id'
);
if (!empty($itemSearchData->getSeachString())) {
$queryData->setWhere('User.login LIKE ? OR User.name LIKE ?');
$search = '%'.$itemSearchData->getSeachString().'%';
$queryData->addParam($search);
$queryData->addParam($search);
if (count($authTokensId) === 0) {
return new QueryResult();
}
$queryData->setOrder('User.login, AuthToken.actionId');
$queryData->setLimit(
'?,?',
[$itemSearchData->getLimitStart(), $itemSearchData->getLimitCount()]
);
$query = $this->queryFactory
->newDelete()
->from(self::TABLE)
->where('id IN (:ids)', ['ids' => $authTokensId]);
return $this->db->doSelect($queryData, true);
$queryData = QueryData::build($query)->setOnErrorMessage(__u('Internal error'));
return $this->db->doQuery($queryData);
}
/**
* Searches for items by a given filter
*
* @param ItemSearchData $itemSearchData
*
* @return QueryResult
* @throws ConstraintException
* @throws QueryException
* @throws Exception
*/
public function search(ItemSearchData $itemSearchData): QueryResult
{
$query = $this->queryFactory
->newSelect()
->from(self::TABLE)
->innerJoin('User', 'AuthToken.userid = User.id')
->cols([
'AuthToken.id',
'AuthToken.userId',
'AuthToken.actionId',
'AuthToken.token',
'User.name',
'User.login'
])
->orderBy(['name ASC', 'clientName ASC'])
->limit($itemSearchData->getLimitCount())
->offset($itemSearchData->getLimitStart());
if (!empty($itemSearchData->getSeachString())) {
$query->where('User.login LIKE :userLogin OR User.name LIKE :userName');
$search = '%' . $itemSearchData->getSeachString() . '%';
$query->bindValues(['userLogin' => $search, 'userName' => $search]);
}
return $this->db->doSelect(QueryData::build($query), true);
}
/**
* Creates an item
*
* @param AuthTokenData $itemData
* @param AuthToken $authToken
*
* @return int
* @throws DuplicatedItemException
* @return QueryResult
* @throws ConstraintException
* @throws DuplicatedItemException
* @throws QueryException
*/
public function create($itemData): int
public function create(AuthToken $authToken): QueryResult
{
if ($this->checkDuplicatedOnAdd($itemData)) {
throw new DuplicatedItemException(__u('The authorization already exist'));
if ($this->checkDuplicatedOnAdd($authToken)) {
throw new DuplicatedItemException(__u('Authorization already exist'));
}
$query = /** @lang SQL */
'INSERT INTO AuthToken
SET userId = ?,
actionId = ?,
createdBy = ?,
token = ?,
vault = ?,
`hash` = ?,
startDate = UNIX_TIMESTAMP()';
$query = $this->queryFactory
->newInsert()
->into(self::TABLE)
->cols($authToken->toArray(null, ['id', 'startDate']))
->set('startDate', 'UNIX_TIMESTAMP()');
$queryData = new QueryData();
$queryData->setQuery($query);
$queryData->setParams([
$itemData->getUserId(),
$itemData->getActionId(),
$itemData->getCreatedBy(),
$itemData->getToken(),
$itemData->getVault(),
$itemData->getHash(),
]);
$queryData->setOnErrorMessage(__u('Internal error'));
$queryData = QueryData::build($query)->setOnErrorMessage(__u('Internal error'));
return $this->db->doQuery($queryData)->getLastId();
return $this->db->doQuery($queryData);
}
/**
* Checks whether the item is duplicated on adding
*
* @param AuthTokenData $itemData
*
* @param AuthToken $authToken
* @return bool
* @throws ConstraintException
* @throws QueryException
*/
public function checkDuplicatedOnAdd($itemData): bool
private function checkDuplicatedOnAdd(AuthToken $authToken): bool
{
$query = /** @lang SQL */
'SELECT id FROM AuthToken
WHERE (userId = ? OR token = ?)
AND actionId = ? LIMIT 1';
$query = $this->queryFactory
->newSelect()
->cols(['id'])
->from(self::TABLE)
->where('userId = :userId')
->orWhere('token = :token')
->where('actionId = :actionId')
->bindValues(
[
'userId' => $authToken->getUserId(),
'token' => $authToken->getToken(),
'actionId' => $authToken->getActionId()
]
);
$queryData = new QueryData();
$queryData->setQuery($query);
$queryData->setParams([
$itemData->getUserId(),
$itemData->getToken(),
$itemData->getActionId(),
]);
return $this->db->doSelect($queryData)->getNumRows() === 1;
return $this->db->doQuery(QueryData::build($query))->getNumRows() === 1;
}
/**
* Obtener el token de la API de un usuario
*
* @param int $id
*
* @return string
* @throws ConstraintException
* @throws QueryException
* @param int $userId
* @return QueryResult<T>
*/
public function getTokenByUserId(int $id): ?string
public function getTokenByUserId(int $userId): QueryResult
{
$queryData = new QueryData();
$queryData->setQuery('SELECT token FROM AuthToken WHERE userId = ? AND token <> \'\' LIMIT 1');
$queryData->addParam($id);
$query = $this->queryFactory
->newSelect()
->from(self::TABLE)
->cols(AuthToken::getCols())
->where('userId = :userId')
->where('token <> \'\'')
->bindValues(['userId' => $userId])
->limit(1);
$result = $this->db->doSelect($queryData);
$queryData = QueryData::buildWithMapper($query, AuthToken::class);
return $result->getNumRows() === 1 ? $result->getData()->token : null;
return $this->db->doSelect($queryData);
}
/**
* Updates an item
*
* @param AuthTokenData $itemData
*
* @return int
* @throws DuplicatedItemException
* @param AuthToken $authToken
* @return bool
* @throws ConstraintException
* @throws DuplicatedItemException
* @throws QueryException
*/
public function update($itemData): int
public function update(AuthToken $authToken): bool
{
if ($this->checkDuplicatedOnUpdate($itemData)) {
throw new DuplicatedItemException(__u('The authorization already exist'));
if ($this->checkDuplicatedOnUpdate($authToken)) {
throw new DuplicatedItemException(__u('Authorization already exist'));
}
$query = /** @lang SQL */
'UPDATE AuthToken
SET userId = ?,
actionId = ?,
createdBy = ?,
token = ?,
vault = ?,
`hash` = ?,
startDate = UNIX_TIMESTAMP()
WHERE id = ? LIMIT 1';
$query = $this->queryFactory
->newUpdate()
->table(self::TABLE)
->cols($authToken->toArray(null, ['id', 'startDate']))
->set('startDate', 'UNIX_TIMESTAMP()')
->where('id = :id')
->limit(1)
->bindValues(['id' => $authToken->getId()]);
$queryData = new QueryData();
$queryData->setQuery($query);
$queryData->setParams([
$itemData->getUserId(),
$itemData->getActionId(),
$itemData->getCreatedBy(),
$itemData->getToken(),
$itemData->getVault(),
$itemData->getHash(),
$itemData->getId(),
]);
$queryData->setOnErrorMessage(__u('Internal error'));
$queryData = QueryData::build($query)->setOnErrorMessage(__u('Internal error'));
return $this->db->doQuery($queryData)->getAffectedNumRows();
return $this->db->doQuery($queryData)->getAffectedNumRows() === 1;
}
/**
* Checks whether the item is duplicated on updating
*
* @param AuthTokenData $itemData
*
* @param AuthToken $authToken
* @return bool
* @throws ConstraintException
* @throws QueryException
*/
public function checkDuplicatedOnUpdate($itemData): bool
private function checkDuplicatedOnUpdate(AuthToken $authToken): bool
{
$query = /** @lang SQL */
'SELECT id FROM AuthToken
WHERE (userId = ? OR token = ?)
AND actionId = ?
AND id <> ? LIMIT 1';
$query = $this->queryFactory
->newSelect()
->cols(['id'])
->from(self::TABLE)
->where('(userId = :userId OR token = :token)')
->where('actionId = :actionId')
->where('id <> :id')
->bindValues(
[
'id' => $authToken->getId(),
'userId' => $authToken->getUserId(),
'token' => $authToken->getToken(),
'actionId' => $authToken->getActionId()
]
);
$queryData = new QueryData();
$queryData->setQuery($query);
$queryData->setParams([
$itemData->getUserId(),
$itemData->getToken(),
$itemData->getActionId(),
$itemData->getId(),
]);
return $this->db->doSelect($queryData)->getNumRows() === 1;
return $this->db->doQuery(QueryData::build($query))->getNumRows() === 1;
}
/**
* Regenerar el hash de los tokens de un usuario
*
* @param int $id
* @param string $token
* @param int $userId
* @param string $token
*
* @return int
* @throws ConstraintException
* @throws QueryException
*/
public function refreshTokenByUserId(int $id, string $token): int
public function refreshTokenByUserId(int $userId, string $token): int
{
$query = /** @lang SQL */
'UPDATE AuthToken
SET token = ?,
startDate = UNIX_TIMESTAMP()
WHERE userId = ?';
$query = $this->queryFactory
->newUpdate()
->table(self::TABLE)
->col('token', $token)
->set('startDate', 'UNIX_TIMESTAMP()')
->where('userId = :userId', ['userId' => $userId]);
$queryData = new QueryData();
$queryData->setQuery($query);
$queryData->setParams([$token, $id]);
$queryData->setOnErrorMessage(__u('Internal error'));
$queryData = QueryData::build($query)->setOnErrorMessage(__u('Internal error'));
return $this->db->doQuery($queryData)->getAffectedNumRows();
}
@@ -401,74 +331,51 @@ final class AuthTokenRepository extends Repository implements RepositoryInterfac
/**
* Regenerar el hash de los tokens de un usuario
*
* @param int $id
* @param string $vault
* @param string $hash
* @param int $userId
* @param string $vault
* @param string $hash
*
* @return int
* @throws ConstraintException
* @throws QueryException
*/
public function refreshVaultByUserId(int $id, string $vault, string $hash): int
public function refreshVaultByUserId(int $userId, string $vault, string $hash): int
{
$query = /** @lang SQL */
'UPDATE AuthToken
SET vault = ?,
`hash` = ?,
startDate = UNIX_TIMESTAMP()
WHERE userId = ? AND vault IS NOT NULL';
$query = $this->queryFactory
->newUpdate()
->table(self::TABLE)
->cols([
'vault' => $vault,
'hash' => $hash
])
->set('startDate', 'UNIX_TIMESTAMP()')
->where('userId = :userId', ['userId' => $userId])
->where('vault IS NOT NULL');
$queryData = new QueryData();
$queryData->setQuery($query);
$queryData->setParams([$vault, $hash, $id]);
$queryData->setOnErrorMessage(__u('Internal error'));
$queryData = QueryData::build($query)->setOnErrorMessage(__u('Internal error'));
return $this->db->doQuery($queryData)->getAffectedNumRows();
}
/**
* Obtener el usuario a partir del token
*
* @param $token string El token de autorización
*
* @return false|int
* @throws ConstraintException
* @throws QueryException
*/
public function getUserIdForToken(string $token)
{
$queryData = new QueryData();
$queryData->setQuery('SELECT userId FROM AuthToken WHERE token = ? LIMIT 1');
$queryData->addParam($token);
$result = $this->db->doSelect($queryData);
return $result->getNumRows() === 1 ? (int)$result->getData()->userId : false;
}
/**
* Devolver los datos de un token
*
* @param $actionId int El id de la accion
* @param $token string El token de seguridad
*
* @return QueryResult
* @throws ConstraintException
* @throws QueryException
* @return QueryResult<T>
*/
public function getTokenByToken(int $actionId, string $token): QueryResult
{
$query = /** @lang SQL */
'SELECT id, actionId, userId, vault, `hash`, token
FROM AuthToken
WHERE actionId = ?
AND token = ? LIMIT 1';
$query = $this->queryFactory
->newSelect()
->from(self::TABLE)
->cols(AuthToken::getCols())
->where('actionId = :actionId')
->where('token = :token')
->bindValues(['actionId' => $actionId, 'token' => $token])
->limit(1);
$queryData = new QueryData();
$queryData->setMapClassName(AuthTokenData::class);
$queryData->setQuery($query);
$queryData->setParams([$actionId, $token]);
return $this->db->doSelect($queryData);
return $this->db->doSelect(QueryData::buildWithMapper($query, AuthToken::class));
}
}

View File

@@ -24,6 +24,7 @@
namespace SP\Infrastructure\Database;
use SP\Domain\Common\Models\Model;
use SP\Domain\Core\Exceptions\SPException;
use function SP\__u;
@@ -31,7 +32,7 @@ use function SP\__u;
/**
* Class QueryResult
*
* @package SP\Infrastructure\Database
* @template T of Model
*/
class QueryResult
{
@@ -71,8 +72,6 @@ class QueryResult
}
/**
* @template T
*
* @param class-string<T>|null $dataType
*
* @return T|mixed|null
@@ -100,8 +99,6 @@ class QueryResult
}
/**
* @template T
*
* @param class-string<T>|null $dataType
*
* @return T[]
@@ -159,9 +156,4 @@ class QueryResult
return $this;
}
public function getDataType(): ?string
{
return $this->dataType;
}
}

View File

@@ -31,7 +31,7 @@ use ReflectionClass;
use SP\Core\Context\ContextException;
use SP\Core\Crypt\Crypt;
use SP\Core\Crypt\Vault;
use SP\DataModel\AuthTokenData;
use SP\DataModel\AuthToken;
use SP\Domain\Api\Ports\ApiRequestInterface;
use SP\Domain\Api\Services\ApiService;
use SP\Domain\Auth\Ports\AuthTokenServiceInterface;
@@ -270,7 +270,7 @@ class ApiServiceTest extends UnitaryTestCase
$userId = self::$faker->randomNumber();
$authTokenData = new AuthTokenData(['actionId' => $actionId, 'userId' => $userId]);
$authTokenData = new AuthToken(['actionId' => $actionId, 'userId' => $userId]);
$this->authTokenService
->expects(self::once())
@@ -407,7 +407,7 @@ class ApiServiceTest extends UnitaryTestCase
$userId = self::$faker->randomNumber();
$authTokenData = new AuthTokenData(['actionId' => self::$faker->randomNumber(), 'userId' => $userId]);
$authTokenData = new AuthToken(['actionId' => self::$faker->randomNumber(), 'userId' => $userId]);
$this->authTokenService
->expects(self::once())
@@ -451,7 +451,7 @@ class ApiServiceTest extends UnitaryTestCase
$userId = self::$faker->randomNumber();
$authTokenData =
new AuthTokenData(
new AuthToken(
['actionId' => $actionId, 'userId' => $userId, 'hash' => $authTokenHash, 'vault' => serialize($vault)]
);
@@ -500,7 +500,7 @@ class ApiServiceTest extends UnitaryTestCase
$userId = self::$faker->randomNumber();
$authTokenData =
new AuthTokenData(
new AuthToken(
['actionId' => $actionId, 'userId' => $userId, 'hash' => $authTokenHash, 'vault' => serialize($vault)]
);
@@ -586,7 +586,7 @@ class ApiServiceTest extends UnitaryTestCase
$userId = self::$faker->randomNumber();
$authTokenData =
new AuthTokenData(
new AuthToken(
['actionId' => $actionId, 'userId' => $userId, 'hash' => $authTokenHash, 'vault' => serialize($vault)]
);
@@ -649,7 +649,7 @@ class ApiServiceTest extends UnitaryTestCase
$userId = self::$faker->randomNumber();
$authTokenData =
new AuthTokenData(
new AuthToken(
['actionId' => $actionId, 'userId' => $userId, 'hash' => $authTokenHash, 'vault' => serialize($vault)]
);

View File

@@ -0,0 +1,64 @@
<?php
/*
* sysPass
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2023, 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\Tests\Generators;
use SP\Core\Crypt\Crypt;
use SP\Core\Crypt\Vault;
use SP\DataModel\AuthToken;
use SP\Domain\Core\Exceptions\CryptException;
/**
* Class AuthTokenGenerator
*/
final class AuthTokenGenerator extends DataGenerator
{
public function buildAuthToken(): AuthToken
{
return new AuthToken($this->authTokenProperties());
}
private function authTokenProperties(): array
{
return [
'id' => $this->faker->randomNumber(),
'userId' => $this->faker->randomNumber(),
'token' => $this->faker->sha1(),
'createdBy' => $this->faker->randomNumber(),
'startDate' => $this->faker->unixTime(),
'actionId' => $this->faker->randomNumber(),
'hash' => $this->faker->sha1(),
'vault' => serialize($this->getVault())
];
}
private function getVault(): ?Vault
{
try {
return Vault::factory(new Crypt())->saveData($this->faker->text(), $this->faker->text);
} catch (CryptException) {
return null;
}
}
}

View File

@@ -646,7 +646,7 @@ class AccountRepositoryTest extends UnitaryTestCase
$callback = new Callback(
static function (QueryData $arg) use ($item) {
$params = $arg->getQuery()->getBindValues();
$searchStringLike = '%'.$item->getSeachString().'%';
$searchStringLike = '%' . $item->getSeachString() . '%';
return count($params) === 5
&& $params['name'] === $searchStringLike
@@ -659,7 +659,7 @@ class AccountRepositoryTest extends UnitaryTestCase
}
);
$this->database->expects(self::once())->method('doSelect')->with($callback);
$this->database->expects(self::once())->method('doSelect')->with($callback, true);
$this->accountRepository->search($item);
}
@@ -674,7 +674,7 @@ class AccountRepositoryTest extends UnitaryTestCase
}
);
$this->database->expects(self::once())->method('doSelect')->with($callback);
$this->database->expects(self::once())->method('doSelect')->with($callback, true);
$this->accountRepository->search(new ItemSearchData());
}

View File

@@ -38,7 +38,7 @@ use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use RuntimeException;
use SP\Core\Bootstrap\BootstrapApi;
use SP\DataModel\AuthTokenData;
use SP\DataModel\AuthToken;
use SP\Domain\Api\Services\ApiRequest;
use SP\Domain\Auth\Services\AuthTokenService;
use SP\Domain\Config\Ports\ConfigDataInterface;
@@ -226,8 +226,9 @@ abstract class ApiTestCase extends TestCase
private static function createApiToken(
AuthTokenService $service,
int $actionId
): AuthTokenData {
$data = new AuthTokenData();
): AuthToken
{
$data = new AuthToken();
$data->setActionId($actionId);
$data->setCreatedBy(1);
$data->setHash(self::AUTH_TOKEN_PASS);

View File

@@ -31,8 +31,9 @@ use DI\NotFoundException;
use SP\Core\Context\ContextException;
use SP\Core\Crypt\Hash;
use SP\Core\Crypt\Vault;
use SP\DataModel\AuthTokenData;
use SP\DataModel\AuthToken;
use SP\DataModel\ItemSearchData;
use SP\Domain\Auth\Ports\AuthTokenRepositoryInterface;
use SP\Domain\Core\Acl\AclActionsInterface;
use SP\Domain\Core\Crypt\VaultInterface;
use SP\Domain\Core\Exceptions\ConstraintException;
@@ -58,7 +59,7 @@ class AuthTokenRepositoryTest extends DatabaseTestCase
public const AUTH_TOKEN_PASS = 123456;
/**
* @var AuthTokenRepository
* @var AuthTokenRepositoryInterface
*/
private static $repository;
@@ -88,7 +89,7 @@ class AuthTokenRepositoryTest extends DatabaseTestCase
$data = $result->getData();
$this->assertInstanceOf(AuthTokenData::class, $data);
$this->assertInstanceOf(AuthToken::class, $data);
$this->assertEquals(1, $data->getId());
$this->assertEquals(AclActionsInterface::ACCOUNT_SEARCH, $data->getActionId());
$this->assertEquals('12b9027d24efff7bfbaca8bd774a4c34b45de35e033d2b192a88f4dfaee5c233', $data->getToken());
@@ -99,7 +100,7 @@ class AuthTokenRepositoryTest extends DatabaseTestCase
$data = $result->getData();
$this->assertInstanceOf(AuthTokenData::class, $data);
$this->assertInstanceOf(AuthToken::class, $data);
$this->assertEquals(2, $data->getId());
$this->assertEquals(AclActionsInterface::ACCOUNT_VIEW_PASS, $data->getActionId());
$this->assertEquals(self::AUTH_TOKEN, $data->getToken());
@@ -125,7 +126,7 @@ class AuthTokenRepositoryTest extends DatabaseTestCase
public function testGetTokenByToken()
{
$result = self::$repository->getTokenByToken(AclActionsInterface::ACCOUNT_VIEW_PASS, self::AUTH_TOKEN);
/** @var AuthTokenData $data */
/** @var AuthToken $data */
$data = $result->getData();
$this->assertEquals(1, $result->getNumRows());
@@ -158,12 +159,12 @@ class AuthTokenRepositoryTest extends DatabaseTestCase
$this->assertEquals(1, self::$repository->refreshVaultByUserId(1, $vault, $hash));
$result = self::$repository->getTokenByToken(AclActionsInterface::ACCOUNT_VIEW_PASS, self::AUTH_TOKEN);
/** @var AuthTokenData $data */
/** @var AuthToken $data */
$data = $result->getData();
$this->assertEquals(1, $result->getNumRows());
$this->assertInstanceOf(AuthTokenData::class, $data);
$this->assertInstanceOf(AuthToken::class, $data);
$this->assertTrue(Hash::checkHashKey(self::AUTH_TOKEN_PASS, $data->getHash()));
$this->assertEquals($vault, $data->getVault());
@@ -203,7 +204,7 @@ class AuthTokenRepositoryTest extends DatabaseTestCase
$hash = Hash::hashKey('prueba123');
$vault = Vault::getInstance()->saveData('prueba', 'prueba123');
$authTokenData = new AuthTokenData();
$authTokenData = new AuthToken();
$authTokenData->setId(1);
$authTokenData->setActionId(AclActionsInterface::ACCOUNT_CREATE);
$authTokenData->setCreatedBy(1);
@@ -215,11 +216,11 @@ class AuthTokenRepositoryTest extends DatabaseTestCase
$this->assertEquals(1, self::$repository->update($authTokenData));
$result = self::$repository->getTokenByToken(AclActionsInterface::ACCOUNT_CREATE, $token);
/** @var AuthTokenData $data */
/** @var AuthToken $data */
$data = $result->getData();
$this->assertEquals(1, $result->getNumRows());
$this->assertInstanceOf(AuthTokenData::class, $data);
$this->assertInstanceOf(AuthToken::class, $data);
$this->assertEquals(AclActionsInterface::ACCOUNT_CREATE, $data->getActionId());
$this->assertEquals($hash, $data->getHash());
$this->assertEquals(2, $data->getUserId());
@@ -316,7 +317,7 @@ class AuthTokenRepositoryTest extends DatabaseTestCase
$hash = Hash::hashKey('prueba123');
$vault = Vault::getInstance()->saveData('prueba', 'prueba123');
$authTokenData = new AuthTokenData();
$authTokenData = new AuthToken();
$authTokenData->setActionId(AclActionsInterface::ACCOUNT_CREATE);
$authTokenData->setCreatedBy(1);
$authTokenData->setHash($hash);
@@ -328,11 +329,11 @@ class AuthTokenRepositoryTest extends DatabaseTestCase
$this->assertEquals(6, self::getRowCount('AuthToken'));
$result = self::$repository->getTokenByToken(AclActionsInterface::ACCOUNT_CREATE, $token);
/** @var AuthTokenData $data */
/** @var AuthToken $data */
$data = $result->getData();
$this->assertEquals(1, $result->getNumRows());
$this->assertInstanceOf(AuthTokenData::class, $data);
$this->assertInstanceOf(AuthToken::class, $data);
$this->assertEquals(AclActionsInterface::ACCOUNT_CREATE, $data->getActionId());
$this->assertEquals($hash, $data->getHash());
$this->assertEquals(6, $data->getId());

View File

@@ -0,0 +1,525 @@
<?php
/*
* sysPass
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2023, 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\Tests\Infrastructure\Auth\Repositories;
use Aura\SqlQuery\Common\DeleteInterface;
use Aura\SqlQuery\Common\InsertInterface;
use Aura\SqlQuery\Common\SelectInterface;
use Aura\SqlQuery\Common\UpdateInterface;
use Aura\SqlQuery\QueryFactory;
use PHPUnit\Framework\Constraint\Callback;
use PHPUnit\Framework\MockObject\MockObject;
use SP\DataModel\AuthToken;
use SP\DataModel\ItemSearchData;
use SP\Domain\Common\Models\Simple;
use SP\Domain\Core\Exceptions\ConstraintException;
use SP\Domain\Core\Exceptions\QueryException;
use SP\Infrastructure\Auth\Repositories\AuthTokenRepository;
use SP\Infrastructure\Common\Repositories\DuplicatedItemException;
use SP\Infrastructure\Database\DatabaseInterface;
use SP\Infrastructure\Database\QueryData;
use SP\Infrastructure\Database\QueryResult;
use SP\Tests\Generators\AuthTokenGenerator;
use SP\Tests\UnitaryTestCase;
/**
* Class AuthTokenRepositoryTest
*
* @group unitary
*/
class AuthTokenRepositoryTest extends UnitaryTestCase
{
private AuthTokenRepository $authTokenRepository;
private MockObject|DatabaseInterface $database;
/**
* @throws ConstraintException
* @throws QueryException
*/
public function testSearch()
{
$item = new ItemSearchData(self::$faker->name);
$callback = new Callback(
static function (QueryData $arg) use ($item) {
$query = $arg->getQuery();
$params = $query->getBindValues();
$searchStringLike = '%' . $item->getSeachString() . '%';
return count($params) === 2
&& $params['userLogin'] === $searchStringLike
&& $params['userName'] === $searchStringLike
&& $arg->getMapClassName() === Simple::class
&& is_a($query, SelectInterface::class)
&& !empty($query->getStatement());
}
);
$this->database
->expects(self::once())
->method('doSelect')
->with($callback, true);
$this->authTokenRepository->search($item);
}
/**
* @throws ConstraintException
* @throws QueryException
*/
public function testSearchWithoutString(): void
{
$callback = new Callback(
static function (QueryData $arg) {
$query = $arg->getQuery();
return count($query->getBindValues()) === 0
&& $arg->getMapClassName() === Simple::class
&& is_a($query, SelectInterface::class)
&& !empty($query->getStatement());
}
);
$this->database
->expects(self::once())
->method('doSelect')
->with($callback, true);
$this->authTokenRepository->search(new ItemSearchData());
}
/**
* @throws ConstraintException
* @throws QueryException
*/
public function testDeleteByIdBatch()
{
$ids = [self::$faker->randomNumber(), self::$faker->randomNumber(), self::$faker->randomNumber()];
$callback = new Callback(
static function (QueryData $arg) use ($ids) {
$query = $arg->getQuery();
$values = $query->getBindValues();
return count($values) === 3
&& array_shift($values) === array_shift($ids)
&& array_shift($values) === array_shift($ids)
&& array_shift($values) === array_shift($ids)
&& $arg->getMapClassName() === Simple::class
&& is_a($query, DeleteInterface::class)
&& !empty($query->getStatement());
}
);
$this->database
->expects(self::once())
->method('doQuery')
->with($callback);
$this->authTokenRepository->deleteByIdBatch($ids);
}
/**
* @throws ConstraintException
* @throws QueryException
*/
public function testDeleteByIdBatchWithNoIds(): void
{
$this->database
->expects(self::never())
->method('doQuery');
$this->authTokenRepository->deleteByIdBatch([]);
}
public function testGetTokenByUserId()
{
$id = self::$faker->randomNumber();
$callback = new Callback(
static function (QueryData $arg) use ($id) {
$query = $arg->getQuery();
$params = $query->getBindValues();
return count($params) === 1
&& $params['userId'] === $id
&& $arg->getMapClassName() === AuthToken::class
&& is_a($query, SelectInterface::class)
&& !empty($query->getStatement());
}
);
$this->database
->expects(self::once())
->method('doSelect')
->with($callback);
$this->authTokenRepository->getTokenByUserId($id);
}
public function testGetById()
{
$id = self::$faker->randomNumber();
$callback = new Callback(
static function (QueryData $arg) use ($id) {
$query = $arg->getQuery();
$params = $query->getBindValues();
return count($params) === 1
&& $params['id'] === $id
&& $arg->getMapClassName() === AuthToken::class
&& is_a($query, SelectInterface::class)
&& !empty($query->getStatement());
}
);
$this->database
->expects(self::once())
->method('doSelect')
->with($callback);
$this->authTokenRepository->getById($id);
}
/**
* @throws ConstraintException
* @throws DuplicatedItemException
* @throws QueryException
*/
public function testUpdate()
{
$authToken = AuthTokenGenerator::factory()->buildAuthToken();
$callbackDuplicate = new Callback(
static function (QueryData $arg) use ($authToken) {
$query = $arg->getQuery();
$params = $query->getBindValues();
return count($params) === 4
&& $params['id'] === $authToken->getId()
&& $params['userId'] === $authToken->getUserId()
&& $params['token'] === $authToken->getToken()
&& $params['actionId'] === $authToken->getActionId()
&& is_a($query, SelectInterface::class)
&& !empty($query->getStatement());
}
);
$callbackUpdate = new Callback(
static function (QueryData $arg) use ($authToken) {
$query = $arg->getQuery();
$params = $query->getBindValues();
return count($params) === 7
&& $params['id'] === $authToken->getId()
&& $params['userId'] === $authToken->getUserId()
&& $params['token'] === $authToken->getToken()
&& $params['createdBy'] === $authToken->getCreatedBy()
&& $params['actionId'] === $authToken->getActionId()
&& $params['hash'] === $authToken->getHash()
&& $params['vault'] === $authToken->getVault()
&& is_a($query, UpdateInterface::class)
&& !empty($query->getStatement());
}
);
$this->database
->expects(self::exactly(2))
->method('doQuery')
->with(...self::withConsecutive([$callbackDuplicate], [$callbackUpdate]))
->willReturn(new QueryResult([]), new QueryResult([1]));
$this->authTokenRepository->update($authToken);
}
/**
* @throws ConstraintException
* @throws DuplicatedItemException
* @throws QueryException
*/
public function testUpdateWithDuplicate()
{
$authToken = AuthTokenGenerator::factory()->buildAuthToken();
$callback = new Callback(
static function (QueryData $arg) use ($authToken) {
$query = $arg->getQuery();
$params = $query->getBindValues();
return count($params) === 4
&& $params['id'] === $authToken->getId()
&& $params['userId'] === $authToken->getUserId()
&& $params['token'] === $authToken->getToken()
&& $params['actionId'] === $authToken->getActionId()
&& is_a($query, SelectInterface::class)
&& !empty($query->getStatement());
}
);
$this->database
->expects(self::once())
->method('doQuery')
->with($callback)
->willReturn(new QueryResult([1]));
$this->expectException(DuplicatedItemException::class);
$this->expectExceptionMessage('Authorization already exist');
$this->authTokenRepository->update($authToken);
}
/**
* @throws ConstraintException
* @throws QueryException
*/
public function testRefreshVaultByUserId()
{
$userId = self::$faker->randomNumber();
$token = self::$faker->sha1();
$callback = new Callback(
static function (QueryData $arg) use ($userId, $token) {
$query = $arg->getQuery();
$params = $query->getBindValues();
return count($params) === 2
&& $params['userId'] === $userId
&& $params['token'] === $token
&& $arg->getOnErrorMessage() === 'Internal error'
&& is_a($query, UpdateInterface::class)
&& !empty($query->getStatement());
}
);
$this->database
->expects(self::once())
->method('doQuery')
->with($callback)
->willReturn(new QueryResult([1]));
$this->authTokenRepository->refreshTokenByUserId($userId, $token);
}
/**
* @throws ConstraintException
* @throws QueryException
*/
public function testDelete()
{
$id = self::$faker->randomNumber();
$callback = new Callback(
static function (QueryData $arg) use ($id) {
$query = $arg->getQuery();
return $query->getBindValues()['id'] === $id
&& is_a($query, DeleteInterface::class)
&& !empty($query->getStatement());
}
);
$this->database->expects(self::once())->method('doQuery')->with($callback);
$this->authTokenRepository->delete($id);
}
public function testGetAll()
{
$callback = new Callback(
static function (QueryData $arg) {
$query = $arg->getQuery();
return $arg->getMapClassName() === AuthToken::class
&& is_a($query, SelectInterface::class)
&& !empty($query->getStatement());
}
);
$this->database
->expects(self::once())
->method('doSelect')
->with($callback);
$this->authTokenRepository->getAll();
}
/**
* @throws ConstraintException
* @throws QueryException
*/
public function testRefreshTokenByUserId()
{
$userId = self::$faker->randomNumber();
$token = self::$faker->sha1();
$callback = new Callback(
static function (QueryData $arg) use ($userId, $token) {
$query = $arg->getQuery();
$params = $query->getBindValues();
return count($params) === 2
&& $params['userId'] === $userId
&& $params['token'] === $token
&& $arg->getOnErrorMessage() === 'Internal error'
&& is_a($query, UpdateInterface::class)
&& !empty($query->getStatement());
}
);
$this->database
->expects(self::once())
->method('doQuery')
->with($callback)
->willReturn(new QueryResult([1]));
$this->authTokenRepository->refreshTokenByUserId($userId, $token);
}
public function testGetTokenByToken()
{
$actionId = self::$faker->randomNumber();
$token = self::$faker->sha1();
$callback = new Callback(
static function (QueryData $arg) use ($actionId, $token) {
$query = $arg->getQuery();
$params = $query->getBindValues();
return count($params) === 2
&& $params['actionId'] === $actionId
&& $params['token'] === $token
&& $arg->getMapClassName() === AuthToken::class
&& is_a($query, SelectInterface::class)
&& !empty($query->getStatement());
}
);
$this->database
->expects(self::once())
->method('doSelect')
->with($callback);
$this->authTokenRepository->getTokenByToken($actionId, $token);
}
/**
* @throws ConstraintException
* @throws DuplicatedItemException
* @throws QueryException
*/
public function testCreate()
{
$authToken = AuthTokenGenerator::factory()->buildAuthToken();
$callbackDuplicate = new Callback(
static function (QueryData $arg) use ($authToken) {
$query = $arg->getQuery();
$params = $query->getBindValues();
return count($params) === 3
&& $params['userId'] === $authToken->getUserId()
&& $params['token'] === $authToken->getToken()
&& $params['actionId'] === $authToken->getActionId()
&& is_a($query, SelectInterface::class)
&& !empty($query->getStatement());
}
);
$callbackUpdate = new Callback(
static function (QueryData $arg) use ($authToken) {
$query = $arg->getQuery();
$params = $query->getBindValues();
return count($params) === 6
&& $params['userId'] === $authToken->getUserId()
&& $params['token'] === $authToken->getToken()
&& $params['createdBy'] === $authToken->getCreatedBy()
&& $params['actionId'] === $authToken->getActionId()
&& $params['hash'] === $authToken->getHash()
&& $params['vault'] === $authToken->getVault()
&& is_a($query, InsertInterface::class)
&& !empty($query->getStatement());
}
);
$this->database
->expects(self::exactly(2))
->method('doQuery')
->with(...self::withConsecutive([$callbackDuplicate], [$callbackUpdate]))
->willReturn(new QueryResult([]), new QueryResult([1]));
$this->authTokenRepository->create($authToken);
}
/**
* @throws ConstraintException
* @throws DuplicatedItemException
* @throws QueryException
*/
public function testCreateWithDuplicate()
{
$authToken = AuthTokenGenerator::factory()->buildAuthToken();
$callback = new Callback(
static function (QueryData $arg) use ($authToken) {
$query = $arg->getQuery();
$params = $query->getBindValues();
return count($params) === 3
&& $params['userId'] === $authToken->getUserId()
&& $params['token'] === $authToken->getToken()
&& $params['actionId'] === $authToken->getActionId()
&& is_a($query, SelectInterface::class)
&& !empty($query->getStatement());
}
);
$this->database
->expects(self::once())
->method('doQuery')
->with($callback)
->willReturn(new QueryResult([1]));
$this->expectException(DuplicatedItemException::class);
$this->expectExceptionMessage('Authorization already exist');
$this->authTokenRepository->create($authToken);
}
protected function setUp(): void
{
parent::setUp();
$this->database = $this->createMock(DatabaseInterface::class);
$queryFactory = new QueryFactory('mysql');
$this->authTokenRepository = new AuthTokenRepository(
$this->database,
$this->context,
$this->application->getEventDispatcher(),
$queryFactory,
);
}
}

View File

@@ -32,7 +32,7 @@ use Exception;
use SP\Core\Context\ContextException;
use SP\Core\Crypt\Hash;
use SP\Core\Crypt\Vault;
use SP\DataModel\AuthTokenData;
use SP\DataModel\AuthToken;
use SP\DataModel\ItemSearchData;
use SP\Domain\Auth\Services\AuthTokenService;
use SP\Domain\Common\Services\ServiceException;
@@ -119,7 +119,7 @@ class AuthTokenServiceTest extends DatabaseTestCase
*/
public function testRefreshAndUpdate()
{
$data = new AuthTokenData();
$data = new AuthToken();
$data->setId(1);
$data->setActionId(AclActionsInterface::ACCOUNT_CREATE);
$data->setCreatedBy(1);
@@ -152,7 +152,7 @@ class AuthTokenServiceTest extends DatabaseTestCase
{
$data = self::$service->getTokenByToken(AclActionsInterface::ACCOUNT_VIEW_PASS, self::AUTH_TOKEN);
$this->assertInstanceOf(AuthTokenData::class, $data);
$this->assertInstanceOf(AuthToken::class, $data);
$this->assertEquals(2, $data->getId());
$this->assertEquals(AclActionsInterface::ACCOUNT_VIEW_PASS, $data->getActionId());
$this->assertTrue(Hash::checkHashKey(self::AUTH_TOKEN_PASS, $data->getHash()));
@@ -176,7 +176,7 @@ class AuthTokenServiceTest extends DatabaseTestCase
*/
public function testUpdate()
{
$data = new AuthTokenData();
$data = new AuthToken();
$data->setId(1);
$data->setActionId(AclActionsInterface::ACCOUNT_CREATE);
$data->setCreatedBy(1);
@@ -187,7 +187,7 @@ class AuthTokenServiceTest extends DatabaseTestCase
$data = self::$service->getTokenByToken(AclActionsInterface::ACCOUNT_CREATE, $data->getToken());
$this->assertInstanceOf(AuthTokenData::class, $data);
$this->assertInstanceOf(AuthToken::class, $data);
$this->assertEquals(AclActionsInterface::ACCOUNT_CREATE, $data->getActionId());
$this->assertTrue(Hash::checkHashKey(self::AUTH_TOKEN_PASS, $data->getHash()));
$this->assertEquals(2, $data->getUserId());
@@ -213,7 +213,7 @@ class AuthTokenServiceTest extends DatabaseTestCase
{
$data = self::$service->getById(1);
$this->assertInstanceOf(AuthTokenData::class, $data);
$this->assertInstanceOf(AuthToken::class, $data);
$this->assertEquals(1, $data->getId());
$this->assertEquals(AclActionsInterface::ACCOUNT_SEARCH, $data->getActionId());
$this->assertEquals(pack('H*', '31326239303237643234656666663762666261636138626437373461346333346234356465333565303333643262313932613838663464666165653563323333'), $data->getToken());
@@ -221,7 +221,7 @@ class AuthTokenServiceTest extends DatabaseTestCase
$data = self::$service->getById(2);
$this->assertInstanceOf(AuthTokenData::class, $data);
$this->assertInstanceOf(AuthToken::class, $data);
$this->assertEquals(2, $data->getId());
$this->assertEquals(AclActionsInterface::ACCOUNT_VIEW_PASS, $data->getActionId());
$this->assertEquals(self::AUTH_TOKEN, $data->getToken());
@@ -275,7 +275,7 @@ class AuthTokenServiceTest extends DatabaseTestCase
*/
public function testCreate()
{
$authTokenData = new AuthTokenData();
$authTokenData = new AuthToken();
$authTokenData->setActionId(AclActionsInterface::ACCOUNT_CREATE);
$authTokenData->setCreatedBy(1);
$authTokenData->setHash(self::AUTH_TOKEN_PASS);
@@ -286,7 +286,7 @@ class AuthTokenServiceTest extends DatabaseTestCase
$data = self::$service->getTokenByToken(AclActionsInterface::ACCOUNT_CREATE, $authTokenData->getToken());
$this->assertInstanceOf(AuthTokenData::class, $data);
$this->assertInstanceOf(AuthToken::class, $data);
$this->assertEquals(AclActionsInterface::ACCOUNT_CREATE, $data->getActionId());
$this->assertTrue(Hash::checkHashKey(self::AUTH_TOKEN_PASS, $data->getHash()));
$this->assertEquals(6, $data->getId());