From 930b1f7139a3fd423c8d99e396ed41b49b1833cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D?= Date: Sun, 27 Nov 2022 16:01:51 +0100 Subject: [PATCH] chore: Create AccountCryptService tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rubén D --- .../Controllers/Account/CreateController.php | 6 +- .../Controllers/Account/EditController.php | 6 +- .../Account/EditPassController.php | 6 +- .../AccountManager/SaveBulkEditController.php | 2 +- .../ConfigEncryption/SaveController.php | 5 +- .../Controllers/Task/TestTaskController.php | 4 +- app/modules/web/Forms/AccountForm.php | 2 +- lib/SP/Core/Crypt/Crypt.php | 134 ++++--- lib/SP/Core/Crypt/CryptInterface.php | 85 ++++ lib/SP/Core/Exceptions/CryptException.php | 33 ++ lib/SP/Core/Messages/TaskMessage.php | 32 +- .../{Services => Dtos}/AccountBulkRequest.php | 13 +- .../{Services => Dtos}/AccountRequest.php | 4 +- .../Domain/Account/Dtos/EncryptedPassword.php | 43 ++ .../Ports/AccountCryptServiceInterface.php | 8 + .../AccountFilterUserInterface.php | 4 +- .../Ports/AccountPresetServiceInterface.php | 2 +- .../Ports/AccountRepositoryInterface.php | 6 +- .../Account/Ports/AccountServiceInterface.php | 19 +- .../Ports/AccountToTagRepositoryInterface.php | 4 +- .../Account/Services/AccountCryptService.php | 114 ++++-- .../Account/Services/AccountFilterUser.php | 1 + .../Services/AccountHistoryService.php | 9 +- .../Services/AccountPasswordRequest.php | 33 +- .../Account/Services/AccountPresetService.php | 1 + .../Account/Services/AccountService.php | 98 ++--- .../Domain/Client/Services/ClientService.php | 6 +- lib/SP/Domain/Common/Adapters/DataModel.php | 23 +- .../Common/Services/ServiceException.php | 3 +- .../Services/UpdateMasterPassRequest.php | 26 +- .../Domain/Import/Services/CsvImportBase.php | 3 +- lib/SP/Domain/Import/Services/ImportTrait.php | 4 +- .../Domain/Import/Services/KeepassImport.php | 8 +- .../Domain/Import/Services/SyspassImport.php | 3 +- lib/SP/Domain/Task/Ports/TaskInterface.php | 89 +++++ lib/SP/Domain/Task/Services/Task.php | 27 +- lib/SP/Domain/Task/Services/TaskFactory.php | 34 +- .../Repositories/AccountHistoryRepository.php | 8 +- .../Repositories/AccountRepository.php | 19 +- .../Repositories/AccountSearchRepository.php | 2 +- .../Repositories/AccountToTagRepository.php | 4 +- .../Account/Adapters/AccountAdapterTest.php | 4 +- .../Services/AccountCryptServiceTest.php | 370 ++++++++++++++++++ tests/SP/Generators/AccountDataGenerator.php | 23 +- .../Repositories/AccountRepositoryTest.php | 2 +- .../AccountToTagRepositoryTest.php | 2 +- .../AccountToTagRepositoryTest.php | 2 +- .../AccountToUserGroupRepositoryTest.php | 2 +- .../AccountToUserRepositoryTest.php | 2 +- .../Services/Account/AccountServiceTest.php | 4 +- tests/SP/Services/Task/TaskServiceTest.php | 12 +- 51 files changed, 989 insertions(+), 367 deletions(-) create mode 100644 lib/SP/Core/Crypt/CryptInterface.php create mode 100644 lib/SP/Core/Exceptions/CryptException.php rename lib/SP/Domain/Account/{Services => Dtos}/AccountBulkRequest.php (84%) rename lib/SP/Domain/Account/{Services => Dtos}/AccountRequest.php (98%) create mode 100644 lib/SP/Domain/Account/Dtos/EncryptedPassword.php rename lib/SP/Domain/Account/{Services => Ports}/AccountFilterUserInterface.php (97%) create mode 100644 lib/SP/Domain/Task/Ports/TaskInterface.php create mode 100644 tests/SP/Domain/Account/Services/AccountCryptServiceTest.php diff --git a/app/modules/api/Controllers/Account/CreateController.php b/app/modules/api/Controllers/Account/CreateController.php index 29630771..24359dbd 100644 --- a/app/modules/api/Controllers/Account/CreateController.php +++ b/app/modules/api/Controllers/Account/CreateController.php @@ -29,7 +29,7 @@ use Exception; use SP\Core\Acl\ActionsInterface; use SP\Core\Events\Event; use SP\Core\Events\EventMessage; -use SP\Domain\Account\Services\AccountRequest; +use SP\Domain\Account\Dtos\AccountRequest; use SP\Domain\Api\Services\ApiResponse; /** @@ -74,7 +74,7 @@ final class CreateController extends AccountBase } /** - * @return \SP\Domain\Account\Services\AccountRequest + * @return \SP\Domain\Account\Dtos\AccountRequest * @throws \SP\Domain\Common\Services\ServiceException */ private function buildAccountRequest(): AccountRequest @@ -103,4 +103,4 @@ final class CreateController extends AccountBase return $accountRequest; } -} \ No newline at end of file +} diff --git a/app/modules/api/Controllers/Account/EditController.php b/app/modules/api/Controllers/Account/EditController.php index 87f5f192..8d743b32 100644 --- a/app/modules/api/Controllers/Account/EditController.php +++ b/app/modules/api/Controllers/Account/EditController.php @@ -28,7 +28,7 @@ use Exception; use SP\Core\Acl\ActionsInterface; use SP\Core\Events\Event; use SP\Core\Events\EventMessage; -use SP\Domain\Account\Services\AccountRequest; +use SP\Domain\Account\Dtos\AccountRequest; use SP\Domain\Api\Services\ApiResponse; /** @@ -73,7 +73,7 @@ final class EditController extends AccountBase } /** - * @return \SP\Domain\Account\Services\AccountRequest + * @return \SP\Domain\Account\Dtos\AccountRequest * @throws \SP\Domain\Common\Services\ServiceException */ private function buildAccountRequest(): AccountRequest @@ -106,4 +106,4 @@ final class EditController extends AccountBase return $accountRequest; } -} \ No newline at end of file +} diff --git a/app/modules/api/Controllers/Account/EditPassController.php b/app/modules/api/Controllers/Account/EditPassController.php index a0007686..8162c4a4 100644 --- a/app/modules/api/Controllers/Account/EditPassController.php +++ b/app/modules/api/Controllers/Account/EditPassController.php @@ -28,7 +28,7 @@ use Exception; use SP\Core\Acl\ActionsInterface; use SP\Core\Events\Event; use SP\Core\Events\EventMessage; -use SP\Domain\Account\Services\AccountRequest; +use SP\Domain\Account\Dtos\AccountRequest; use SP\Domain\Api\Services\ApiResponse; /** @@ -75,7 +75,7 @@ final class EditPassController extends AccountBase } /** - * @return \SP\Domain\Account\Services\AccountRequest + * @return \SP\Domain\Account\Dtos\AccountRequest * @throws \SP\Domain\Common\Services\ServiceException */ private function buildAccountRequest(): AccountRequest @@ -88,4 +88,4 @@ final class EditPassController extends AccountBase return $accountRequest; } -} \ No newline at end of file +} diff --git a/app/modules/web/Controllers/AccountManager/SaveBulkEditController.php b/app/modules/web/Controllers/AccountManager/SaveBulkEditController.php index e5aa8ee5..a06d9dda 100644 --- a/app/modules/web/Controllers/AccountManager/SaveBulkEditController.php +++ b/app/modules/web/Controllers/AccountManager/SaveBulkEditController.php @@ -29,10 +29,10 @@ use SP\Core\Acl\ActionsInterface; use SP\Core\Application; use SP\Core\Events\Event; use SP\Core\Events\EventMessage; +use SP\Domain\Account\Dtos\AccountBulkRequest; use SP\Domain\Account\Ports\AccountHistoryServiceInterface; use SP\Domain\Account\Ports\AccountPresetServiceInterface; use SP\Domain\Account\Ports\AccountServiceInterface; -use SP\Domain\Account\Services\AccountBulkRequest; use SP\Http\JsonResponse; use SP\Modules\Web\Controllers\ControllerBase; use SP\Modules\Web\Controllers\Traits\JsonTrait; diff --git a/app/modules/web/Controllers/ConfigEncryption/SaveController.php b/app/modules/web/Controllers/ConfigEncryption/SaveController.php index abde9744..febc71d1 100644 --- a/app/modules/web/Controllers/ConfigEncryption/SaveController.php +++ b/app/modules/web/Controllers/ConfigEncryption/SaveController.php @@ -35,6 +35,7 @@ use SP\Domain\Config\Ports\ConfigServiceInterface; use SP\Domain\Crypt\Ports\MasterPassServiceInterface; use SP\Domain\Crypt\Services\MasterPassService; use SP\Domain\Crypt\Services\UpdateMasterPassRequest; +use SP\Domain\Task\Ports\TaskInterface; use SP\Domain\Task\Services\Task; use SP\Domain\Task\Services\TaskFactory; use SP\Http\JsonResponse; @@ -191,12 +192,12 @@ final class SaveController extends SimpleControllerBase /** * @throws \SP\Infrastructure\File\FileException */ - private function getTask(): ?Task + private function getTask(): ?TaskInterface { $taskId = $this->request->analyzeString('taskId'); return $taskId !== null - ? TaskFactory::create(__FUNCTION__, $taskId) + ? TaskFactory::register(new Task(__FUNCTION__, $taskId)) : null; } diff --git a/app/modules/web/Controllers/Task/TestTaskController.php b/app/modules/web/Controllers/Task/TestTaskController.php index cde8adff..a3df1477 100644 --- a/app/modules/web/Controllers/Task/TestTaskController.php +++ b/app/modules/web/Controllers/Task/TestTaskController.php @@ -40,7 +40,7 @@ final class TestTaskController */ public function testTaskAction(string $taskId): void { - $task = TaskFactory::create($taskId, $taskId); + $task = TaskFactory::register($taskId, $taskId); echo $task->getTaskId(); @@ -55,4 +55,4 @@ final class TestTaskController TaskFactory::end($task); } -} \ No newline at end of file +} diff --git a/app/modules/web/Forms/AccountForm.php b/app/modules/web/Forms/AccountForm.php index 1c0d532b..1bc563a4 100644 --- a/app/modules/web/Forms/AccountForm.php +++ b/app/modules/web/Forms/AccountForm.php @@ -30,8 +30,8 @@ use SP\Core\Exceptions\ConstraintException; use SP\Core\Exceptions\NoSuchPropertyException; use SP\Core\Exceptions\QueryException; use SP\Core\Exceptions\ValidationException; +use SP\Domain\Account\Dtos\AccountRequest; use SP\Domain\Account\Ports\AccountPresetServiceInterface; -use SP\Domain\Account\Services\AccountRequest; use SP\Http\RequestInterface; /** diff --git a/lib/SP/Core/Crypt/Crypt.php b/lib/SP/Core/Crypt/Crypt.php index da055171..56269191 100644 --- a/lib/SP/Core/Crypt/Crypt.php +++ b/lib/SP/Core/Crypt/Crypt.php @@ -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. * @@ -28,91 +28,116 @@ use Defuse\Crypto\Crypto; use Defuse\Crypto\Exception\CryptoException; use Defuse\Crypto\Key; use Defuse\Crypto\KeyProtectedByPassword; +use SP\Core\Exceptions\CryptException; +use SP\Core\Exceptions\SPException; /** * Class Crypt * * @package SP\Core\Crypt */ -class Crypt +class Crypt implements CryptInterface { + /** + * Securiza una clave de seguridad + * + * @param string $password + * @param bool $useAscii + * + * @return string|KeyProtectedByPassword + * @throws \SP\Core\Exceptions\CryptException + * @TODO: Update callers to use instance + */ + public function makeSecuredKey( + string $password, + bool $useAscii = true + ): KeyProtectedByPassword|string { + try { + if ($useAscii) { + return KeyProtectedByPassword::createRandomPasswordProtectedKey($password)->saveToAsciiSafeString(); + } + + return KeyProtectedByPassword::createRandomPasswordProtectedKey($password); + } catch (CryptoException $e) { + throw new CryptException($e->getMessage(), SPException::ERROR, null, $e->getCode(), $e); + } + } + /** * Encriptar datos con una clave segura * - * @param string $data - * @param string|Key $securedKey - * @param string|null $password + * @param string $data + * @param string|Key $securedKey + * @param string|null $password * * @return string - * @throws \Defuse\Crypto\Exception\BadFormatException - * @throws \Defuse\Crypto\Exception\CryptoException - * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException + * @throws \SP\Core\Exceptions\CryptException + * + * @TODO: Update callers to use instance */ - public static function encrypt( - string $data, - $securedKey, + public function encrypt( + string $data, + Key|string $securedKey, ?string $password = null - ): string - { + ): string { try { if ($securedKey instanceof Key) { $key = $securedKey; } elseif (null !== $password) { - $key = self::unlockSecuredKey($securedKey, $password, false); + $key = $this->unlockSecuredKey($securedKey, $password, false); } else { $key = Key::loadFromAsciiSafeString($securedKey); } return Crypto::encrypt($data, $key); } catch (CryptoException $e) { - logger($e->getMessage()); - - throw $e; + throw new CryptException($e->getMessage(), SPException::ERROR, null, $e->getCode(), $e); } } /** + * @param string $key + * @param string $password + * @param bool $useAscii + * * @return string|Key - * @throws CryptoException + * @throws \SP\Core\Exceptions\CryptException + * @TODO: Update callers to use instance */ - public static function unlockSecuredKey( + public function unlockSecuredKey( string $key, string $password, - bool $useAscii = true - ) - { + bool $useAscii = true + ): Key|string { try { if ($useAscii) { - return KeyProtectedByPassword::loadFromAsciiSafeString($key)->unlockKey($password)->saveToAsciiSafeString(); + return KeyProtectedByPassword::loadFromAsciiSafeString($key) + ->unlockKey($password) + ->saveToAsciiSafeString(); } return KeyProtectedByPassword::loadFromAsciiSafeString($key)->unlockKey($password); } catch (CryptoException $e) { - logger($e->getMessage()); - - throw $e; + throw new CryptException($e->getMessage(), SPException::ERROR, null, $e->getCode(), $e); } } /** * Desencriptar datos con una clave segura * - * @param string $data - * @param string|Key|KeyProtectedByPassword $securedKey - * @param string|null $password + * @param string $data + * @param string|Key|KeyProtectedByPassword $securedKey + * @param string|null $password * * @return string - * @throws \Defuse\Crypto\Exception\BadFormatException - * @throws \Defuse\Crypto\Exception\CryptoException - * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException - * @throws \Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException + * @throws \SP\Core\Exceptions\CryptException + * @TODO: Update callers to use instance */ - public static function decrypt( - string $data, - $securedKey, + public function decrypt( + string $data, + Key|KeyProtectedByPassword|string $securedKey, ?string $password = null - ): string - { + ): string { try { if ($securedKey instanceof Key) { return Crypto::decrypt($data, $securedKey); @@ -123,39 +148,12 @@ class Crypt return Crypto::decrypt($data, $securedKey->unlockKey($password)); } - return Crypto::decrypt($data, self::unlockSecuredKey($securedKey, $password, false)); + return Crypto::decrypt($data, $this->unlockSecuredKey($securedKey, $password, false)); } return Crypto::decrypt($data, Key::loadFromAsciiSafeString($securedKey)); } catch (CryptoException $e) { - logger($e->getMessage()); - - throw $e; - } - - } - - /** - * Securiza una clave de seguridad - * - * @return string|KeyProtectedByPassword - * @throws CryptoException - */ - public static function makeSecuredKey( - string $password, - bool $useAscii = true - ) - { - try { - if ($useAscii) { - return KeyProtectedByPassword::createRandomPasswordProtectedKey($password)->saveToAsciiSafeString(); - } - - return KeyProtectedByPassword::createRandomPasswordProtectedKey($password); - } catch (CryptoException $e) { - logger($e->getMessage()); - - throw $e; + throw new CryptException($e->getMessage(), SPException::ERROR, null, $e->getCode(), $e); } } -} \ No newline at end of file +} diff --git a/lib/SP/Core/Crypt/CryptInterface.php b/lib/SP/Core/Crypt/CryptInterface.php new file mode 100644 index 00000000..ae9af0ee --- /dev/null +++ b/lib/SP/Core/Crypt/CryptInterface.php @@ -0,0 +1,85 @@ +. + */ + +namespace SP\Core\Crypt; + +use Defuse\Crypto\Key; +use Defuse\Crypto\KeyProtectedByPassword; + +/** + * Class Crypt + * + * @package SP\Core\Crypt + */ +interface CryptInterface +{ + /** + * Securiza una clave de seguridad + * + * @param string $password + * @param bool $useAscii + * + * @return string|KeyProtectedByPassword + * @throws \SP\Core\Exceptions\CryptException + */ + public function makeSecuredKey(string $password, bool $useAscii = true): KeyProtectedByPassword|string; + + /** + * Encriptar datos con una clave segura + * + * @param string $data + * @param string|Key $securedKey + * @param string|null $password + * + * @return string + * @throws \SP\Core\Exceptions\CryptException + */ + public function encrypt(string $data, Key|string $securedKey, ?string $password = null): string; + + /** + * @param string $key + * @param string $password + * @param bool $useAscii + * + * @return string|Key + * @throws \SP\Core\Exceptions\CryptException + */ + public function unlockSecuredKey(string $key, string $password, bool $useAscii = true): Key|string; + + /** + * Desencriptar datos con una clave segura + * + * @param string $data + * @param string|Key|KeyProtectedByPassword $securedKey + * @param string|null $password + * + * @return string + * @throws \SP\Core\Exceptions\CryptException + */ + public function decrypt( + string $data, + Key|KeyProtectedByPassword|string $securedKey, + ?string $password = null + ): string; +} diff --git a/lib/SP/Core/Exceptions/CryptException.php b/lib/SP/Core/Exceptions/CryptException.php new file mode 100644 index 00000000..7f6b178f --- /dev/null +++ b/lib/SP/Core/Exceptions/CryptException.php @@ -0,0 +1,33 @@ +. + */ + +namespace SP\Core\Exceptions; + +/** + * Class CryptException + */ +final class CryptException extends SPException +{ + +} diff --git a/lib/SP/Core/Messages/TaskMessage.php b/lib/SP/Core/Messages/TaskMessage.php index 009423a7..b660e70f 100644 --- a/lib/SP/Core/Messages/TaskMessage.php +++ b/lib/SP/Core/Messages/TaskMessage.php @@ -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. * @@ -33,18 +33,12 @@ use JsonSerializable; */ final class TaskMessage implements MessageInterface, JsonSerializable { - protected string $taskId; - protected string $task; - protected ?string $message = null; - protected int $time = 0; - protected int $progress = 0; - protected int $end = 0; + protected ?string $message = null; + protected int $time = 0; + protected int $progress = 0; + protected int $end = 0; - public function __construct(string $taskId, string $task) - { - $this->taskId = $taskId; - $this->task = $task; - } + public function __construct(private string $taskId, private string $task) {} public function getTask(): string { @@ -120,12 +114,12 @@ final class TaskMessage implements MessageInterface, JsonSerializable public function composeText(string $delimiter = ';'): string { return implode($delimiter, [ - 'taskId' => $this->taskId, - 'task' => $this->task, - 'message' => $this->message, - 'time' => $this->time, + 'taskId' => $this->taskId, + 'task' => $this->task, + 'message' => $this->message, + 'time' => $this->time, 'progress' => $this->progress, - 'end' => $this->end + 'end' => $this->end, ]); } @@ -134,7 +128,7 @@ final class TaskMessage implements MessageInterface, JsonSerializable * * @throws \JsonException */ - public function composeJson() + public function composeJson(): bool|string { return json_encode($this, JSON_THROW_ON_ERROR); } @@ -163,4 +157,4 @@ final class TaskMessage implements MessageInterface, JsonSerializable return $this; } -} \ No newline at end of file +} diff --git a/lib/SP/Domain/Account/Services/AccountBulkRequest.php b/lib/SP/Domain/Account/Dtos/AccountBulkRequest.php similarity index 84% rename from lib/SP/Domain/Account/Services/AccountBulkRequest.php rename to lib/SP/Domain/Account/Dtos/AccountBulkRequest.php index e49a9137..ce154244 100644 --- a/lib/SP/Domain/Account/Services/AccountBulkRequest.php +++ b/lib/SP/Domain/Account/Dtos/AccountBulkRequest.php @@ -22,7 +22,7 @@ * along with sysPass. If not, see . */ -namespace SP\Domain\Account\Services; +namespace SP\Domain\Account\Dtos; /** * Class AccountBulkRequest @@ -31,9 +31,7 @@ namespace SP\Domain\Account\Services; */ final class AccountBulkRequest { - private array $itemsId; - private AccountRequest $accountRequest; - private bool $deleteHistory = false; + private bool $deleteHistory = false; /** * AccountBulkRequest constructor. @@ -41,11 +39,8 @@ final class AccountBulkRequest * @param int[] $itemsId * @param AccountRequest $accountRequest */ - public function __construct(array $itemsId, AccountRequest $accountRequest) + public function __construct(private array $itemsId, private AccountRequest $accountRequest) { - $this->itemsId = $itemsId; - $this->accountRequest = $accountRequest; - $this->setUp(); } @@ -77,4 +72,4 @@ final class AccountBulkRequest return $request; } -} \ No newline at end of file +} diff --git a/lib/SP/Domain/Account/Services/AccountRequest.php b/lib/SP/Domain/Account/Dtos/AccountRequest.php similarity index 98% rename from lib/SP/Domain/Account/Services/AccountRequest.php rename to lib/SP/Domain/Account/Dtos/AccountRequest.php index 1a792c06..cac883fb 100644 --- a/lib/SP/Domain/Account/Services/AccountRequest.php +++ b/lib/SP/Domain/Account/Dtos/AccountRequest.php @@ -22,7 +22,7 @@ * along with sysPass. If not, see . */ -namespace SP\Domain\Account\Services; +namespace SP\Domain\Account\Dtos; /** * Class AccountRequest @@ -58,4 +58,4 @@ final class AccountRequest public ?bool $changeUserGroup = false; public ?bool $changePermissions = false; public ?bool $updateTags = false; -} \ No newline at end of file +} diff --git a/lib/SP/Domain/Account/Dtos/EncryptedPassword.php b/lib/SP/Domain/Account/Dtos/EncryptedPassword.php new file mode 100644 index 00000000..e5ab90c4 --- /dev/null +++ b/lib/SP/Domain/Account/Dtos/EncryptedPassword.php @@ -0,0 +1,43 @@ +. + */ + +namespace SP\Domain\Account\Dtos; + +/** + * Class EncryptedPassword + */ +final class EncryptedPassword +{ + public function __construct(private string $pass, private string $key) {} + + public function getPass(): string + { + return $this->pass; + } + + public function getKey(): string + { + return $this->key; + } +} diff --git a/lib/SP/Domain/Account/Ports/AccountCryptServiceInterface.php b/lib/SP/Domain/Account/Ports/AccountCryptServiceInterface.php index 6abfb0f3..0ff9a3b8 100644 --- a/lib/SP/Domain/Account/Ports/AccountCryptServiceInterface.php +++ b/lib/SP/Domain/Account/Ports/AccountCryptServiceInterface.php @@ -24,6 +24,7 @@ namespace SP\Domain\Account\Ports; +use SP\Domain\Account\Dtos\EncryptedPassword; use SP\Domain\Common\Services\ServiceException; use SP\Domain\Crypt\Services\UpdateMasterPassRequest; @@ -47,4 +48,11 @@ interface AccountCryptServiceInterface * @throws \SP\Domain\Common\Services\ServiceException */ public function updateHistoryMasterPassword(UpdateMasterPassRequest $updateMasterPassRequest): void; + + /** + * Devolver los datos de la clave encriptados + * + * @throws \SP\Domain\Common\Services\ServiceException + */ + public function getPasswordEncrypted(string $pass, ?string $masterPass = null): EncryptedPassword; } diff --git a/lib/SP/Domain/Account/Services/AccountFilterUserInterface.php b/lib/SP/Domain/Account/Ports/AccountFilterUserInterface.php similarity index 97% rename from lib/SP/Domain/Account/Services/AccountFilterUserInterface.php rename to lib/SP/Domain/Account/Ports/AccountFilterUserInterface.php index 7e1f250f..dd4fe5fb 100644 --- a/lib/SP/Domain/Account/Services/AccountFilterUserInterface.php +++ b/lib/SP/Domain/Account/Ports/AccountFilterUserInterface.php @@ -22,7 +22,7 @@ * along with sysPass. If not, see . */ -namespace SP\Domain\Account\Services; +namespace SP\Domain\Account\Ports; use Aura\SqlQuery\Common\SelectInterface; @@ -40,4 +40,4 @@ interface AccountFilterUserInterface * Devuelve el filtro para la consulta SQL de cuentas que un usuario puede acceder */ public function buildFilter(bool $useGlobalSearch = false, ?SelectInterface $query = null): SelectInterface; -} \ No newline at end of file +} diff --git a/lib/SP/Domain/Account/Ports/AccountPresetServiceInterface.php b/lib/SP/Domain/Account/Ports/AccountPresetServiceInterface.php index 160e7f28..3282f5e6 100644 --- a/lib/SP/Domain/Account/Ports/AccountPresetServiceInterface.php +++ b/lib/SP/Domain/Account/Ports/AccountPresetServiceInterface.php @@ -28,7 +28,7 @@ use SP\Core\Exceptions\ConstraintException; use SP\Core\Exceptions\NoSuchPropertyException; use SP\Core\Exceptions\QueryException; use SP\Core\Exceptions\ValidationException; -use SP\Domain\Account\Services\AccountRequest; +use SP\Domain\Account\Dtos\AccountRequest; /** * Class AccountPreset diff --git a/lib/SP/Domain/Account/Ports/AccountRepositoryInterface.php b/lib/SP/Domain/Account/Ports/AccountRepositoryInterface.php index dec440b6..bb796123 100644 --- a/lib/SP/Domain/Account/Ports/AccountRepositoryInterface.php +++ b/lib/SP/Domain/Account/Ports/AccountRepositoryInterface.php @@ -29,8 +29,8 @@ use SP\Core\Exceptions\QueryException; use SP\Core\Exceptions\SPException; use SP\DataModel\AccountHistoryData; use SP\DataModel\ItemSearchData; +use SP\Domain\Account\Dtos\AccountRequest; use SP\Domain\Account\Services\AccountPasswordRequest; -use SP\Domain\Account\Services\AccountRequest; use SP\Domain\Common\Adapters\SimpleModel; use SP\Domain\Common\Ports\RepositoryInterface; use SP\Infrastructure\Database\QueryResult; @@ -82,7 +82,7 @@ interface AccountRepositoryInterface extends RepositoryInterface /** * Actualiza la clave de una cuenta en la BBDD. * - * @param AccountRequest $accountRequest + * @param \SP\Domain\Account\Dtos\AccountRequest $accountRequest * * @return int * @throws ConstraintException @@ -175,7 +175,7 @@ interface AccountRepositoryInterface extends RepositoryInterface /** * Crea una nueva cuenta en la BBDD * - * @param AccountRequest $accountRequest + * @param \SP\Domain\Account\Dtos\AccountRequest $accountRequest * * @return int * @throws ConstraintException diff --git a/lib/SP/Domain/Account/Ports/AccountServiceInterface.php b/lib/SP/Domain/Account/Ports/AccountServiceInterface.php index f1153aeb..4b02f003 100644 --- a/lib/SP/Domain/Account/Ports/AccountServiceInterface.php +++ b/lib/SP/Domain/Account/Ports/AccountServiceInterface.php @@ -34,9 +34,9 @@ use SP\DataModel\Dto\AccountEnrichedDto; use SP\DataModel\ItemSearchData; use SP\Domain\Account\Adapters\AccountData; use SP\Domain\Account\Adapters\AccountPassData; -use SP\Domain\Account\Services\AccountBulkRequest; +use SP\Domain\Account\Dtos\AccountBulkRequest; +use SP\Domain\Account\Dtos\AccountRequest; use SP\Domain\Account\Services\AccountPasswordRequest; -use SP\Domain\Account\Services\AccountRequest; use SP\Domain\Common\Services\ServiceException; use SP\Infrastructure\Common\Repositories\NoSuchItemException; use SP\Infrastructure\Database\QueryResult; @@ -102,13 +102,6 @@ interface AccountServiceInterface */ public function create(AccountRequest $accountRequest): int; - /** - * Devolver los datos de la clave encriptados - * - * @throws \SP\Domain\Common\Services\ServiceException - */ - public function getPasswordEncrypted(string $pass, ?string $masterPass = null): array; - /** * @throws QueryException * @throws NoSuchItemException @@ -119,7 +112,7 @@ interface AccountServiceInterface /** * Updates external items for the account * - * @param \SP\Domain\Account\Services\AccountRequest $accountRequest + * @param \SP\Domain\Account\Dtos\AccountRequest $accountRequest * * @throws \SP\Domain\Common\Services\ServiceException */ @@ -128,14 +121,14 @@ interface AccountServiceInterface /** * Update accounts in bulk mode * - * @param \SP\Domain\Account\Services\AccountBulkRequest $request + * @param \SP\Domain\Account\Dtos\AccountBulkRequest $request * * @throws \SP\Domain\Common\Services\ServiceException */ public function updateBulk(AccountBulkRequest $request): void; /** - * @param \SP\Domain\Account\Services\AccountRequest $accountRequest + * @param \SP\Domain\Account\Dtos\AccountRequest $accountRequest * * @throws \SP\Domain\Common\Services\ServiceException */ @@ -147,7 +140,7 @@ interface AccountServiceInterface * @throws ConstraintException * @throws QueryException */ - public function updatePasswordMasterPass(AccountPasswordRequest $accountRequest): bool; + public function updatePasswordMasterPass(AccountPasswordRequest $accountRequest): void; /** * @param int $historyId diff --git a/lib/SP/Domain/Account/Ports/AccountToTagRepositoryInterface.php b/lib/SP/Domain/Account/Ports/AccountToTagRepositoryInterface.php index 87b57fa6..b53e8b00 100644 --- a/lib/SP/Domain/Account/Ports/AccountToTagRepositoryInterface.php +++ b/lib/SP/Domain/Account/Ports/AccountToTagRepositoryInterface.php @@ -26,7 +26,7 @@ namespace SP\Domain\Account\Ports; use SP\Core\Exceptions\ConstraintException; use SP\Core\Exceptions\QueryException; -use SP\Domain\Account\Services\AccountRequest; +use SP\Domain\Account\Dtos\AccountRequest; use SP\Domain\Common\Ports\RepositoryInterface; use SP\Infrastructure\Database\QueryResult; @@ -62,7 +62,7 @@ interface AccountToTagRepositoryInterface extends RepositoryInterface /** * Actualizar las etiquetas de una cuenta * - * @param AccountRequest $accountRequest + * @param \SP\Domain\Account\Dtos\AccountRequest $accountRequest * * @throws ConstraintException * @throws QueryException diff --git a/lib/SP/Domain/Account/Services/AccountCryptService.php b/lib/SP/Domain/Account/Services/AccountCryptService.php index 0482f790..daa6b838 100644 --- a/lib/SP/Domain/Account/Services/AccountCryptService.php +++ b/lib/SP/Domain/Account/Services/AccountCryptService.php @@ -24,13 +24,14 @@ namespace SP\Domain\Account\Services; -use Defuse\Crypto\Exception\CryptoException; use Exception; use SP\Core\Application; -use SP\Core\Crypt\Crypt; +use SP\Core\Crypt\CryptInterface; use SP\Core\Events\Event; use SP\Core\Events\EventMessage; +use SP\Core\Exceptions\CryptException; use SP\Core\Exceptions\SPException; +use SP\Domain\Account\Dtos\EncryptedPassword; use SP\Domain\Account\Ports\AccountCryptServiceInterface; use SP\Domain\Account\Ports\AccountHistoryServiceInterface; use SP\Domain\Account\Ports\AccountServiceInterface; @@ -39,6 +40,9 @@ use SP\Domain\Common\Services\ServiceException; use SP\Domain\Crypt\Services\UpdateMasterPassRequest; use SP\Domain\Task\Services\TaskFactory; use SP\Util\Util; +use function SP\__; +use function SP\__u; +use function SP\logger; /** * Class AccountCryptService @@ -47,19 +51,13 @@ use SP\Util\Util; */ final class AccountCryptService extends Service implements AccountCryptServiceInterface { - private AccountService $accountService; - private AccountHistoryService $accountHistoryService; - private ?UpdateMasterPassRequest $request = null; - public function __construct( Application $application, - AccountServiceInterface $accountService, - AccountHistoryServiceInterface $accountHistoryService + private AccountServiceInterface $accountService, + private AccountHistoryServiceInterface $accountHistoryService, + private CryptInterface $crypt ) { parent::__construct($application); - - $this->accountService = $accountService; - $this->accountHistoryService = $accountHistoryService; } /** @@ -69,8 +67,6 @@ final class AccountCryptService extends Service implements AccountCryptServiceIn */ public function updateMasterPassword(UpdateMasterPassRequest $updateMasterPassRequest): void { - $this->request = $updateMasterPassRequest; - try { $this->eventDispatcher->notifyEvent( 'update.masterPassword.accounts.start', @@ -81,9 +77,9 @@ final class AccountCryptService extends Service implements AccountCryptServiceIn ) ); - if ($this->request->useTask()) { - $task = $this->request->getTask(); + $task = $updateMasterPassRequest->getTask(); + if (null !== $task) { TaskFactory::update( $task, TaskFactory::createMessage( @@ -97,7 +93,8 @@ final class AccountCryptService extends Service implements AccountCryptServiceIn $this->accountService->getAccountsPassData(), function (AccountPasswordRequest $request) { $this->accountService->updatePasswordMasterPass($request); - } + }, + $updateMasterPassRequest ); $this->eventDispatcher->notifyEvent( @@ -119,7 +116,8 @@ final class AccountCryptService extends Service implements AccountCryptServiceIn private function processAccounts( array $accounts, - callable $passUpdater + callable $passUpdater, + UpdateMasterPassRequest $updateMasterPassRequest ): EventMessage { set_time_limit(0); @@ -140,11 +138,9 @@ final class AccountCryptService extends Service implements AccountCryptServiceIn } $configData = $this->config->getConfigData(); - $currentMasterPassHash = $this->request->getCurrentHash(); + $currentMasterPassHash = $updateMasterPassRequest->getCurrentHash(); - if ($this->request->useTask()) { - $task = $this->request->getTask(); - } + $task = $updateMasterPassRequest->getTask(); foreach ($accounts as $account) { // No realizar cambios si está en modo demo @@ -156,13 +152,13 @@ final class AccountCryptService extends Service implements AccountCryptServiceIn if ($counter % 100 === 0) { $eta = Util::getETA($startTime, $counter, $numAccounts); - if (isset($task)) { + if (null !== $task) { $taskMessage = TaskFactory::createMessage( $task->getTaskId(), __('Update Master Password') - )->setMessage(sprintf(__('Accounts updated: %d / %d'), $counter, $numAccounts)) - ->setProgress(round(($counter * 100) / $numAccounts, 2)) - ->setTime(sprintf('ETA: %ds (%.2f/s)', $eta[0], $eta[1])); + )->setMessage( + sprintf(__('Accounts updated: %d / %d - ETA: %ds (%.2f/s)'), $counter, $numAccounts, ...$eta) + )->setProgress(round(($counter * 100) / $numAccounts, 2)); TaskFactory::update($task, $taskMessage); @@ -187,24 +183,30 @@ final class AccountCryptService extends Service implements AccountCryptServiceIn continue; } - $request = new AccountPasswordRequest(); - $request->id = $account->id; - try { - $passData = $this->accountService->getPasswordEncrypted( - Crypt::decrypt($account->pass, $account->key, $this->request->getCurrentMasterPass()), - $this->request->getNewMasterPass() + $encryptedPassword = $this->getPasswordEncrypted( + $this->crypt->decrypt( + $account->pass, + $account->key, + $updateMasterPassRequest->getCurrentMasterPass() + ), + $updateMasterPassRequest->getNewMasterPass() ); - $request->key = $passData['key']; - $request->pass = $passData['pass']; + $request = new AccountPasswordRequest( + $account->id, + $encryptedPassword, + $updateMasterPassRequest->getHash() + ); // Call the specific updater $passUpdater($request); $accountsOk[] = $account->id; $counter++; - } catch (SPException|CryptoException $e) { + } catch (SPException $e) { + $this->eventDispatcher->notifyEvent('exception', new Event($e)); + $errorCount++; $eventMessage->addDescription(__u('Error while updating the account\'s password')); @@ -218,6 +220,39 @@ final class AccountCryptService extends Service implements AccountCryptServiceIn return $eventMessage; } + /** + * Devolver los datos de la clave encriptados + * + * @throws \SP\Domain\Common\Services\ServiceException + */ + public function getPasswordEncrypted(string $pass, ?string $masterPass = null): EncryptedPassword + { + try { + if ($masterPass === null) { + $masterPass = $this->getMasterKeyFromContext(); + } + + if (empty($masterPass)) { + throw new ServiceException(__u('Master password not set')); + } + + $key = $this->crypt->makeSecuredKey($masterPass); + + $encryptedPassword = new EncryptedPassword( + $this->crypt->encrypt($pass, $key, $masterPass), + $key + ); + + if (strlen($encryptedPassword->getPass()) > 1000 || strlen($encryptedPassword->getKey()) > 1000) { + throw new ServiceException(__u('Internal error')); + } + + return $encryptedPassword; + } catch (CryptException $e) { + throw new ServiceException(__u('Internal error'), SPException::ERROR, null, $e->getCode(), $e); + } + } + /** * Actualiza las claves de todas las cuentas con la nueva clave maestra. * @@ -226,8 +261,6 @@ final class AccountCryptService extends Service implements AccountCryptServiceIn public function updateHistoryMasterPassword( UpdateMasterPassRequest $updateMasterPassRequest ): void { - $this->request = $updateMasterPassRequest; - try { $this->eventDispatcher->notifyEvent( 'update.masterPassword.accountsHistory.start', @@ -238,9 +271,9 @@ final class AccountCryptService extends Service implements AccountCryptServiceIn ) ); - if ($this->request->useTask()) { - $task = $this->request->getTask(); + $task = $updateMasterPassRequest->getTask(); + if (null !== $task) { TaskFactory::update( $task, TaskFactory::createMessage( @@ -253,10 +286,9 @@ final class AccountCryptService extends Service implements AccountCryptServiceIn $eventMessage = $this->processAccounts( $this->accountHistoryService->getAccountsPassData(), function (AccountPasswordRequest $request) { - $request->hash = $this->request->getHash(); - $this->accountHistoryService->updatePasswordMasterPass($request); - } + }, + $updateMasterPassRequest ); $this->eventDispatcher->notifyEvent( diff --git a/lib/SP/Domain/Account/Services/AccountFilterUser.php b/lib/SP/Domain/Account/Services/AccountFilterUser.php index 5577597f..5c2baff8 100644 --- a/lib/SP/Domain/Account/Services/AccountFilterUser.php +++ b/lib/SP/Domain/Account/Services/AccountFilterUser.php @@ -28,6 +28,7 @@ use Aura\SqlQuery\Common\SelectInterface; use Aura\SqlQuery\QueryFactory; use SP\Core\Context\ContextInterface; use SP\DataModel\ProfileData; +use SP\Domain\Account\Ports\AccountFilterUserInterface; use SP\Domain\Account\Search\AccountSearchConstants; use SP\Domain\Config\Ports\ConfigDataInterface; use SP\Domain\User\Services\UserLoginResponse; diff --git a/lib/SP/Domain/Account/Services/AccountHistoryService.php b/lib/SP/Domain/Account/Services/AccountHistoryService.php index a668e046..49baa3f5 100644 --- a/lib/SP/Domain/Account/Services/AccountHistoryService.php +++ b/lib/SP/Domain/Account/Services/AccountHistoryService.php @@ -27,7 +27,6 @@ namespace SP\Domain\Account\Services; use SP\Core\Application; use SP\Core\Exceptions\ConstraintException; use SP\Core\Exceptions\QueryException; -use SP\Core\Exceptions\SPException; use SP\DataModel\AccountHistoryData; use SP\DataModel\Dto\AccountHistoryCreateDto; use SP\DataModel\ItemData; @@ -40,6 +39,7 @@ use SP\Domain\Common\Services\Service; use SP\Domain\Common\Services\ServiceException; use SP\Infrastructure\Common\Repositories\NoSuchItemException; use SP\Infrastructure\Database\QueryResult; +use function SP\__u; /** * Class AccountHistoryService @@ -208,13 +208,14 @@ final class AccountHistoryService extends Service implements AccountHistoryServi } /** - * @throws SPException - * @throws ConstraintException + * @param \SP\Domain\Account\Services\AccountPasswordRequest $accountRequest + * + * @throws \SP\Domain\Common\Services\ServiceException */ public function updatePasswordMasterPass( AccountPasswordRequest $accountRequest ): void { - if ($this->accountHistoryRepository->updatePassword($accountRequest) !== 1) { + if (!$this->accountHistoryRepository->updatePassword($accountRequest)) { throw new ServiceException(__u('Error while updating the password')); } } diff --git a/lib/SP/Domain/Account/Services/AccountPasswordRequest.php b/lib/SP/Domain/Account/Services/AccountPasswordRequest.php index 733e94d5..d239f755 100644 --- a/lib/SP/Domain/Account/Services/AccountPasswordRequest.php +++ b/lib/SP/Domain/Account/Services/AccountPasswordRequest.php @@ -24,6 +24,8 @@ namespace SP\Domain\Account\Services; +use SP\Domain\Account\Dtos\EncryptedPassword; + /** * Class AccountPasswordRequest * @@ -31,8 +33,29 @@ namespace SP\Domain\Account\Services; */ final class AccountPasswordRequest { - public ?int $id = null; - public ?string $pass = null; - public ?string $key = null; - public ?string $hash = null; -} \ No newline at end of file + /** + * @param int $id + * @param \SP\Domain\Account\Dtos\EncryptedPassword $encryptedPassword + * @param string|null $hash + */ + public function __construct( + private int $id, + private EncryptedPassword $encryptedPassword, + private ?string $hash = null + ) {} + + public function getId(): int + { + return $this->id; + } + + public function getHash(): ?string + { + return $this->hash; + } + + public function getEncryptedPassword(): EncryptedPassword + { + return $this->encryptedPassword; + } +} diff --git a/lib/SP/Domain/Account/Services/AccountPresetService.php b/lib/SP/Domain/Account/Services/AccountPresetService.php index 75214d88..c27bf829 100644 --- a/lib/SP/Domain/Account/Services/AccountPresetService.php +++ b/lib/SP/Domain/Account/Services/AccountPresetService.php @@ -29,6 +29,7 @@ use SP\Core\Exceptions\NoSuchPropertyException; use SP\Core\Exceptions\QueryException; use SP\Core\Exceptions\ValidationException; use SP\DataModel\ItemPreset\Password; +use SP\Domain\Account\Dtos\AccountRequest; use SP\Domain\Account\Ports\AccountPresetServiceInterface; use SP\Domain\Config\Ports\ConfigDataInterface; use SP\Domain\ItemPreset\Ports\ItemPresetInterface; diff --git a/lib/SP/Domain/Account/Services/AccountService.php b/lib/SP/Domain/Account/Services/AccountService.php index 9408265a..41af2dc1 100644 --- a/lib/SP/Domain/Account/Services/AccountService.php +++ b/lib/SP/Domain/Account/Services/AccountService.php @@ -24,9 +24,7 @@ namespace SP\Domain\Account\Services; -use Defuse\Crypto\Exception\CryptoException; use SP\Core\Application; -use SP\Core\Crypt\Crypt; use SP\Core\Exceptions\ConstraintException; use SP\Core\Exceptions\NoSuchPropertyException; use SP\Core\Exceptions\QueryException; @@ -41,6 +39,9 @@ use SP\DataModel\ItemSearchData; use SP\DataModel\ProfileData; use SP\Domain\Account\Adapters\AccountData; use SP\Domain\Account\Adapters\AccountPassData; +use SP\Domain\Account\Dtos\AccountBulkRequest; +use SP\Domain\Account\Dtos\AccountRequest; +use SP\Domain\Account\Ports\AccountCryptServiceInterface; use SP\Domain\Account\Ports\AccountHistoryServiceInterface; use SP\Domain\Account\Ports\AccountRepositoryInterface; use SP\Domain\Account\Ports\AccountServiceInterface; @@ -67,32 +68,17 @@ final class AccountService extends Service implements AccountServiceInterface { use ServiceItemTrait; - private AccountRepositoryInterface $accountRepository; - private AccountToUserGroupRepositoryInterface $accountToUserGroupRepository; - private AccountToUserRepositoryInterface $accountToUserRepository; - private AccountToTagRepositoryInterface $accountToTagRepository; - private ItemPresetServiceInterface $itemPresetService; - private AccountHistoryServiceInterface $accountHistoryService; - private ConfigServiceInterface $configService; - public function __construct( Application $application, - AccountRepositoryInterface $accountRepository, - AccountToUserGroupRepositoryInterface $accountToUserGroupRepository, - AccountToUserRepositoryInterface $accountToUserRepository, - AccountToTagRepositoryInterface $accountToTagRepository, - ItemPresetServiceInterface $itemPresetService, - AccountHistoryServiceInterface $accountHistoryService, - ConfigServiceInterface $configService, + private AccountRepositoryInterface $accountRepository, + private AccountToUserGroupRepositoryInterface $accountToUserGroupRepository, + private AccountToUserRepositoryInterface $accountToUserRepository, + private AccountToTagRepositoryInterface $accountToTagRepository, + private ItemPresetServiceInterface $itemPresetService, + private AccountHistoryServiceInterface $accountHistoryService, + private ConfigServiceInterface $configService, + private AccountCryptServiceInterface $accountCryptService ) { - $this->accountRepository = $accountRepository; - $this->accountToUserGroupRepository = $accountToUserGroupRepository; - $this->accountToUserRepository = $accountToUserRepository; - $this->accountToTagRepository = $accountToTagRepository; - $this->itemPresetService = $itemPresetService; - $this->accountHistoryService = $accountHistoryService; - $this->configService = $configService; - parent::__construct($application); } @@ -228,10 +214,10 @@ final class AccountService extends Service implements AccountServiceInterface } if (empty($accountRequest->key)) { - $pass = $this->getPasswordEncrypted($accountRequest->pass); + $encryptedPassword = $this->accountCryptService->getPasswordEncrypted($accountRequest->pass); - $accountRequest->pass = $pass['pass']; - $accountRequest->key = $pass['key']; + $accountRequest->pass = $encryptedPassword->getPass(); + $accountRequest->key = $encryptedPassword->getKey(); } $this->setPresetPrivate($accountRequest); @@ -245,35 +231,6 @@ final class AccountService extends Service implements AccountServiceInterface return $accountRequest->id; } - /** - * Devolver los datos de la clave encriptados - * - * @throws \SP\Domain\Common\Services\ServiceException - */ - public function getPasswordEncrypted(string $pass, ?string $masterPass = null): array - { - try { - if ($masterPass === null) { - $masterPass = $this->getMasterKeyFromContext(); - } - - if (empty($masterPass)) { - throw new ServiceException(__u('Master password not set')); - } - - $out['key'] = Crypt::makeSecuredKey($masterPass); - $out['pass'] = Crypt::encrypt($pass, $out['key'], $masterPass); - - if (strlen($out['pass']) > 1000 || strlen($out['key']) > 1000) { - throw new ServiceException(__u('Internal error')); - } - - return $out; - } catch (CryptoException $e) { - throw new ServiceException(__u('Internal error')); - } - } - /** * @throws QueryException * @throws ConstraintException @@ -307,9 +264,10 @@ final class AccountService extends Service implements AccountServiceInterface } /** - * @throws QueryException - * @throws NoSuchItemException - * @throws ConstraintException + * @param int $id + * + * @return \SP\DataModel\Dto\AccountEnrichedDto + * @throws \SP\Infrastructure\Common\Repositories\NoSuchItemException */ public function getById(int $id): AccountEnrichedDto { @@ -406,7 +364,7 @@ final class AccountService extends Service implements AccountServiceInterface /** * Updates external items for the account * - * @param \SP\Domain\Account\Services\AccountRequest $accountRequest + * @param \SP\Domain\Account\Dtos\AccountRequest $accountRequest * * @throws \SP\Domain\Common\Services\ServiceException */ @@ -557,7 +515,7 @@ final class AccountService extends Service implements AccountServiceInterface /** * Update accounts in bulk mode * - * @param \SP\Domain\Account\Services\AccountBulkRequest $request + * @param \SP\Domain\Account\Dtos\AccountBulkRequest $request * * @throws \SP\Domain\Common\Services\ServiceException */ @@ -579,7 +537,7 @@ final class AccountService extends Service implements AccountServiceInterface } /** - * @param \SP\Domain\Account\Services\AccountRequest $accountRequest + * @param \SP\Domain\Account\Dtos\AccountRequest $accountRequest * * @throws \SP\Domain\Common\Services\ServiceException */ @@ -602,12 +560,18 @@ final class AccountService extends Service implements AccountServiceInterface /** * Updates an already encrypted password data from a master password changing action * - * @throws ConstraintException - * @throws QueryException + * @param \SP\Domain\Account\Services\AccountPasswordRequest $accountRequest + * + * @return void + * @throws \SP\Core\Exceptions\ConstraintException + * @throws \SP\Core\Exceptions\QueryException + * @throws \SP\Domain\Common\Services\ServiceException */ - public function updatePasswordMasterPass(AccountPasswordRequest $accountRequest): bool + public function updatePasswordMasterPass(AccountPasswordRequest $accountRequest): void { - return $this->accountRepository->updatePassword($accountRequest); + if (!$this->accountRepository->updatePassword($accountRequest)) { + throw new ServiceException(__u('Error while updating the password')); + } } /** diff --git a/lib/SP/Domain/Client/Services/ClientService.php b/lib/SP/Domain/Client/Services/ClientService.php index 93d2afb7..13518b04 100644 --- a/lib/SP/Domain/Client/Services/ClientService.php +++ b/lib/SP/Domain/Client/Services/ClientService.php @@ -31,7 +31,7 @@ use SP\Core\Exceptions\SPException; use SP\DataModel\ClientData; use SP\DataModel\ItemData; use SP\DataModel\ItemSearchData; -use SP\Domain\Account\Services\AccountFilterUserInterface; +use SP\Domain\Account\Ports\AccountFilterUserInterface; use SP\Domain\Client\Ports\ClientRepositoryInterface; use SP\Domain\Client\Ports\ClientServiceInterface; use SP\Domain\Common\Services\Service; @@ -49,8 +49,8 @@ final class ClientService extends Service implements ClientServiceInterface { use ServiceItemTrait; - private ClientRepositoryInterface $clientRepository; - private AccountFilterUserInterface $accountFilterUser; + private ClientRepositoryInterface $clientRepository; + private \SP\Domain\Account\Ports\AccountFilterUserInterface $accountFilterUser; public function __construct( Application $application, diff --git a/lib/SP/Domain/Common/Adapters/DataModel.php b/lib/SP/Domain/Common/Adapters/DataModel.php index 49d6a641..bbbbda2f 100644 --- a/lib/SP/Domain/Common/Adapters/DataModel.php +++ b/lib/SP/Domain/Common/Adapters/DataModel.php @@ -40,6 +40,20 @@ abstract class DataModel implements JsonSerializable } } + final public static function buildFromSimpleModel(SimpleModel $model): static + { + return new static($model->toArray()); + } + + final public function toArray(): array + { + if (count($this->properties) !== 0) { + return $this->properties; + } + + return get_object_vars($this); + } + /** * @param string $name * @@ -84,13 +98,4 @@ abstract class DataModel implements JsonSerializable { return $this->toArray(); } - - final public function toArray(): array - { - if (count($this->properties) !== 0) { - return $this->properties; - } - - return get_object_vars($this); - } } diff --git a/lib/SP/Domain/Common/Services/ServiceException.php b/lib/SP/Domain/Common/Services/ServiceException.php index 97616b3c..7983c1e2 100644 --- a/lib/SP/Domain/Common/Services/ServiceException.php +++ b/lib/SP/Domain/Common/Services/ServiceException.php @@ -24,7 +24,6 @@ namespace SP\Domain\Common\Services; - use SP\Core\Exceptions\SPException; /** @@ -35,4 +34,4 @@ use SP\Core\Exceptions\SPException; final class ServiceException extends SPException { -} \ No newline at end of file +} diff --git a/lib/SP/Domain/Crypt/Services/UpdateMasterPassRequest.php b/lib/SP/Domain/Crypt/Services/UpdateMasterPassRequest.php index 05b5efd8..6684d5bd 100644 --- a/lib/SP/Domain/Crypt/Services/UpdateMasterPassRequest.php +++ b/lib/SP/Domain/Crypt/Services/UpdateMasterPassRequest.php @@ -25,8 +25,7 @@ namespace SP\Domain\Crypt\Services; use SP\Core\Crypt\Hash; -use SP\Domain\Task\Services\Task; - +use SP\Domain\Task\Ports\TaskInterface; /** * Class UpdateMasterPassRequest @@ -35,11 +34,7 @@ use SP\Domain\Task\Services\Task; */ final class UpdateMasterPassRequest { - private string $currentMasterPass; - private string $newMasterPass; - private ?Task $task; private string $hash; - private string $currentHash; /** * UpdateMasterPassRequest constructor. @@ -47,19 +42,15 @@ final class UpdateMasterPassRequest * @param string $currentMasterPass * @param string $newMasterPass * @param string $currentHash - * @param \SP\Domain\Task\Services\Task|null $task + * @param TaskInterface|null $task */ public function __construct( - string $currentMasterPass, - string $newMasterPass, - string $currentHash, - ?Task $task = null + private string $currentMasterPass, + private string $newMasterPass, + private string $currentHash, + private ?TaskInterface $task = null ) { - $this->currentMasterPass = $currentMasterPass; - $this->newMasterPass = $newMasterPass; - $this->task = $task; $this->hash = Hash::hashKey($newMasterPass); - $this->currentHash = $currentHash; } public function getCurrentMasterPass(): string @@ -72,7 +63,7 @@ final class UpdateMasterPassRequest return $this->newMasterPass; } - public function getTask(): ?Task + public function getTask(): ?TaskInterface { return $this->task; } @@ -91,5 +82,4 @@ final class UpdateMasterPassRequest { return $this->currentHash; } - -} \ No newline at end of file +} diff --git a/lib/SP/Domain/Import/Services/CsvImportBase.php b/lib/SP/Domain/Import/Services/CsvImportBase.php index d0300574..2c57387b 100644 --- a/lib/SP/Domain/Import/Services/CsvImportBase.php +++ b/lib/SP/Domain/Import/Services/CsvImportBase.php @@ -32,7 +32,6 @@ use SP\Core\Events\EventMessage; use SP\Core\Exceptions\SPException; use SP\DataModel\CategoryData; use SP\DataModel\ClientData; -use SP\Domain\Account\Services\AccountRequest; use SP\Infrastructure\File\FileException; defined('APP_ROOT') || die(); @@ -113,7 +112,7 @@ abstract class CsvImportBase $categoryId = $this->addCategory(new CategoryData(null, $categoryName)); // Crear la nueva cuenta - $accountRequest = new AccountRequest(); + $accountRequest = new \SP\Domain\Account\Dtos\AccountRequest(); $accountRequest->name = $accountName; $accountRequest->login = $login; $accountRequest->clientId = $clientId; diff --git a/lib/SP/Domain/Import/Services/ImportTrait.php b/lib/SP/Domain/Import/Services/ImportTrait.php index d57256f0..6ecf52b4 100644 --- a/lib/SP/Domain/Import/Services/ImportTrait.php +++ b/lib/SP/Domain/Import/Services/ImportTrait.php @@ -33,8 +33,8 @@ use SP\Core\Exceptions\SPException; use SP\DataModel\CategoryData; use SP\DataModel\ClientData; use SP\DataModel\TagData; +use SP\Domain\Account\Dtos\AccountRequest; use SP\Domain\Account\Ports\AccountServiceInterface; -use SP\Domain\Account\Services\AccountRequest; use SP\Domain\Category\Ports\CategoryServiceInterface; use SP\Domain\Client\Ports\ClientServiceInterface; use SP\Domain\Tag\Ports\TagServiceInterface; @@ -80,7 +80,7 @@ trait ImportTrait * @throws NoSuchPropertyException * @throws QueryException */ - protected function addAccount(AccountRequest $accountRequest): void + protected function addAccount(\SP\Domain\Account\Dtos\AccountRequest $accountRequest): void { if (empty($accountRequest->categoryId)) { throw new ImportException(__u('Category Id not set. Unable to import account.')); diff --git a/lib/SP/Domain/Import/Services/KeepassImport.php b/lib/SP/Domain/Import/Services/KeepassImport.php index 484ce694..bc3725ae 100644 --- a/lib/SP/Domain/Import/Services/KeepassImport.php +++ b/lib/SP/Domain/Import/Services/KeepassImport.php @@ -32,7 +32,7 @@ use SP\Core\Events\EventMessage; use SP\Core\Exceptions\SPException; use SP\DataModel\CategoryData; use SP\DataModel\ClientData; -use SP\Domain\Account\Services\AccountRequest; +use SP\Domain\Account\Dtos\AccountRequest; use SP\Util\Filter; defined('APP_ROOT') || die(); @@ -79,7 +79,7 @@ final class KeepassImport extends XmlImportBase implements ImportInterface $this->getEntries(); - /** @var AccountRequest[] $group */ + /** @var \SP\Domain\Account\Dtos\AccountRequest[] $group */ foreach ($this->items as $group => $entry) { try { $categoryId = $this->addCategory(new CategoryData(null, $group, 'KeePass')); @@ -172,9 +172,9 @@ final class KeepassImport extends XmlImportBase implements ImportInterface } } - private function mapEntryToAccount(array $entry): AccountRequest + private function mapEntryToAccount(array $entry): \SP\Domain\Account\Dtos\AccountRequest { - $accountRequest = new AccountRequest(); + $accountRequest = new \SP\Domain\Account\Dtos\AccountRequest(); $accountRequest->name = isset($entry['Title']) ? Filter::getString($entry['Title']) : ''; $accountRequest->login = isset($entry['UserName']) ? Filter::getString($entry['UserName']) : ''; $accountRequest->pass = $entry['Password'] ?? ''; diff --git a/lib/SP/Domain/Import/Services/SyspassImport.php b/lib/SP/Domain/Import/Services/SyspassImport.php index 811a03d3..eff8d482 100644 --- a/lib/SP/Domain/Import/Services/SyspassImport.php +++ b/lib/SP/Domain/Import/Services/SyspassImport.php @@ -38,7 +38,6 @@ use SP\Core\Exceptions\SPException; use SP\DataModel\CategoryData; use SP\DataModel\ClientData; use SP\DataModel\TagData; -use SP\Domain\Account\Services\AccountRequest; use SP\Domain\Export\Services\XmlVerifyService; use SP\Util\VersionUtil; @@ -422,7 +421,7 @@ final class SyspassImport extends XmlImportBase implements ImportInterface 'Accounts', 'Account', function (DOMElement $account) { - $accountRequest = new AccountRequest(); + $accountRequest = new \SP\Domain\Account\Dtos\AccountRequest(); /** @var DOMElement $node */ foreach ($account->childNodes as $node) { diff --git a/lib/SP/Domain/Task/Ports/TaskInterface.php b/lib/SP/Domain/Task/Ports/TaskInterface.php new file mode 100644 index 00000000..c79bb04e --- /dev/null +++ b/lib/SP/Domain/Task/Ports/TaskInterface.php @@ -0,0 +1,89 @@ +. + */ + +namespace SP\Domain\Task\Ports; + +use SP\Core\Messages\TaskMessage; +use SP\Infrastructure\File\FileException; +use SP\Infrastructure\File\FileHandlerInterface; + +/** + * Class Task + * + * @package SP\Core + */ +interface TaskInterface +{ + public function genUid(): string; + + /** + * Escribir el tado de la tarea a un archivo + */ + public function writeStatusAndFlush(TaskMessage $message): bool; + + /** + * Escribir un mensaje en el archivo de la tarea en formato JSON + */ + public function writeJsonStatusAndFlush(TaskMessage $message): void; + + /** + * Iniciar la tarea + */ + public function end(): void; + + /** + * Desregistrar la tarea en la sesión + * + * @throws FileException + */ + public function unregister(): void; + + public function getInterval(): int; + + public function setInterval(int $interval): TaskInterface; + + public function getTaskId(): string; + + public function getFileOut(): ?FileHandlerInterface; + + /** + * Register a task + * + * @throws FileException + */ + public function register(): TaskInterface; + + /** + * Register a task + * + * Session is locked in order to allow other scripts execution + * + * @throws FileException + */ + public function registerSession(): TaskInterface; + + public function getUid(): string; + + public function getFileTask(): ?FileHandlerInterface; +} diff --git a/lib/SP/Domain/Task/Services/Task.php b/lib/SP/Domain/Task/Services/Task.php index da582f59..4352dc81 100644 --- a/lib/SP/Domain/Task/Services/Task.php +++ b/lib/SP/Domain/Task/Services/Task.php @@ -27,26 +27,21 @@ namespace SP\Domain\Task\Services; use JsonException; use SP\Core\Context\SessionContext; use SP\Core\Messages\TaskMessage; +use SP\Domain\Task\Ports\TaskInterface; use SP\Infrastructure\File\FileException; use SP\Infrastructure\File\FileHandler; use SP\Infrastructure\File\FileHandlerInterface; use SP\Util\Util; +use function SP\logger; +use function SP\processException; /** * Class Task * * @package SP\Core */ -final class Task +final class Task implements TaskInterface { - /** - * @var string Nombre de la tarea - */ - private string $name; - /** - * @var string ID de la tarea - */ - private string $taskId; private ?FileHandler $fileOut = null; private ?FileHandler $fileTask = null; /** @@ -63,12 +58,10 @@ final class Task * Task constructor. * * @param string $name Nombre de la tarea - * @param string $id + * @param string $taskId */ - public function __construct(string $name, string $id) + public function __construct(private string $name, private string $taskId) { - $this->name = $name; - $this->taskId = $id; $this->initialized = $this->checkFile(); $this->uid = $this->genUid(); } @@ -202,7 +195,7 @@ final class Task return $this->interval; } - public function setInterval(int $interval): Task + public function setInterval(int $interval): TaskInterface { $this->interval = $interval; @@ -224,7 +217,7 @@ final class Task * * @throws FileException */ - public function register(): Task + public function register(): TaskInterface { logger("Register Task: $this->name"); @@ -240,7 +233,7 @@ final class Task * * @throws FileException */ - public function registerSession(): Task + public function registerSession(): TaskInterface { logger("Register Task (session): $this->name"); @@ -260,4 +253,4 @@ final class Task { return $this->fileTask; } -} \ No newline at end of file +} diff --git a/lib/SP/Domain/Task/Services/TaskFactory.php b/lib/SP/Domain/Task/Services/TaskFactory.php index 7e194d86..883c40c7 100644 --- a/lib/SP/Domain/Task/Services/TaskFactory.php +++ b/lib/SP/Domain/Task/Services/TaskFactory.php @@ -26,6 +26,7 @@ namespace SP\Domain\Task\Services; use RuntimeException; use SP\Core\Messages\TaskMessage; +use SP\Domain\Task\Ports\TaskInterface; use SP\Infrastructure\File\FileException; /** @@ -36,7 +37,7 @@ use SP\Infrastructure\File\FileException; final class TaskFactory { /** - * @var Task[] + * @var TaskInterface[] */ private static array $tasks = []; @@ -45,12 +46,9 @@ final class TaskFactory * * @throws FileException */ - public static function create( - string $name, - string $id, - bool $hasSession = true - ): Task { - $task = self::add((new Task($name, $id))); + public static function register(TaskInterface $task, bool $hasSession = true): TaskInterface + { + $task = self::add($task); if ($hasSession) { return $task->registerSession(); @@ -59,7 +57,7 @@ final class TaskFactory return $task->register(); } - private static function add(Task $task): Task + private static function add(TaskInterface $task): TaskInterface { if (!isset(self::$tasks[$task->getUid()])) { self::$tasks[$task->getUid()] = $task; @@ -73,15 +71,14 @@ final class TaskFactory /** * Finalizar la tarea */ - public static function end(Task $task): void + public static function end(TaskInterface $task): void { - self::get($task->getUid()) - ->end(); + self::get($task->getUid())->end(); self::delete($task->getUid()); } - private static function get(string $id): Task + private static function get(string $id): TaskInterface { if (isset(self::$tasks[$id])) { return self::$tasks[$id]; @@ -97,19 +94,16 @@ final class TaskFactory } } - public static function createMessage( - string $taskId, - string $task - ): TaskMessage { + public static function createMessage(string $taskId, string $task): TaskMessage + { return new TaskMessage($taskId, $task); } /** * Enviar un mensaje de actualización a la tarea */ - public static function update(Task $task, TaskMessage $taskMessage): void + public static function update(TaskInterface $task, TaskMessage $taskMessage): void { - self::get($task->getUid()) - ->writeJsonStatusAndFlush($taskMessage); + self::get($task->getUid())->writeJsonStatusAndFlush($taskMessage); } -} \ No newline at end of file +} diff --git a/lib/SP/Infrastructure/Account/Repositories/AccountHistoryRepository.php b/lib/SP/Infrastructure/Account/Repositories/AccountHistoryRepository.php index 04cf64d7..6fc8db08 100644 --- a/lib/SP/Infrastructure/Account/Repositories/AccountHistoryRepository.php +++ b/lib/SP/Infrastructure/Account/Repositories/AccountHistoryRepository.php @@ -394,12 +394,12 @@ final class AccountHistoryRepository extends Repository implements AccountHistor ->newUpdate() ->table('AccountHistory') ->cols([ - 'pass' => $request->pass, - 'key' => $request->key, - 'mPassHash' => $request->hash, + 'pass' => $request->getEncryptedPassword()->getPass(), + 'key' => $request->getEncryptedPassword()->getKey(), + 'mPassHash' => $request->getHash(), ]) ->where('id = :id') - ->bindValues(['id' => $request->id]); + ->bindValues(['id' => $request->getId()]); $queryData = QueryData::build($query)->setOnErrorMessage(__u('Error while updating the password')); diff --git a/lib/SP/Infrastructure/Account/Repositories/AccountRepository.php b/lib/SP/Infrastructure/Account/Repositories/AccountRepository.php index d59e46c9..9dc73ea4 100644 --- a/lib/SP/Infrastructure/Account/Repositories/AccountRepository.php +++ b/lib/SP/Infrastructure/Account/Repositories/AccountRepository.php @@ -32,10 +32,10 @@ use SP\Core\Exceptions\QueryException; use SP\Core\Exceptions\SPException; use SP\DataModel\AccountHistoryData; use SP\DataModel\ItemSearchData; +use SP\Domain\Account\Dtos\AccountRequest; +use SP\Domain\Account\Ports\AccountFilterUserInterface; use SP\Domain\Account\Ports\AccountRepositoryInterface; -use SP\Domain\Account\Services\AccountFilterUserInterface; use SP\Domain\Account\Services\AccountPasswordRequest; -use SP\Domain\Account\Services\AccountRequest; use SP\Domain\Common\Adapters\SimpleModel; use SP\Infrastructure\Common\Repositories\Repository; use SP\Infrastructure\Common\Repositories\RepositoryItemTrait; @@ -153,7 +153,7 @@ final class AccountRepository extends Repository implements AccountRepositoryInt /** * Crea una nueva cuenta en la BBDD * - * @param AccountRequest $accountRequest + * @param \SP\Domain\Account\Dtos\AccountRequest $accountRequest * * @return int * @throws ConstraintException @@ -192,7 +192,7 @@ final class AccountRepository extends Repository implements AccountRepositoryInt /** * Actualiza la clave de una cuenta en la BBDD. * - * @param AccountRequest $accountRequest + * @param \SP\Domain\Account\Dtos\AccountRequest $accountRequest * * @return int * @throws ConstraintException @@ -233,9 +233,14 @@ final class AccountRepository extends Repository implements AccountRepositoryInt $query = $this->queryFactory ->newUpdate() ->table('Account') - ->cols(['pass' => $request->pass, 'key' => $request->key]) + ->cols( + [ + 'pass' => $request->getEncryptedPassword()->getPass(), + 'key' => $request->getEncryptedPassword()->getKey(), + ] + ) ->where('id = :id') - ->bindValues(['id' => $request->id]); + ->bindValues(['id' => $request->getId()]); $queryData = QueryData::build($query)->setOnErrorMessage(__u('Error while updating the password')); @@ -351,7 +356,7 @@ final class AccountRepository extends Repository implements AccountRepositoryInt /** * Updates an item for bulk action * - * @param AccountRequest $itemData + * @param \SP\Domain\Account\Dtos\AccountRequest $itemData * * @return int * @throws SPException diff --git a/lib/SP/Infrastructure/Account/Repositories/AccountSearchRepository.php b/lib/SP/Infrastructure/Account/Repositories/AccountSearchRepository.php index 6a0f3224..bbc518f7 100644 --- a/lib/SP/Infrastructure/Account/Repositories/AccountSearchRepository.php +++ b/lib/SP/Infrastructure/Account/Repositories/AccountSearchRepository.php @@ -30,10 +30,10 @@ use Aura\SqlQuery\QueryFactory; use SP\Core\Context\ContextInterface; use SP\Core\Events\EventDispatcherInterface; use SP\DataModel\AccountSearchVData; +use SP\Domain\Account\Ports\AccountFilterUserInterface; use SP\Domain\Account\Ports\AccountSearchRepositoryInterface; use SP\Domain\Account\Search\AccountSearchConstants; use SP\Domain\Account\Search\AccountSearchFilter; -use SP\Domain\Account\Services\AccountFilterUserInterface; use SP\Infrastructure\Common\Repositories\Repository; use SP\Infrastructure\Database\DatabaseInterface; use SP\Infrastructure\Database\QueryData; diff --git a/lib/SP/Infrastructure/Account/Repositories/AccountToTagRepository.php b/lib/SP/Infrastructure/Account/Repositories/AccountToTagRepository.php index abf9c021..dc0f5d72 100644 --- a/lib/SP/Infrastructure/Account/Repositories/AccountToTagRepository.php +++ b/lib/SP/Infrastructure/Account/Repositories/AccountToTagRepository.php @@ -24,8 +24,8 @@ namespace SP\Infrastructure\Account\Repositories; +use SP\Domain\Account\Dtos\AccountRequest; use SP\Domain\Account\Ports\AccountToTagRepositoryInterface; -use SP\Domain\Account\Services\AccountRequest; use SP\Infrastructure\Common\Repositories\Repository; use SP\Infrastructure\Common\Repositories\RepositoryItemTrait; use SP\Infrastructure\Database\QueryData; @@ -92,7 +92,7 @@ final class AccountToTagRepository extends Repository implements AccountToTagRep /** * Actualizar las etiquetas de una cuenta * - * @param AccountRequest $accountRequest + * @param \SP\Domain\Account\Dtos\AccountRequest $accountRequest * * @return void * @throws \SP\Core\Exceptions\ConstraintException diff --git a/tests/SP/Domain/Account/Adapters/AccountAdapterTest.php b/tests/SP/Domain/Account/Adapters/AccountAdapterTest.php index ff718eb5..2e6757bc 100644 --- a/tests/SP/Domain/Account/Adapters/AccountAdapterTest.php +++ b/tests/SP/Domain/Account/Adapters/AccountAdapterTest.php @@ -46,7 +46,7 @@ class AccountAdapterTest extends UnitaryTestCase $this->config->getConfigData(), $this->createStub(CustomFieldServiceInterface::class) ); - $accountData = $dataGenerator->getAccountData(); + $accountData = $dataGenerator->buildAccountEnrichedData(); $out = $adapter->transform($accountData); @@ -113,7 +113,7 @@ class AccountAdapterTest extends UnitaryTestCase $fractal = new Manager(); $fractal->parseIncludes('customFields'); $out = $fractal->createData( - new Item(AccountDataGenerator::factory()->getAccountData(), $adapter) + new Item(AccountDataGenerator::factory()->buildAccountEnrichedData(), $adapter) )->toArray(); $this->assertArrayHasKey('customFields', $out['data']); diff --git a/tests/SP/Domain/Account/Services/AccountCryptServiceTest.php b/tests/SP/Domain/Account/Services/AccountCryptServiceTest.php new file mode 100644 index 00000000..161f0538 --- /dev/null +++ b/tests/SP/Domain/Account/Services/AccountCryptServiceTest.php @@ -0,0 +1,370 @@ +. + */ + +namespace SP\Tests\Domain\Account\Services; + +use PHPUnit\Framework\MockObject\MockObject; +use SP\Core\Crypt\CryptInterface; +use SP\Core\Exceptions\SPException; +use SP\Domain\Account\Ports\AccountHistoryServiceInterface; +use SP\Domain\Account\Ports\AccountServiceInterface; +use SP\Domain\Account\Services\AccountCryptService; +use SP\Domain\Common\Services\ServiceException; +use SP\Domain\Crypt\Services\UpdateMasterPassRequest; +use SP\Domain\Task\Ports\TaskInterface; +use SP\Domain\Task\Services\TaskFactory; +use SP\Tests\Generators\AccountDataGenerator; +use SP\Tests\UnitaryTestCase; + +/** + * Class AccountCryptServiceTest + */ +class AccountCryptServiceTest extends UnitaryTestCase +{ + + private MockObject|AccountServiceInterface $accountService; + private MockObject|AccountHistoryServiceInterface $accountHistoryService; + private AccountCryptService $accountCryptService; + private MockObject|CryptInterface $crypt; + + /** + * @throws \SP\Domain\Common\Services\ServiceException + * @throws \SP\Infrastructure\File\FileException + */ + public function testUpdateMasterPassword(): void + { + $task = $this->createMock(TaskInterface::class); + $task->method('getUid') + ->willReturn(self::$faker->uuid); + $task->method('getTaskId') + ->willReturn((string)self::$faker->randomNumber()); + $task->method('registerSession') + ->willReturnSelf(); + + $request = + new UpdateMasterPassRequest( + self::$faker->password, + self::$faker->password, + self::$faker->sha1, + TaskFactory::register($task) + ); + $accountData = array_map(static fn() => AccountDataGenerator::factory()->buildAccountData(), range(0, 9)); + + $this->accountService->expects(self::once()) + ->method('getAccountsPassData') + ->willReturn($accountData); + $this->accountService->expects(self::exactly(10)) + ->method('updatePasswordMasterPass'); + $this->crypt->expects(self::exactly(10)) + ->method('decrypt'); + $this->crypt->expects(self::exactly(10)) + ->method('makeSecuredKey') + ->willReturn(self::$faker->password); + $this->crypt->expects(self::exactly(10)) + ->method('encrypt') + ->willReturn(self::$faker->password); + $task->expects(self::exactly(2)) + ->method('writeJsonStatusAndFlush'); + + $this->accountCryptService->updateMasterPassword($request); + } + + /** + * @throws \SP\Domain\Common\Services\ServiceException + */ + public function testUpdateMasterPasswordWithNoAccounts(): void + { + $request = + new UpdateMasterPassRequest( + self::$faker->password, + self::$faker->password, + self::$faker->sha1 + ); + + $this->accountService->expects(self::once()) + ->method('getAccountsPassData') + ->willReturn([]); + $this->accountService->expects(self::never()) + ->method('updatePasswordMasterPass'); + $this->crypt->expects(self::never()) + ->method('decrypt'); + $this->crypt->expects(self::never()) + ->method('makeSecuredKey'); + $this->crypt->expects(self::never()) + ->method('encrypt'); + + $this->accountCryptService->updateMasterPassword($request); + } + + /** + * @throws \SP\Domain\Common\Services\ServiceException + */ + public function testUpdateMasterPasswordDoesNotThrowException(): void + { + $request = new UpdateMasterPassRequest(self::$faker->password, self::$faker->password, self::$faker->sha1); + $accountData = array_map(static fn() => AccountDataGenerator::factory()->buildAccountData(), range(0, 9)); + + $this->accountService->expects(self::once()) + ->method('getAccountsPassData') + ->willReturn($accountData); + $this->crypt->expects(self::exactly(10)) + ->method('decrypt') + ->willThrowException(new SPException('test')); + + $this->accountCryptService->updateMasterPassword($request); + } + + /** + * @throws \SP\Domain\Common\Services\ServiceException + */ + public function testUpdateMasterPasswordThrowException(): void + { + $request = new UpdateMasterPassRequest(self::$faker->password, self::$faker->password, self::$faker->sha1); + + $this->accountService->expects(self::once()) + ->method('getAccountsPassData') + ->willThrowException(new \RuntimeException('test')); + + $this->expectException(ServiceException::class); + $this->expectExceptionMessage('Error while updating the accounts\' passwords'); + + $this->accountCryptService->updateMasterPassword($request); + } + + /** + * @throws \SP\Domain\Common\Services\ServiceException + * @throws \SP\Infrastructure\File\FileException + */ + public function testUpdateHistoryMasterPassword(): void + { + $task = $this->createMock(TaskInterface::class); + $task->method('getUid') + ->willReturn(self::$faker->uuid); + $task->method('getTaskId') + ->willReturn((string)self::$faker->randomNumber()); + $task->method('registerSession') + ->willReturnSelf(); + + $request = + new UpdateMasterPassRequest( + self::$faker->password, + self::$faker->password, + self::$faker->sha1, + TaskFactory::register($task) + ); + $accountData = array_map(static fn() => AccountDataGenerator::factory()->buildAccountData(), range(0, 9)); + + $this->accountHistoryService->expects(self::once()) + ->method('getAccountsPassData') + ->willReturn($accountData); + $this->accountHistoryService->expects(self::exactly(10)) + ->method('updatePasswordMasterPass'); + $this->crypt->expects(self::exactly(10)) + ->method('decrypt'); + $task->expects(self::exactly(2)) + ->method('writeJsonStatusAndFlush'); + + $this->accountCryptService->updateHistoryMasterPassword($request); + } + + /** + * @throws \SP\Domain\Common\Services\ServiceException + */ + public function testUpdateHistoryMasterPasswordWithNoAccounts(): void + { + $request = + new UpdateMasterPassRequest( + self::$faker->password, + self::$faker->password, + self::$faker->sha1 + ); + + $this->accountHistoryService->expects(self::once()) + ->method('getAccountsPassData') + ->willReturn([]); + $this->accountHistoryService->expects(self::never()) + ->method('updatePasswordMasterPass'); + $this->crypt->expects(self::never()) + ->method('decrypt'); + $this->crypt->expects(self::never()) + ->method('makeSecuredKey'); + $this->crypt->expects(self::never()) + ->method('encrypt'); + + $this->accountCryptService->updateHistoryMasterPassword($request); + } + + /** + * @throws \SP\Domain\Common\Services\ServiceException + */ + public function testUpdateHistoryMasterPasswordThrowException(): void + { + $request = new UpdateMasterPassRequest(self::$faker->password, self::$faker->password, self::$faker->sha1); + + $this->accountHistoryService->expects(self::once()) + ->method('getAccountsPassData') + ->willThrowException(new \RuntimeException('test')); + + + $this->expectException(ServiceException::class); + $this->expectExceptionMessage('Error while updating the accounts\' passwords in history'); + + $this->accountCryptService->updateHistoryMasterPassword($request); + } + + /** + * @throws \SP\Domain\Common\Services\ServiceException + */ + public function testUpdateHistoryMasterPasswordDoesNotThrowException(): void + { + $request = new UpdateMasterPassRequest(self::$faker->password, self::$faker->password, self::$faker->sha1); + $accountData = array_map(static fn() => AccountDataGenerator::factory()->buildAccountData(), range(0, 9)); + + $this->accountHistoryService->expects(self::once()) + ->method('getAccountsPassData') + ->willReturn($accountData); + $this->crypt->expects(self::exactly(10)) + ->method('decrypt') + ->willThrowException(new SPException('test')); + + $this->accountCryptService->updateHistoryMasterPassword($request); + } + + /** + * @throws \SP\Domain\Common\Services\ServiceException + */ + public function testGetPasswordEncrypted(): void + { + $pass = self::$faker->password; + $key = self::$faker->password; + $masterPass = self::$faker->password; + + $this->crypt->expects(self::once()) + ->method('makeSecuredKey') + ->with($masterPass) + ->willReturn($key); + + $this->crypt->expects(self::once()) + ->method('encrypt') + ->with($pass) + ->willReturn($pass); + + $out = $this->accountCryptService->getPasswordEncrypted($pass, $masterPass); + + $this->assertEquals($pass, $out->getPass()); + $this->assertEquals($key, $out->getKey()); + } + + /** + * @throws \SP\Domain\Common\Services\ServiceException + * @throws \SP\Core\Context\ContextException + */ + public function testGetPasswordEncryptedThrowsExceptionWithNoMasterPassword(): void + { + $this->context->setTrasientKey('_masterpass', ''); + + $this->crypt->expects(self::never()) + ->method('makeSecuredKey'); + + $this->crypt->expects(self::never()) + ->method('encrypt'); + + $this->expectException(ServiceException::class); + $this->expectExceptionMessage('Error while retrieving master password from context'); + + $this->accountCryptService->getPasswordEncrypted(self::$faker->password); + } + + /** + * @throws \SP\Domain\Common\Services\ServiceException + */ + public function testGetPasswordEncryptedThrowsExceptionWithEmptyMasterPassword(): void + { + $this->crypt->expects(self::never()) + ->method('makeSecuredKey'); + + $this->crypt->expects(self::never()) + ->method('encrypt'); + + $this->expectException(ServiceException::class); + $this->expectExceptionMessage('Master password not set'); + + $this->accountCryptService->getPasswordEncrypted(self::$faker->password, ''); + } + + /** + * @throws \SP\Domain\Common\Services\ServiceException + */ + public function testGetPasswordEncryptedThrowsExceptionWithLongPass(): void + { + $this->crypt->expects(self::once()) + ->method('makeSecuredKey') + ->willReturn(self::$faker->password); + + $this->crypt->expects(self::once()) + ->method('encrypt') + ->willReturn(self::$faker->text(1500)); + + $this->expectException(ServiceException::class); + $this->expectExceptionMessage('Internal error'); + + $this->accountCryptService->getPasswordEncrypted(self::$faker->password, self::$faker->password); + } + + /** + * @throws \SP\Domain\Common\Services\ServiceException + */ + public function testGetPasswordEncryptedThrowsExceptionWithLongKey(): void + { + $this->crypt->expects(self::once()) + ->method('makeSecuredKey') + ->willReturn(self::$faker->text(1500)); + + $this->crypt->expects(self::once()) + ->method('encrypt') + ->willReturn(self::$faker->password); + + $this->expectException(ServiceException::class); + $this->expectExceptionMessage('Internal error'); + + $this->accountCryptService->getPasswordEncrypted(self::$faker->password, self::$faker->password); + } + + protected function setUp(): void + { + parent::setUp(); + + $this->accountService = $this->createMock(AccountServiceInterface::class); + $this->accountHistoryService = $this->createMock(AccountHistoryServiceInterface::class); + $this->crypt = $this->createMock(CryptInterface::class); + + $this->accountCryptService = + new AccountCryptService( + $this->application, + $this->accountService, + $this->accountHistoryService, + $this->crypt + ); + } +} diff --git a/tests/SP/Generators/AccountDataGenerator.php b/tests/SP/Generators/AccountDataGenerator.php index 60cd7c37..4e939e13 100644 --- a/tests/SP/Generators/AccountDataGenerator.php +++ b/tests/SP/Generators/AccountDataGenerator.php @@ -27,15 +27,26 @@ namespace SP\Tests\Generators; use SP\DataModel\AccountVData; use SP\DataModel\Dto\AccountEnrichedDto; use SP\DataModel\ItemData; +use SP\Domain\Common\Adapters\SimpleModel; /** * Class AccountDataGenerator */ final class AccountDataGenerator extends DataGenerator { - public function getAccountData(): AccountEnrichedDto + public function buildAccountEnrichedData(): AccountEnrichedDto { - $accountData = new AccountVData([ + $out = new AccountEnrichedDto(AccountVData::buildFromSimpleModel($this->buildAccountData())); + $out->setUsers($this->buildItemData()); + $out->setTags($this->buildItemData()); + $out->setUserGroups($this->buildItemData()); + + return $out; + } + + public function buildAccountData(): SimpleModel + { + return new SimpleModel([ 'id' => $this->faker->randomNumber(), 'name' => $this->faker->name, 'clientId' => $this->faker->randomNumber(), @@ -65,13 +76,9 @@ final class AccountDataGenerator extends DataGenerator 'passDateChange' => $this->faker->unixTime, 'parentId' => $this->faker->randomNumber(), 'publicLinkHash' => $this->faker->sha1, + 'pass' => $this->faker->password, + 'key' => $this->faker->sha1, ]); - $out = new AccountEnrichedDto($accountData); - $out->setUsers($this->buildItemData()); - $out->setTags($this->buildItemData()); - $out->setUserGroups($this->buildItemData()); - - return $out; } /** diff --git a/tests/SP/Infrastructure/Account/Repositories/AccountRepositoryTest.php b/tests/SP/Infrastructure/Account/Repositories/AccountRepositoryTest.php index 27bfadfb..2e970b93 100644 --- a/tests/SP/Infrastructure/Account/Repositories/AccountRepositoryTest.php +++ b/tests/SP/Infrastructure/Account/Repositories/AccountRepositoryTest.php @@ -29,9 +29,9 @@ use PHPUnit\Framework\Constraint\Callback; use PHPUnit\Framework\MockObject\MockObject; use SP\DataModel\AccountHistoryData; use SP\DataModel\ItemSearchData; +use SP\Domain\Account\Dtos\AccountRequest; use SP\Domain\Account\Services\AccountFilterUser; use SP\Domain\Account\Services\AccountPasswordRequest; -use SP\Domain\Account\Services\AccountRequest; use SP\Domain\Common\Adapters\SimpleModel; use SP\Infrastructure\Account\Repositories\AccountRepository; use SP\Infrastructure\Database\DatabaseInterface; diff --git a/tests/SP/Infrastructure/Account/Repositories/AccountToTagRepositoryTest.php b/tests/SP/Infrastructure/Account/Repositories/AccountToTagRepositoryTest.php index b7125399..6a9926cc 100644 --- a/tests/SP/Infrastructure/Account/Repositories/AccountToTagRepositoryTest.php +++ b/tests/SP/Infrastructure/Account/Repositories/AccountToTagRepositoryTest.php @@ -27,7 +27,7 @@ namespace SP\Tests\Infrastructure\Account\Repositories; use Aura\SqlQuery\QueryFactory; use PHPUnit\Framework\Constraint\Callback; use PHPUnit\Framework\MockObject\MockObject; -use SP\Domain\Account\Services\AccountRequest; +use SP\Domain\Account\Dtos\AccountRequest; use SP\Domain\Common\Adapters\SimpleModel; use SP\Infrastructure\Account\Repositories\AccountToTagRepository; use SP\Infrastructure\Database\DatabaseInterface; diff --git a/tests/SP/Repositories/AccountToTagRepositoryTest.php b/tests/SP/Repositories/AccountToTagRepositoryTest.php index ba9b7861..01bcb3fe 100644 --- a/tests/SP/Repositories/AccountToTagRepositoryTest.php +++ b/tests/SP/Repositories/AccountToTagRepositoryTest.php @@ -30,8 +30,8 @@ use SP\Core\Context\ContextException; use SP\Core\Exceptions\ConstraintException; use SP\Core\Exceptions\QueryException; use SP\DataModel\ItemData; +use SP\Domain\Account\Dtos\AccountRequest; use SP\Domain\Account\Ports\AccountToTagRepositoryInterface; -use SP\Domain\Account\Services\AccountRequest; use SP\Infrastructure\Account\Repositories\AccountToTagRepository; use SP\Tests\DatabaseTestCase; use function SP\Tests\setupContext; diff --git a/tests/SP/Repositories/AccountToUserGroupRepositoryTest.php b/tests/SP/Repositories/AccountToUserGroupRepositoryTest.php index ad68fec7..5166ea21 100644 --- a/tests/SP/Repositories/AccountToUserGroupRepositoryTest.php +++ b/tests/SP/Repositories/AccountToUserGroupRepositoryTest.php @@ -30,8 +30,8 @@ use SP\Core\Context\ContextException; use SP\Core\Exceptions\ConstraintException; use SP\Core\Exceptions\QueryException; use SP\DataModel\ItemData; +use SP\Domain\Account\Dtos\AccountRequest; use SP\Domain\Account\Ports\AccountToUserGroupRepositoryInterface; -use SP\Domain\Account\Services\AccountRequest; use SP\Infrastructure\Account\Repositories\AccountToUserGroupRepository; use SP\Tests\DatabaseTestCase; use function SP\Tests\setupContext; diff --git a/tests/SP/Repositories/AccountToUserRepositoryTest.php b/tests/SP/Repositories/AccountToUserRepositoryTest.php index 970c19e2..f3113a09 100644 --- a/tests/SP/Repositories/AccountToUserRepositoryTest.php +++ b/tests/SP/Repositories/AccountToUserRepositoryTest.php @@ -30,8 +30,8 @@ use SP\Core\Context\ContextException; use SP\Core\Exceptions\ConstraintException; use SP\Core\Exceptions\QueryException; use SP\DataModel\ItemData; +use SP\Domain\Account\Dtos\AccountRequest; use SP\Domain\Account\Ports\AccountToUserRepositoryInterface; -use SP\Domain\Account\Services\AccountRequest; use SP\Infrastructure\Account\Repositories\AccountToUserRepository; use SP\Tests\DatabaseTestCase; use function SP\Tests\setupContext; diff --git a/tests/SP/Services/Account/AccountServiceTest.php b/tests/SP/Services/Account/AccountServiceTest.php index 54218a55..23352e7e 100644 --- a/tests/SP/Services/Account/AccountServiceTest.php +++ b/tests/SP/Services/Account/AccountServiceTest.php @@ -40,12 +40,12 @@ use SP\DataModel\AccountVData; use SP\DataModel\ItemSearchData; use SP\DataModel\ProfileData; use SP\Domain\Account\Adapters\AccountData; +use SP\Domain\Account\Dtos\AccountBulkRequest; +use SP\Domain\Account\Dtos\AccountRequest; use SP\Domain\Account\Ports\AccountHistoryServiceInterface; use SP\Domain\Account\Search\AccountSearchFilter; -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\AccountService; use SP\Domain\Common\Services\ServiceException; use SP\Domain\User\Services\UserLoginResponse; diff --git a/tests/SP/Services/Task/TaskServiceTest.php b/tests/SP/Services/Task/TaskServiceTest.php index bf728caf..e8522dd4 100644 --- a/tests/SP/Services/Task/TaskServiceTest.php +++ b/tests/SP/Services/Task/TaskServiceTest.php @@ -1,10 +1,10 @@ . + * along with sysPass. If not, see . */ namespace SP\Tests\Services\Task; @@ -51,7 +51,7 @@ class TaskServiceTest extends TestCase { $this->markTestSkipped(); - $task = TaskFactory::create(__FUNCTION__, Task::genTaskId(__FUNCTION__)); + $task = TaskFactory::register(__FUNCTION__, Task::genTaskId(__FUNCTION__)); $this->assertFileExists($task->getFileTask()->getFile());