chore(tests): UT for UserMasterPass service

Signed-off-by: Rubén D <nuxsmin@syspass.org>
This commit is contained in:
Rubén D
2024-04-07 07:44:23 +02:00
parent eea34cf282
commit 1d2e991be5
50 changed files with 1994 additions and 1076 deletions

View File

@@ -43,7 +43,7 @@ use SP\Domain\Core\Exceptions\SPException;
use SP\Domain\Core\PhpExtensionCheckerService;
use SP\Domain\Core\UI\ThemeInterface;
use SP\Domain\Http\RequestInterface;
use SP\Domain\User\Services\UserLoginResponse;
use SP\Domain\User\Dtos\UserDataDto;
use SP\Modules\Web\Controllers\Helpers\LayoutHelper;
use SP\Modules\Web\Controllers\Traits\WebControllerTrait;
use SP\Mvc\Controller\WebControllerHelper;
@@ -71,9 +71,9 @@ abstract class ControllerBase
protected ConfigDataInterface $configData;
protected RequestInterface $request;
protected PhpExtensionCheckerService $extensionChecker;
protected TemplateInterface $view;
protected ?UserLoginResponse $userData = null;
protected ?ProfileData $userProfileData = null;
protected TemplateInterface $view;
protected ?UserDataDto $userData = null;
protected ?ProfileData $userProfileData = null;
protected bool $isAjax;
protected LayoutHelper $layoutHelper;
protected string $actionName;

View File

@@ -28,9 +28,9 @@ use Exception;
use JsonException;
use SP\Core\Application;
use SP\Core\Events\Event;
use SP\Domain\User\Dtos\UserDataDto;
use SP\Domain\User\Models\UserPreferences;
use SP\Domain\User\Ports\UserServiceInterface;
use SP\Domain\User\Services\UserLoginResponse;
use SP\Domain\User\Services\UserService;
use SP\Http\JsonMessage;
use SP\Modules\Web\Controllers\SimpleControllerBase;
@@ -87,11 +87,11 @@ final class SaveController extends SimpleControllerBase
}
/**
* @param UserLoginResponse $userData
* @param UserDataDto $userData
*
* @return UserPreferences
*/
private function getUserPreferencesData(UserLoginResponse $userData): UserPreferences
private function getUserPreferencesData(UserDataDto $userData): UserPreferences
{
$userPreferencesData = clone $userData->getPreferences();

View File

@@ -4,7 +4,7 @@
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org
* @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
@@ -29,7 +29,7 @@ use SP\Domain\Account\Dtos\AccountCacheDto;
use SP\Domain\Account\Dtos\AccountSearchFilterDto;
use SP\Domain\Core\Context\SessionContextInterface;
use SP\Domain\Core\Crypt\VaultInterface;
use SP\Domain\User\Services\UserLoginResponse;
use SP\Domain\User\Dtos\UserDataDto;
use function SP\__u;
use function SP\getLastCaller;
@@ -145,9 +145,9 @@ class SessionContext extends ContextBase implements SessionContextInterface
/**
* Establece los datos del usuario en la sesión.
*/
public function setUserData(?UserLoginResponse $userLoginResponse = null): void
public function setUserData(?UserDataDto $userDataDto = null): void
{
$this->setContextKey('userData', $userLoginResponse);
$this->setContextKey('userData', $userDataDto);
}
/**
@@ -193,9 +193,9 @@ class SessionContext extends ContextBase implements SessionContextInterface
/**
* Devuelve los datos del usuario en la sesión.
*/
public function getUserData(): UserLoginResponse
public function getUserData(): UserDataDto
{
return $this->getContextKey('userData', new UserLoginResponse());
return $this->getContextKey('userData', new UserDataDto());
}
/**

View File

@@ -4,7 +4,7 @@
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org
* @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
@@ -25,7 +25,7 @@
namespace SP\Core\Context;
use SP\DataModel\ProfileData;
use SP\Domain\User\Services\UserLoginResponse;
use SP\Domain\User\Dtos\UserDataDto;
use function SP\processException;
@@ -39,9 +39,9 @@ class StatelessContext extends ContextBase
/**
* Establece los datos del usuario en la sesión.
*/
public function setUserData(?UserLoginResponse $userLoginResponse = null): void
public function setUserData(?UserDataDto $userDataDto = null): void
{
$this->setContextKey('userData', $userLoginResponse);
$this->setContextKey('userData', $userDataDto);
}
/**
@@ -99,9 +99,9 @@ class StatelessContext extends ContextBase
/**
* Devuelve los datos del usuario en la sesión.
*/
public function getUserData(): UserLoginResponse
public function getUserData(): UserDataDto
{
return $this->getContextKey('userData', new UserLoginResponse());
return $this->getContextKey('userData', new UserDataDto());
}
/**

View File

@@ -96,7 +96,7 @@ use SP\Providers\Auth\AuthTypeEnum;
use SP\Providers\Auth\Browser\BrowserAuth;
use SP\Providers\Auth\Browser\BrowserAuthInterface;
use SP\Providers\Auth\Database\DatabaseAuth;
use SP\Providers\Auth\Database\DatabaseAuthInterface;
use SP\Providers\Auth\Database\DatabaseAuthService;
use SP\Providers\Auth\Ldap\LdapActions;
use SP\Providers\Auth\Ldap\LdapAuth;
use SP\Providers\Auth\Ldap\LdapBase;
@@ -166,7 +166,7 @@ final class CoreDefinitions
),
ThemeInterface::class => autowire(Theme::class),
TemplateInterface::class => autowire(Template::class),
DatabaseAuthInterface::class => autowire(DatabaseAuth::class),
DatabaseAuthService::class => autowire(DatabaseAuth::class),
BrowserAuthInterface::class => autowire(BrowserAuth::class),
LdapParams::class => factory([LdapParams::class, 'getFrom']),
LdapConnectionInterface::class => autowire(LdapConnection::class),
@@ -178,11 +178,11 @@ final class CoreDefinitions
),
AuthProviderInterface::class => factory(
static function (
AuthProvider $authProvider,
ConfigDataInterface $configData,
LdapAuthService $ldapAuth,
BrowserAuthInterface $browserAuth,
DatabaseAuthInterface $databaseAuth,
AuthProvider $authProvider,
ConfigDataInterface $configData,
LdapAuthService $ldapAuth,
BrowserAuthInterface $browserAuth,
DatabaseAuthService $databaseAuth,
) {
if ($configData->isLdapEnabled()) {
$authProvider->registerAuth($ldapAuth, AuthTypeEnum::Ldap);

View File

@@ -4,7 +4,7 @@
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org
* @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
@@ -29,7 +29,7 @@ use SP\Domain\Account\Adapters\AccountPermission;
use SP\Domain\Account\Dtos\AccountAclDto;
use SP\Domain\Core\Exceptions\ConstraintException;
use SP\Domain\Core\Exceptions\QueryException;
use SP\Domain\User\Services\UserLoginResponse;
use SP\Domain\User\Dtos\UserDataDto;
/**
* Class AccountAclService
@@ -41,12 +41,12 @@ interface AccountAclService
/**
* Sets grants which don't need the account's data
*
* @param UserLoginResponse $userData
* @param UserDataDto $userData
* @param ProfileData $profileData
*
* @return bool
*/
public static function getShowPermission(UserLoginResponse $userData, ProfileData $profileData): bool;
public static function getShowPermission(UserDataDto $userData, ProfileData $profileData): bool;
/**
* Obtener la ACL de una cuenta

View File

@@ -56,7 +56,7 @@ use SP\Domain\Core\Exceptions\QueryException;
use SP\Domain\Core\Exceptions\SPException;
use SP\Domain\ItemPreset\Ports\ItemPresetInterface;
use SP\Domain\ItemPreset\Ports\ItemPresetService;
use SP\Domain\User\Services\UserLoginResponse;
use SP\Domain\User\Dtos\UserDataDto;
use SP\Infrastructure\Common\Repositories\NoSuchItemException;
use SP\Infrastructure\Database\QueryResult;
@@ -247,14 +247,14 @@ final class Account extends Service implements AccountService
}
/**
* @param UserLoginResponse $userData
* @param UserDataDto $userData
* @param ProfileData $userProfile
* @param AccountModel $account
*
* @return bool
*/
protected function userCanChangeOwner(
UserLoginResponse $userData,
UserDataDto $userData,
ProfileData $userProfile,
AccountModel $account
): bool {
@@ -263,14 +263,14 @@ final class Account extends Service implements AccountService
}
/**
* @param UserLoginResponse $userData
* @param UserDataDto $userData
* @param ProfileData $userProfile
* @param AccountModel $account
*
* @return bool
*/
protected function userCanChangeGroup(
UserLoginResponse $userData,
UserDataDto $userData,
ProfileData $userProfile,
AccountModel $account
): bool {
@@ -335,13 +335,13 @@ final class Account extends Service implements AccountService
}
/**
* @param UserLoginResponse $userData
* @param UserDataDto $userData
* @param AccountCreateDto $accountCreateDto
*
* @return AccountCreateDto
*/
private static function buildWithUserData(
UserLoginResponse $userData,
UserDataDto $userData,
AccountCreateDto $accountCreateDto
): AccountCreateDto {
return $accountCreateDto->withUserGroupId($userData->getUserGroupId())->withUserId($userData->getId());

View File

@@ -24,7 +24,6 @@
namespace SP\Domain\Account\Services;
use SP\Core\Acl\Acl;
use SP\Core\Application;
use SP\Core\Events\Event;
use SP\Core\Events\EventMessage;
@@ -38,8 +37,8 @@ use SP\Domain\Core\Acl\AclInterface;
use SP\Domain\Core\Exceptions\ConstraintException;
use SP\Domain\Core\Exceptions\QueryException;
use SP\Domain\Storage\Ports\FileCacheService;
use SP\Domain\User\Dtos\UserDataDto;
use SP\Domain\User\Ports\UserToUserGroupServiceInterface;
use SP\Domain\User\Services\UserLoginResponse;
use SP\Infrastructure\File\FileException;
use function SP\processException;
@@ -56,25 +55,19 @@ final class AccountAcl extends Service implements AccountAclService
*/
public const ACL_PATH = CACHE_PATH . DIRECTORY_SEPARATOR . 'accountAcl' . DIRECTORY_SEPARATOR;
private ?AccountAclDto $accountAclDto = null;
private ?AccountPermission $accountAcl = null;
private Acl $acl;
private ?FileCacheService $fileCache;
private UserToUserGroupServiceInterface $userToUserGroupService;
private UserLoginResponse $userData;
private ?AccountAclDto $accountAclDto = null;
private ?AccountPermission $accountAcl = null;
private UserDataDto $userData;
public function __construct(
Application $application,
AclInterface $acl,
UserToUserGroupServiceInterface $userGroupService,
?FileCacheService $fileCache = null
Application $application,
private readonly AclInterface $acl,
private readonly UserToUserGroupServiceInterface $userToUserGroupService,
private readonly ?FileCacheService $fileCache = null
) {
parent::__construct($application);
$this->acl = $acl;
$this->userToUserGroupService = $userGroupService;
$this->userData = $this->context->getUserData();
$this->fileCache = $fileCache;
}
/**
@@ -130,12 +123,12 @@ final class AccountAcl extends Service implements AccountAclService
/**
* Sets grants which don't need the account's data
*
* @param UserLoginResponse $userData
* @param UserDataDto $userData
* @param ProfileData $profileData
*
* @return bool
*/
public static function getShowPermission(UserLoginResponse $userData, ProfileData $profileData): bool
public static function getShowPermission(UserDataDto $userData, ProfileData $profileData): bool
{
return $userData->getIsAdminApp()
|| $userData->getIsAdminAcc()

View File

@@ -31,7 +31,7 @@ use SP\Domain\Account\Ports\AccountFilterBuilder;
use SP\Domain\Account\Ports\AccountSearchConstants;
use SP\Domain\Config\Ports\ConfigDataInterface;
use SP\Domain\Core\Context\ContextInterface;
use SP\Domain\User\Services\UserLoginResponse;
use SP\Domain\User\Dtos\UserDataDto;
/**
* Class AccountFilterUser
@@ -91,14 +91,14 @@ final class AccountFilter implements AccountFilterBuilder
}
/**
* @param UserLoginResponse $userData
* @param UserDataDto $userData
* @param bool $useGlobalSearch
* @param ProfileData|null $userProfile
*
* @return bool
*/
private function isFilterWithoutGlobalSearch(
UserLoginResponse $userData,
UserDataDto $userData,
bool $useGlobalSearch,
?ProfileData $userProfile
): bool {

View File

@@ -46,9 +46,9 @@ use SP\Domain\Core\Exceptions\InvalidClassException;
use SP\Domain\Core\Exceptions\SPException;
use SP\Domain\Security\Dtos\TrackRequest;
use SP\Domain\Security\Ports\TrackService;
use SP\Domain\User\Dtos\UserDataDto;
use SP\Domain\User\Ports\UserProfileServiceInterface;
use SP\Domain\User\Ports\UserServiceInterface;
use SP\Domain\User\Services\UserService;
use SP\Infrastructure\Common\Repositories\NoSuchItemException;
use SP\Modules\Api\Controllers\Help\HelpInterface;
use SP\Util\Filter;
@@ -216,7 +216,7 @@ final class Api extends Service implements ApiService
*/
private function setupUser(): void
{
$userLoginResponse = UserService::mapUserLoginResponse(
$userLoginResponse = new UserDataDto(
$this->userService->getById($this->authToken->getUserId())
);
$userLoginResponse->getIsDisabled() && $this->accessDenied();

View File

@@ -29,7 +29,7 @@ namespace SP\Domain\Auth\Dtos;
*
* @package SP\Domain\Auth\Services
*/
final class LoginResponse
final class LoginResponseDto
{
private int $status;
private ?string $redirect;

View File

@@ -4,7 +4,7 @@
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org
* @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
@@ -22,20 +22,30 @@
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\DataModel;
namespace SP\Domain\Auth\Dtos;
use SP\Domain\User\Services\UserLoginResponse;
use SP\Domain\User\Dtos\UserDataDto;
/**
* Class UserLoginData
*
* @package SP\DataModel
* Class UserLoginDto
*/
class UserLoginData
class UserLoginDto
{
protected ?string $loginUser = null;
protected ?string $loginPass = null;
protected ?UserLoginResponse $userLoginResponse = null;
protected ?string $loginUser = null;
protected ?string $loginPass = null;
protected ?UserDataDto $userDataDto = null;
/**
* @param string|null $loginUser
* @param string|null $loginPass
* @param UserDataDto|null $userDataDto
*/
public function __construct(?string $loginUser = null, ?string $loginPass = null, ?UserDataDto $userDataDto = null)
{
$this->loginUser = $loginUser;
$this->loginPass = $loginPass;
$this->userDataDto = $userDataDto;
}
public function getLoginUser(): ?string
{
@@ -57,13 +67,13 @@ class UserLoginData
$this->loginPass = $loginPass;
}
public function getUserLoginResponse(): ?UserLoginResponse
public function getUserDataDto(): ?UserDataDto
{
return $this->userLoginResponse;
return $this->userDataDto;
}
public function setUserLoginResponse(UserLoginResponse $userLoginResponse = null): void
public function setUserDataDto(UserDataDto $userDataDto = null): void
{
$this->userLoginResponse = $userLoginResponse;
$this->userDataDto = $userDataDto;
}
}

View File

@@ -26,7 +26,7 @@ namespace SP\Domain\Auth\Ports;
use Defuse\Crypto\Exception\EnvironmentIsBrokenException;
use Exception;
use SP\Domain\Auth\Dtos\LoginResponse;
use SP\Domain\Auth\Dtos\LoginResponseDto;
use SP\Domain\Auth\Services\AuthException;
use SP\Domain\Core\Exceptions\ConstraintException;
use SP\Domain\Core\Exceptions\QueryException;
@@ -42,7 +42,7 @@ interface LoginService
/**
* Ejecutar las acciones de login
*
* @return LoginResponse
* @return LoginResponseDto
* @throws AuthException
* @throws SPException
* @throws EnvironmentIsBrokenException
@@ -54,7 +54,7 @@ interface LoginService
* @uses Login::authLdap()
*
*/
public function doLogin(): LoginResponse;
public function doLogin(): LoginResponseDto;
/**
* @param string|null $from

View File

@@ -24,19 +24,21 @@
namespace SP\Domain\Auth\Services;
use Defuse\Crypto\Exception\CryptoException;
use Defuse\Crypto\Exception\EnvironmentIsBrokenException;
use Exception;
use SP\Core\Application;
use SP\Core\Events\Event;
use SP\Core\Events\EventMessage;
use SP\DataModel\UserLoginData;
use SP\Domain\Auth\Dtos\LoginResponse;
use SP\DataModel\ProfileData;
use SP\Domain\Auth\Dtos\LoginResponseDto;
use SP\Domain\Auth\Dtos\UserLoginDto;
use SP\Domain\Auth\Ports\LdapAuthService;
use SP\Domain\Auth\Ports\LoginService;
use SP\Domain\Common\Services\Service;
use SP\Domain\Common\Services\ServiceException;
use SP\Domain\Config\Ports\ConfigDataInterface;
use SP\Domain\Core\Exceptions\ConstraintException;
use SP\Domain\Core\Exceptions\CryptException;
use SP\Domain\Core\Exceptions\InvalidArgumentException;
use SP\Domain\Core\Exceptions\QueryException;
use SP\Domain\Core\Exceptions\SPException;
@@ -45,13 +47,12 @@ use SP\Domain\Crypt\Ports\TemporaryMasterPassService;
use SP\Domain\Http\RequestInterface;
use SP\Domain\Security\Dtos\TrackRequest;
use SP\Domain\Security\Ports\TrackService;
use SP\Domain\User\Models\UserPreferences;
use SP\Domain\User\Ports\UserMasterPassService;
use SP\Domain\User\Ports\UserPassRecoverService;
use SP\Domain\User\Ports\UserPassServiceInterface;
use SP\Domain\User\Ports\UserProfileServiceInterface;
use SP\Domain\User\Ports\UserServiceInterface;
use SP\Domain\User\Services\UserLoginRequest;
use SP\Domain\User\Services\UserPassService;
use SP\Domain\User\Services\UserMasterPassStatus;
use SP\Http\Uri;
use SP\Infrastructure\Common\Repositories\NoSuchItemException;
use SP\Providers\Auth\AuthProviderInterface;
@@ -61,6 +62,7 @@ use SP\Providers\Auth\Ldap\LdapAuthData;
use SP\Providers\Auth\Ldap\LdapCodeEnum;
use SP\Util\PasswordUtil;
use function SP\__;
use function SP\__u;
/**
@@ -77,7 +79,7 @@ final class Login extends Service implements LoginService
private const STATUS_PASS = 0;
private const STATUS_NONE = 100;
private UserLoginData $userLoginData;
private UserLoginDto $userLoginData;
private ConfigDataInterface $configData;
private TrackRequest $trackRequest;
private ?string $from = null;
@@ -94,13 +96,13 @@ final class Login extends Service implements LoginService
private readonly UserServiceInterface $userService,
private readonly UserPassRecoverService $userPassRecoverService,
private readonly TemporaryMasterPassService $temporaryMasterPassService,
private readonly UserPassServiceInterface $userPassService,
private readonly UserMasterPassService $userMasterPassService,
private readonly UserProfileServiceInterface $userProfileService
) {
parent::__construct($application);
$this->configData = $this->config->getConfigData();
$this->userLoginData = new UserLoginData();
$this->userLoginData = new UserLoginDto();
$this->trackRequest = $this->trackService->buildTrackRequest(__CLASS__);
$this->authProvider->initialize();
}
@@ -108,7 +110,7 @@ final class Login extends Service implements LoginService
/**
* Ejecutar las acciones de login
*
* @return LoginResponse
* @return LoginResponseDto
* @throws AuthException
* @throws SPException
* @throws EnvironmentIsBrokenException
@@ -120,7 +122,7 @@ final class Login extends Service implements LoginService
* @uses Login::authLdap()
*
*/
public function doLogin(): LoginResponse
public function doLogin(): LoginResponseDto
{
$user = $this->request->analyzeString('user');
$pass = $this->request->analyzeEncrypted('pass');
@@ -191,7 +193,7 @@ final class Login extends Service implements LoginService
$uri->addParam('r', 'index');
}
return new LoginResponse(self::STATUS_PASS, $uri->getUri());
return new LoginResponseDto(self::STATUS_PASS, $uri->getUri());
}
/**
@@ -208,7 +210,8 @@ final class Login extends Service implements LoginService
__u('Internal error'),
SPException::ERROR,
null,
Service::STATUS_INTERNAL_ERROR
Service::STATUS_INTERNAL_ERROR,
$e
);
}
}
@@ -216,15 +219,15 @@ final class Login extends Service implements LoginService
/**
* Comprobar estado del usuario
*
* @return LoginResponse
* @return LoginResponseDto
* @throws EnvironmentIsBrokenException
* @throws ConstraintException
* @throws QueryException
* @throws AuthException
*/
private function checkUser(): LoginResponse
private function checkUser(): LoginResponseDto
{
$userLoginResponse = $this->userLoginData->getUserLoginResponse();
$userLoginResponse = $this->userLoginData->getUserDataDto();
if ($userLoginResponse !== null) {
// Comprobar si el usuario está deshabilitado
@@ -263,17 +266,19 @@ final class Login extends Service implements LoginService
$uri = new Uri('index.php');
$uri->addParam('r', 'userPassReset/reset/' . $hash);
return new LoginResponse(self::STATUS_PASS_RESET, $uri->getUri());
return new LoginResponseDto(self::STATUS_PASS_RESET, $uri->getUri());
}
}
return new LoginResponse(self::STATUS_NONE);
return new LoginResponseDto(self::STATUS_NONE);
}
/**
* Cargar la clave maestra o solicitarla
*
* @throws AuthException
* @throws CryptException
* @throws NoSuchItemException
* @throws SPException
*/
private function loadMasterPass(): void
@@ -283,76 +288,21 @@ final class Login extends Service implements LoginService
try {
if ($masterPass) {
if ($this->temporaryMasterPassService->checkTempMasterPass($masterPass)) {
$this->eventDispatcher->notify(
'login.masterPass.temporary',
new Event($this, EventMessage::factory()->addDescription(__u('Using temporary password')))
);
$masterPass = $this->temporaryMasterPassService->getUsingKey($masterPass);
}
if ($this->userPassService->updateMasterPassOnLogin(
$masterPass,
$this->userLoginData
)->getStatus() !== UserPassService::MPASS_OK
) {
$this->eventDispatcher->notify(
'login.masterPass',
new Event($this, EventMessage::factory()->addDescription(__u('Wrong master password')))
);
$this->addTracking();
throw new AuthException(
__u('Wrong master password'),
SPException::INFO,
null,
self::STATUS_INVALID_MASTER_PASS
);
}
$this->eventDispatcher->notify(
'login.masterPass',
new Event($this, EventMessage::factory()->addDescription(__u('Master password updated')))
);
$this->checkMasterPass($masterPass);
} elseif ($oldPass) {
if ($this->userPassService->updateMasterPassFromOldPass(
$oldPass,
$this->userLoginData
)->getStatus() !== UserPassService::MPASS_OK
) {
$this->eventDispatcher->notify(
'login.masterPass',
new Event($this, EventMessage::factory()->addDescription(__u('Wrong master password')))
);
$this->addTracking();
throw new AuthException(
__u('Wrong master password'),
SPException::INFO,
null,
self::STATUS_INVALID_MASTER_PASS
);
}
$this->eventDispatcher->notify(
'login.masterPass',
new Event($this, EventMessage::factory()->addDescription(__u('Master password updated')))
);
$this->loadMasterPassUsingOld($oldPass);
} else {
switch ($this->userPassService->loadUserMPass($this->userLoginData)->getStatus()) {
case UserPassService::MPASS_CHECKOLD:
switch ($this->userMasterPassService->load($this->userLoginData)->getUserMasterPassStatus()) {
case UserMasterPassStatus::CheckOld:
throw new AuthException(
__u('Your previous password is needed'),
SPException::INFO,
null,
self::STATUS_NEED_OLD_PASS
);
case UserPassService::MPASS_NOTSET:
case UserPassService::MPASS_CHANGED:
case UserPassService::MPASS_WRONG:
case UserMasterPassStatus::NotSet:
case UserMasterPassStatus::Changed:
case UserMasterPassStatus::Invalid:
$this->addTracking();
throw new AuthException(
@@ -361,9 +311,15 @@ final class Login extends Service implements LoginService
null,
self::STATUS_INVALID_MASTER_PASS
);
case UserMasterPassStatus::Ok:
$this->eventDispatcher->notify(
'login.masterPass',
new Event($this, EventMessage::factory()->addDescription(__u('Master password loaded')))
);
break;
}
}
} catch (CryptoException $e) {
} catch (ServiceException $e) {
$this->eventDispatcher->notify('exception', new Event($e));
throw new AuthException(
@@ -376,6 +332,81 @@ final class Login extends Service implements LoginService
}
}
/**
* @param string $masterPass
* @return void
* @throws AuthException
* @throws NoSuchItemException
* @throws ServiceException
* @throws CryptException
*/
private function checkMasterPass(string $masterPass): void
{
if ($this->temporaryMasterPassService->checkTempMasterPass($masterPass)) {
$this->eventDispatcher->notify(
'login.masterPass.temporary',
new Event($this, EventMessage::factory()->addDescription(__u('Using temporary password')))
);
$masterPass = $this->temporaryMasterPassService->getUsingKey($masterPass);
}
if ($this->userMasterPassService->updateOnLogin($masterPass, $this->userLoginData)
->getUserMasterPassStatus() !== UserMasterPassStatus::Ok
) {
$this->eventDispatcher->notify(
'login.masterPass',
new Event($this, EventMessage::factory()->addDescription(__u('Wrong master password')))
);
$this->addTracking();
throw new AuthException(
__u('Wrong master password'),
SPException::INFO,
null,
self::STATUS_INVALID_MASTER_PASS
);
}
$this->eventDispatcher->notify(
'login.masterPass',
new Event($this, EventMessage::factory()->addDescription(__u('Master password updated')))
);
}
/**
* @param string $oldPass
* @return void
* @throws AuthException
* @throws SPException
*/
private function loadMasterPassUsingOld(string $oldPass): void
{
if ($this->userMasterPassService->updateFromOldPass($oldPass, $this->userLoginData)
->getUserMasterPassStatus() !== UserMasterPassStatus::Ok
) {
$this->eventDispatcher->notify(
'login.masterPass',
new Event($this, EventMessage::factory()->addDescription(__u('Wrong master password')))
);
$this->addTracking();
throw new AuthException(
__u('Wrong master password'),
SPException::INFO,
null,
self::STATUS_INVALID_MASTER_PASS
);
}
$this->eventDispatcher->notify(
'login.masterPass',
new Event($this, EventMessage::factory()->addDescription(__u('Master password updated')))
);
}
/**
* Cargar la sesión del usuario
*
@@ -385,26 +416,24 @@ final class Login extends Service implements LoginService
*/
private function setUserSession(): void
{
$userLoginResponse = $this->userLoginData->getUserLoginResponse();
$userLoginResponse = $this->userLoginData->getUserDataDto();
// Actualizar el último login del usuario
$this->userService->updateLastLoginById($userLoginResponse->getId());
if ($this->context->getTrasientKey('mpass_updated')) {
$userLoginResponse->setLastUpdateMPass(time());
}
// if ($this->context->getTrasientKey(UserMasterPass::SESSION_MASTERPASS_UPDATED)) {
// $this->context->setTrasientKey('user_master_pass_last_update', time());
// }
// Cargar las variables de ussuario en la sesión
$this->context->setUserData($userLoginResponse);
$this->context->setUserProfile(
$this->userProfileService->getById($userLoginResponse->getUserProfileId())->getProfile()
$this->userProfileService
->getById($userLoginResponse->getUserProfileId())
->hydrate(ProfileData::class)
);
$this->context->setLocale($userLoginResponse->getPreferences()->getLang());
if ($this->configData->isDemoEnabled()) {
$userLoginResponse->setPreferences(new UserPreferences());
}
$this->eventDispatcher->notify(
'login.session.load',
new Event($this, EventMessage::factory()->addDetail(__u('User'), $userLoginResponse->getLogin()))
@@ -426,7 +455,7 @@ final class Login extends Service implements LoginService
*/
private function cleanUserData(): void
{
$this->userLoginData->setUserLoginResponse();
$this->userLoginData->setUserDataDto();
}
/**

View File

@@ -4,7 +4,7 @@
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org
* @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
@@ -27,7 +27,8 @@ namespace SP\Domain\Core\Context;
use SP\Core\Context\ContextException;
use SP\DataModel\ProfileData;
use SP\Domain\Account\Dtos\AccountCacheDto;
use SP\Domain\User\Services\UserLoginResponse;
use SP\Domain\User\Dtos\UserDataDto;
use SP\Domain\User\Services\UserData;
/**
* Class ContextInterface
@@ -58,7 +59,7 @@ interface ContextInterface
/**
* Establece los datos del usuario en la sesión.
*/
public function setUserData(?UserLoginResponse $userLoginResponse = null);
public function setUserData(?UserDataDto $userDataDto = null);
/**
* Obtiene el objeto de perfil de usuario de la sesión.
@@ -78,7 +79,7 @@ interface ContextInterface
/**
* Devuelve los datos del usuario en la sesión.
*/
public function getUserData(): UserLoginResponse;
public function getUserData(): UserDataDto;
/**
* Establecer el lenguaje de la sesión

View File

@@ -24,10 +24,10 @@
namespace SP\Domain\Crypt\Ports;
use Defuse\Crypto\Exception\CryptoException;
use PHPMailer\PHPMailer\Exception;
use SP\Domain\Common\Services\ServiceException;
use SP\Domain\Core\Exceptions\ConstraintException;
use SP\Domain\Core\Exceptions\CryptException;
use SP\Domain\Core\Exceptions\QueryException;
use SP\Infrastructure\Common\Repositories\NoSuchItemException;
@@ -82,7 +82,7 @@ interface TemporaryMasterPassService
* @return string con la clave maestra desencriptada
* @throws NoSuchItemException
* @throws ServiceException
* @throws CryptoException
* @throws CryptException
*/
public function getUsingKey(string $key): string;
}

View File

@@ -0,0 +1,151 @@
<?php
/*
* sysPass
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2024, 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\User\Dtos;
use SP\Domain\User\Models\User;
use SP\Domain\User\Models\UserPreferences;
/**
* Class UserLoginResponse
*/
final readonly class UserDataDto
{
private ?UserPreferences $preferences;
public function __construct(private ?User $user = null)
{
$this->preferences = $this->user?->hydrate(UserPreferences::class);
}
public function getLogin(): ?string
{
return $this->user->getLogin();
}
public function getSsoLogin(): ?string
{
return $this->user->getSsoLogin();
}
public function getName(): ?string
{
return $this->user->getName();
}
public function getEmail(): ?string
{
return $this->user->getEmail();
}
public function getUserGroupId(): int
{
return (int)$this->user->getUserGroupId();
}
public function getUserProfileId(): int
{
return (int)$this->user->getUserProfileId();
}
public function getIsAdminApp(): bool
{
return (bool)$this->user->isAdminApp();
}
public function getIsAdminAcc(): bool
{
return (bool)$this->user->isAdminAcc();
}
public function getIsDisabled(): bool
{
return (bool)$this->user->isDisabled();
}
public function getIsChangePass(): bool
{
return (bool)$this->user->isChangePass();
}
public function getIsChangedPass(): bool
{
return (bool)$this->user->isChangedPass();
}
public function getIsLdap(): bool
{
return (bool)$this->user->isLdap();
}
public function getIsMigrate(): bool
{
return (bool)$this->user->isMigrate();
}
public function getPreferences(): ?UserPreferences
{
return $this->preferences;
}
public function getPass(): ?string
{
return $this->user->getPass();
}
public function getMPass(): ?string
{
return $this->user->getMPass();
}
public function getMKey(): ?string
{
return $this->user->getMKey();
}
public function getLastUpdateMPass(): int
{
return $this->user->getLastUpdateMPass();
}
public function getHashSalt(): ?string
{
return $this->user->getHashSalt();
}
public function getId(): ?int
{
return $this->user->getId();
}
public function getUserGroupName(): ?string
{
return $this->user->offsetGet('userGroup.name');
}
public function getLastUpdate(): int
{
return (int)strtotime($this->user->getLastUpdate());
}
}

View File

@@ -4,7 +4,7 @@
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org
* @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
@@ -22,37 +22,29 @@
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Domain\User\Services;
namespace SP\Domain\User\Dtos;
use SP\Domain\User\Services\UserMasterPassStatus;
/**
* Class UserPassResponse
*
* @package SP\DataModel\Dto
* Class UserMasterPassDto
*/
final class UserPassResponse
final readonly class UserMasterPassDto
{
private int $status;
private ?string $cryptMasterPass = null;
private ?string $cryptSecuredKey = null;
private ?string $clearMasterPass;
/**
* UserPassResponse constructor.
*/
public function __construct(int $status, ?string $clearUserMPass = null)
{
$this->status = $status;
$this->clearMasterPass = $clearUserMPass;
public function __construct(
private UserMasterPassStatus $userMasterPassStatus,
private ?string $clearMasterPass = null,
private ?string $cryptMasterPass = null,
private ?string $cryptSecuredKey = null
) {
}
public function getStatus(): int
public function getUserMasterPassStatus(): UserMasterPassStatus
{
return $this->status;
}
public function setStatus(int $status): void
{
$this->status = $status;
return $this->userMasterPassStatus;
}
public function getCryptMasterPass(): ?string
@@ -60,23 +52,13 @@ final class UserPassResponse
return $this->cryptMasterPass;
}
public function setCryptMasterPass(string $cryptMasterPass): void
{
$this->cryptMasterPass = $cryptMasterPass;
}
public function getCryptSecuredKey(): ?string
{
return $this->cryptSecuredKey;
}
public function setCryptSecuredKey(string $cryptSecuredKey): void
{
$this->cryptSecuredKey = $cryptSecuredKey;
}
public function getClearMasterPass(): ?string
{
return $this->clearMasterPass;
}
}
}

View File

@@ -0,0 +1,65 @@
<?php
/*
* sysPass
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2024, 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\User\Ports;
use SP\Domain\Auth\Dtos\UserLoginDto;
use SP\Domain\Common\Services\ServiceException;
use SP\Domain\User\Dtos\UserMasterPassDto;
/**
* Class UserPassService
*
* @package SP\Domain\User\Services
*/
interface UserMasterPassService
{
/**
* Actualizar la clave maestra con la clave anterior del usuario
*
* @throws ServiceException
*/
public function updateFromOldPass(string $oldUserPass, UserLoginDto $userLoginDto): UserMasterPassDto;
/**
* Comprueba la clave maestra del usuario.
*
* @throws ServiceException
*/
public function load(UserLoginDto $userLoginDto, ?string $userPass = null): UserMasterPassDto;
/**
* Actualizar la clave maestra del usuario al realizar login
*
* @throws ServiceException
*/
public function updateOnLogin(string $userMasterPass, UserLoginDto $userLoginDto): UserMasterPassDto;
/**
* Actualizar la clave maestra del usuario en la BBDD.
*
* @throws ServiceException
*/
public function create(string $masterPass, string $userLogin, string $userPass): UserMasterPassDto;
}

View File

@@ -1,87 +0,0 @@
<?php
/*
* sysPass
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
* sysPass is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* sysPass is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Domain\User\Ports;
use Defuse\Crypto\Exception\CryptoException;
use SP\DataModel\UserLoginData;
use SP\Domain\Core\Exceptions\ConstraintException;
use SP\Domain\Core\Exceptions\QueryException;
use SP\Domain\Core\Exceptions\SPException;
use SP\Domain\User\Services\UserPassResponse;
use SP\Infrastructure\Common\Repositories\NoSuchItemException;
/**
* Class UserPassService
*
* @package SP\Domain\User\Services
*/
interface UserPassServiceInterface
{
/**
* Actualizar la clave maestra con la clave anterior del usuario
*
* @throws SPException
* @throws CryptoException
*/
public function updateMasterPassFromOldPass(string $oldUserPass, UserLoginData $userLoginData): UserPassResponse;
/**
* Comprueba la clave maestra del usuario.
*
* @throws SPException
*/
public function loadUserMPass(UserLoginData $userLoginData, ?string $userPass = null): UserPassResponse;
/**
* Obtener una clave de cifrado basada en la clave del usuario y un salt.
*
* @return string con la clave de cifrado
*/
public function makeKeyForUser(string $userLogin, string $userPass): string;
/**
* Actualizar la clave maestra del usuario al realizar login
*
* @throws SPException
* @throws CryptoException
* @throws SPException
*/
public function updateMasterPassOnLogin(string $userMPass, UserLoginData $userLoginData): UserPassResponse;
/**
* Actualizar la clave maestra del usuario en la BBDD.
*
* @throws CryptoException
* @throws SPException
*/
public function createMasterPass(string $masterPass, string $userLogin, string $userPass): UserPassResponse;
/**
* @throws ConstraintException
* @throws QueryException
* @throws NoSuchItemException
*/
public function migrateUserPassById(int $id, string $userPass): void;
}

View File

@@ -1,323 +0,0 @@
<?php
/*
* sysPass
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2024, 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\User\Services;
use SP\Domain\User\Models\UserPreferences;
/**
* Class UserLoginResponse
*
* @package SP\Domain\User\Services
*/
final class UserLoginResponse
{
private ?int $id = null;
private ?string $login = null;
private ?string $ssoLogin = null;
private ?string $name = null;
private ?string $email = null;
private int $userGroupId = 0;
private ?string $userGroupName = null;
private int $userProfileId = 0;
private bool $isAdminApp = false;
private bool $isAdminAcc = false;
private bool $isDisabled = false;
private bool $isChangePass = false;
private bool $isChangedPass = false;
private bool $isLdap = false;
private bool $isMigrate = false;
private ?UserPreferences $preferences = null;
private ?string $pass = null;
private ?string $hashSalt = null;
private ?string $mPass = null;
private ?string $mKey = null;
private int $lastUpdateMPass = 0;
private ?int $lastUpdate = null;
public function getLogin(): ?string
{
return $this->login;
}
public function setLogin(string $login): UserLoginResponse
{
$this->login = $login;
return $this;
}
public function getSsoLogin(): ?string
{
return $this->ssoLogin;
}
public function setSsoLogin(?string $ssoLogin): UserLoginResponse
{
$this->ssoLogin = $ssoLogin;
return $this;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): UserLoginResponse
{
$this->name = $name;
return $this;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(?string $email): UserLoginResponse
{
$this->email = $email;
return $this;
}
public function getUserGroupId(): int
{
return $this->userGroupId;
}
public function setUserGroupId(int $userGroupId): UserLoginResponse
{
$this->userGroupId = $userGroupId;
return $this;
}
public function getUserProfileId(): int
{
return $this->userProfileId;
}
public function setUserProfileId(int $userProfileId): UserLoginResponse
{
$this->userProfileId = $userProfileId;
return $this;
}
public function getIsAdminApp(): bool
{
return $this->isAdminApp;
}
public function setIsAdminApp(bool $isAdminApp): UserLoginResponse
{
$this->isAdminApp = $isAdminApp;
return $this;
}
public function getIsAdminAcc(): bool
{
return $this->isAdminAcc;
}
public function setIsAdminAcc(bool $isAdminAcc): UserLoginResponse
{
$this->isAdminAcc = $isAdminAcc;
return $this;
}
public function getIsDisabled(): bool
{
return $this->isDisabled;
}
public function setIsDisabled(bool $isDisabled): UserLoginResponse
{
$this->isDisabled = $isDisabled;
return $this;
}
public function getIsChangePass(): bool
{
return $this->isChangePass;
}
public function setIsChangePass(bool $isChangePass): UserLoginResponse
{
$this->isChangePass = $isChangePass;
return $this;
}
public function getIsChangedPass(): bool
{
return $this->isChangedPass;
}
public function setIsChangedPass(bool $isChangedPass): UserLoginResponse
{
$this->isChangedPass = $isChangedPass;
return $this;
}
public function getIsLdap(): bool
{
return $this->isLdap;
}
public function setIsLdap(bool $isLdap): UserLoginResponse
{
$this->isLdap = $isLdap;
return $this;
}
public function getIsMigrate(): bool
{
return $this->isMigrate;
}
public function setIsMigrate(bool $isMigrate): UserLoginResponse
{
$this->isMigrate = $isMigrate;
return $this;
}
public function getPreferences(): ?UserPreferences
{
return $this->preferences;
}
public function setPreferences(UserPreferences $preferences): UserLoginResponse
{
$this->preferences = $preferences;
return $this;
}
public function getPass(): ?string
{
return $this->pass;
}
public function setPass(string $pass): UserLoginResponse
{
$this->pass = $pass;
return $this;
}
public function getMPass(): ?string
{
return $this->mPass;
}
public function setMPass(string $mPass): UserLoginResponse
{
$this->mPass = $mPass;
return $this;
}
public function getMKey(): ?string
{
return $this->mKey;
}
public function setMKey(string $mKey): UserLoginResponse
{
$this->mKey = $mKey;
return $this;
}
public function getLastUpdateMPass(): int
{
return $this->lastUpdateMPass;
}
public function setLastUpdateMPass(int $lastUpdateMPass): UserLoginResponse
{
$this->lastUpdateMPass = $lastUpdateMPass;
return $this;
}
public function getHashSalt(): ?string
{
return $this->hashSalt;
}
public function setHashSalt(string $hashSalt): UserLoginResponse
{
$this->hashSalt = $hashSalt;
return $this;
}
public function getId(): ?int
{
return $this->id;
}
public function setId(int $id): UserLoginResponse
{
$this->id = $id;
return $this;
}
public function getUserGroupName(): ?string
{
return $this->userGroupName;
}
public function setUserGroupName(string $userGroupName): UserLoginResponse
{
$this->userGroupName = $userGroupName;
return $this;
}
public function getLastUpdate(): int
{
return $this->lastUpdate;
}
public function setLastUpdate(int $lastUpdate): UserLoginResponse
{
$this->lastUpdate = $lastUpdate;
return $this;
}
}

View File

@@ -0,0 +1,212 @@
<?php
/*
* sysPass
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2024, 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\User\Services;
use Exception;
use SP\Core\Application;
use SP\Core\Crypt\Hash;
use SP\Core\Events\Event;
use SP\Domain\Auth\Dtos\UserLoginDto;
use SP\Domain\Common\Services\Service;
use SP\Domain\Common\Services\ServiceException;
use SP\Domain\Config\Ports\ConfigService;
use SP\Domain\Core\Crypt\CryptInterface;
use SP\Domain\Core\Exceptions\CryptException;
use SP\Domain\User\Dtos\UserMasterPassDto;
use SP\Domain\User\Ports\UserMasterPassService;
use SP\Domain\User\Ports\UserRepository;
use function SP\__u;
/**
* Class UserMasterPass
*/
final class UserMasterPass extends Service implements UserMasterPassService
{
// public const SESSION_MASTERPASS_UPDATED = 'mpass_updated';
private const PARAM_MASTER_PWD = 'masterPwd';
private const PARAM_LASTUPDATEMPASS = 'lastupdatempass';
public function __construct(
Application $application,
private readonly UserRepository $userRepository,
private readonly ConfigService $configService,
private readonly CryptInterface $crypt
) {
parent::__construct($application);
}
/**
* Update the master pass by using the old user's password
*
* @throws ServiceException
*/
public function updateFromOldPass(string $oldUserPass, UserLoginDto $userLoginDto): UserMasterPassDto
{
$response = $this->load($userLoginDto, $oldUserPass);
if ($response->getUserMasterPassStatus() === UserMasterPassStatus::Ok) {
return $this->updateOnLogin($response->getClearMasterPass(), $userLoginDto);
}
return new UserMasterPassDto(UserMasterPassStatus::Invalid);
}
/**
* Load the user's master pass
*
* @throws ServiceException
*/
public function load(UserLoginDto $userLoginDto, ?string $userPass = null): UserMasterPassDto
{
try {
if (($userDataDto = $userLoginDto->getUserDataDto()) === null
|| empty($userDataDto->getMPass())
|| empty($userDataDto->getMKey())
|| empty($systemMasterPassHash = $this->configService->getByParam(self::PARAM_MASTER_PWD))
) {
return new UserMasterPassDto(UserMasterPassStatus::NotSet);
}
if ($userDataDto->getLastUpdateMPass() <
(int)$this->configService->getByParam(self::PARAM_LASTUPDATEMPASS, 0)
) {
return new UserMasterPassDto(UserMasterPassStatus::Changed);
}
if ($userPass === null && $userDataDto->getIsChangedPass()) {
return new UserMasterPassDto(UserMasterPassStatus::CheckOld);
}
$key = $this->makeKeyForUser($userLoginDto->getLoginUser(), $userPass ?? $userLoginDto->getLoginPass());
$userMasterPass = $this->crypt->decrypt($userDataDto->getMPass(), $userDataDto->getMKey(), $key);
// Comprobamos el hash de la clave del usuario con la guardada
if (Hash::checkHashKey($userMasterPass, $systemMasterPassHash)) {
$this->setMasterKeyInContext($userMasterPass);
return new UserMasterPassDto(
UserMasterPassStatus::Ok,
$userMasterPass,
$userDataDto->getMPass(),
$userDataDto->getMKey()
);
}
} catch (CryptException $e) {
$this->eventDispatcher->notify('exception', new Event($e));
return new UserMasterPassDto(UserMasterPassStatus::CheckOld);
} catch (Exception $e) {
throw ServiceException::from($e);
}
return new UserMasterPassDto(UserMasterPassStatus::Invalid);
}
/**
* Obtener una clave de cifrado basada en la clave del usuario y un salt.
*
* @return string con la clave de cifrado
*/
private function makeKeyForUser(string $userLogin, string $userPass): string
{
return trim($userPass . $userLogin . $this->config->getConfigData()->getPasswordSalt());
}
/**
* Update the user's master pass on log in.
* It requires the user's login data to build a secure key to store the master password
*
* @throws ServiceException
*/
public function updateOnLogin(string $userMasterPass, UserLoginDto $userLoginDto): UserMasterPassDto
{
try {
$userData = $userLoginDto->getUserDataDto();
$systemMasterPassHash = $this->configService->getByParam(self::PARAM_MASTER_PWD);
if (null === $systemMasterPassHash) {
$systemMasterPassHash = Hash::hashKey($userMasterPass);
$this->configService->save(self::PARAM_MASTER_PWD, $systemMasterPassHash);
}
if (Hash::checkHashKey($userMasterPass, $systemMasterPassHash)) {
$response = $this->create(
$userMasterPass,
$userLoginDto->getLoginUser(),
$userLoginDto->getLoginPass()
);
$this->userRepository->updateMasterPassById(
$userData->getId(),
$response->getCryptMasterPass(),
$response->getCryptSecuredKey()
);
// $this->context->setTrasientKey(self::SESSION_MASTERPASS_UPDATED, true);
$this->setMasterKeyInContext($userMasterPass);
return $response;
}
return new UserMasterPassDto(UserMasterPassStatus::Invalid);
} catch (Exception $e) {
throw ServiceException::from($e);
}
}
/**
* Actualizar la clave maestra del usuario en la BBDD.
*
* @throws ServiceException
*/
public function create(string $masterPass, string $userLogin, string $userPass): UserMasterPassDto
{
$key = $this->makeKeyForUser($userLogin, $userPass);
try {
$securedKey = $this->crypt->makeSecuredKey($key);
if (strlen($securedKey) > 1000) {
throw ServiceException::error(__u('Internal error'), null, Service::STATUS_INTERNAL_ERROR);
}
$encryptedMasterPass = $this->crypt->encrypt($masterPass, $securedKey, $key);
if (strlen($encryptedMasterPass) > 1000) {
throw ServiceException::error(__u('Internal error'), null, Service::STATUS_INTERNAL_ERROR);
}
return new UserMasterPassDto(UserMasterPassStatus::Ok, $masterPass, $encryptedMasterPass, $securedKey);
} catch (CryptException $e) {
throw ServiceException::from($e);
}
}
}

View File

@@ -0,0 +1,37 @@
<?php
/*
* sysPass
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2024, 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\User\Services;
/**
* Enum UserMasterPassStatus
*/
enum UserMasterPassStatus
{
case Invalid;
case Ok;
case NotSet;
case Changed;
case CheckOld;
}

View File

@@ -0,0 +1,71 @@
<?php
/*
* sysPass
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2024, 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\User\Services;
use SP\Core\Application;
use SP\Core\Crypt\Hash;
use SP\Domain\Common\Services\Service;
use SP\Domain\Core\Exceptions\ConstraintException;
use SP\Domain\Core\Exceptions\QueryException;
use SP\Domain\User\Models\User;
use SP\Domain\User\Ports\UserRepository;
use SP\Infrastructure\Common\Repositories\NoSuchItemException;
use function SP\__u;
/**
* Class UserPass
*/
final class UserPass extends Service implements UserPassService
{
public function __construct(
Application $application,
private readonly UserRepository $userRepository
) {
parent::__construct($application);
}
/**
* @throws ConstraintException
* @throws QueryException
* @throws NoSuchItemException
*/
public function migrateUserPassById(int $id, string $userPass): void
{
$user = new User(
[
'id' => $id,
'pass' => Hash::hashKey($userPass),
'isChangePass' => 0,
'isChangedPass' => 1,
'isMigrate' => 0
]
);
if ($this->userRepository->updatePassById($user) === 0) {
throw NoSuchItemException::info(__u('User does not exist'));
}
}
}

View File

@@ -24,228 +24,19 @@
namespace SP\Domain\User\Services;
use Defuse\Crypto\Exception\CryptoException;
use SP\Core\Application;
use SP\Core\Crypt\Crypt;
use SP\Core\Crypt\Hash;
use SP\DataModel\UserLoginData;
use SP\Domain\Common\Services\Service;
use SP\Domain\Config\Ports\ConfigService;
use SP\Domain\Core\Exceptions\ConstraintException;
use SP\Domain\Core\Exceptions\QueryException;
use SP\Domain\Core\Exceptions\SPException;
use SP\Domain\User\Ports\UserPassServiceInterface;
use SP\Domain\User\Ports\UserRepository;
use SP\Infrastructure\Common\Repositories\NoSuchItemException;
/**
* Class UserPassService
*
* @package SP\Domain\User\Services
* Class UserPass
*/
final class UserPassService extends Service implements UserPassServiceInterface
interface UserPassService
{
// La clave maestra incorrecta
public const MPASS_WRONG = 0;
// La clave maestra correcta
public const MPASS_OK = 1;
// La clave maestra no está guardada
public const MPASS_NOTSET = 2;
// La clave maestra ha cambiado
public const MPASS_CHANGED = 3;
// Comprobar la clave maestra con la clave del usuario anterior
public const MPASS_CHECKOLD = 4;
private UserRepository $userRepository;
private ConfigService $configService;
public function __construct(
Application $application,
UserRepository $userRepository,
ConfigService $configService
) {
parent::__construct($application);
$this->userRepository = $userRepository;
$this->configService = $configService;
}
/**
* Actualizar la clave maestra con la clave anterior del usuario
*
* @throws SPException
* @throws CryptoException
*/
public function updateMasterPassFromOldPass(
string $oldUserPass,
UserLoginData $userLoginData
): UserPassResponse {
$response = $this->loadUserMPass($userLoginData, $oldUserPass);
if ($response->getStatus() === self::MPASS_OK) {
return $this->updateMasterPassOnLogin($response->getClearMasterPass(), $userLoginData);
}
return new UserPassResponse(self::MPASS_WRONG);
}
/**
* Comprueba la clave maestra del usuario.
*
* @throws SPException
*/
public function loadUserMPass(
UserLoginData $userLoginData,
?string $userPass = null
): UserPassResponse {
$userLoginResponse = $userLoginData->getUserLoginResponse();
$configHashMPass = $this->configService->getByParam('masterPwd');
if (empty($configHashMPass)
|| $userLoginResponse === null
|| empty($userLoginResponse->getMPass())
|| empty($userLoginResponse->getMKey())
) {
return new UserPassResponse(self::MPASS_NOTSET);
}
if ($userLoginResponse->getLastUpdateMPass() < $this->configService->getByParam('lastupdatempass')) {
return new UserPassResponse(self::MPASS_CHANGED);
}
if ($userPass === null && $userLoginResponse->getIsChangedPass()) {
return new UserPassResponse(self::MPASS_CHECKOLD);
}
try {
$key = $this->makeKeyForUser(
$userLoginData->getLoginUser(),
$userPass ?: $userLoginData->getLoginPass()
);
$clearMPass = Crypt::decrypt(
$userLoginResponse->getMPass(),
$userLoginResponse->getMKey(),
$key
);
// Comprobamos el hash de la clave del usuario con la guardada
if (Hash::checkHashKey($clearMPass, $configHashMPass)) {
$this->setMasterKeyInContext($clearMPass);
$response = new UserPassResponse(self::MPASS_OK, $clearMPass);
$response->setCryptMasterPass($userLoginResponse->getMPass());
$response->setCryptSecuredKey($userLoginResponse->getMKey());
return $response;
}
} catch (CryptoException $e) {
return new UserPassResponse(self::MPASS_CHECKOLD);
}
return new UserPassResponse(self::MPASS_WRONG);
}
/**
* Obtener una clave de cifrado basada en la clave del usuario y un salt.
*
* @return string con la clave de cifrado
*/
public function makeKeyForUser(string $userLogin, string $userPass): string
{
return trim($userPass . $userLogin . $this->config->getConfigData()->getPasswordSalt());
}
/**
* Actualizar la clave maestra del usuario al realizar login
*
* @throws SPException
* @throws CryptoException
* @throws SPException
*/
public function updateMasterPassOnLogin(string $userMPass, UserLoginData $userLoginData): UserPassResponse
{
$userData = $userLoginData->getUserLoginResponse();
$configHashMPass = $this->configService->getByParam('masterPwd');
if ($configHashMPass === false) {
return new UserPassResponse(self::MPASS_NOTSET);
}
if (null === $configHashMPass) {
$configHashMPass = Hash::hashKey($userMPass);
$this->configService->save('masterPwd', $configHashMPass);
}
if (Hash::checkHashKey($userMPass, $configHashMPass)) {
$response = $this->createMasterPass(
$userMPass,
$userLoginData->getLoginUser(),
$userLoginData->getLoginPass()
);
$this->userRepository->updateMasterPassById(
$userData->getId(),
$response->getCryptMasterPass(),
$response->getCryptSecuredKey()
);
// Tells that the master password has been updated
$this->context->setTrasientKey('mpass_updated', true);
$this->setMasterKeyInContext($userMPass);
return $response;
}
return new UserPassResponse(self::MPASS_WRONG);
}
/**
* Actualizar la clave maestra del usuario en la BBDD.
*
* @throws CryptoException
* @throws SPException
*/
public function createMasterPass(string $masterPass, string $userLogin, string $userPass): UserPassResponse
{
$key = $this->makeKeyForUser($userLogin, $userPass);
$securedKey = Crypt::makeSecuredKey($key);
$cryptMPass = Crypt::encrypt($masterPass, $securedKey, $key);
if (strlen($securedKey) > 1000 || strlen($cryptMPass) > 1000) {
throw new SPException(
__u('Internal error'),
SPException::ERROR,
'',
Service::STATUS_INTERNAL_ERROR
);
}
$response = new UserPassResponse(self::MPASS_OK, $masterPass);
$response->setCryptMasterPass($cryptMPass);
$response->setCryptSecuredKey($securedKey);
return $response;
}
/**
* @throws ConstraintException
* @throws QueryException
* @throws NoSuchItemException
*/
public function migrateUserPassById(int $id, string $userPass): void
{
$updatePassById = $this->userRepository->updatePassById(
$id,
new UpdatePassRequest(Hash::hashKey($userPass))
);
if ($updatePassById === 0) {
throw new NoSuchItemException(__u('User does not exist'));
}
}
public function migrateUserPassById(int $id, string $userPass): void;
}

View File

@@ -36,7 +36,7 @@ use SP\Domain\Core\Exceptions\QueryException;
use SP\Domain\Core\Exceptions\SPException;
use SP\Domain\User\Models\User as UserModel;
use SP\Domain\User\Models\UserPreferences;
use SP\Domain\User\Ports\UserPassServiceInterface;
use SP\Domain\User\Ports\UserMasterPassService;
use SP\Domain\User\Ports\UserRepository;
use SP\Domain\User\Ports\UserServiceInterface;
use SP\Infrastructure\Common\Repositories\DuplicatedItemException;
@@ -53,13 +53,13 @@ final class UserService extends Service implements UserServiceInterface
{
use ServiceItemTrait;
private UserRepository $userRepository;
private UserPassServiceInterface $userPassService;
private UserRepository $userRepository;
private UserMasterPassService $userPassService;
public function __construct(
Application $application,
UserRepository $userRepository,
UserPassServiceInterface $userPassService
Application $application,
UserRepository $userRepository,
UserMasterPassService $userPassService
) {
parent::__construct($application);
@@ -67,33 +67,6 @@ final class UserService extends Service implements UserServiceInterface
$this->userPassService = $userPassService;
}
public static function mapUserLoginResponse(
UserModel $user
): UserLoginResponse {
// TODO: create static method UserLoginResponse::from($user)
return (new UserLoginResponse())->setId($user->getId())
->setName($user->getName())
->setLogin($user->getLogin())
->setSsoLogin($user->getSsoLogin())
->setEmail($user->getEmail())
->setPass($user->getPass())
->setHashSalt($user->getHashSalt())
->setMPass($user->getMPass())
->setMKey($user->getMKey())
->setLastUpdateMPass($user->getLastUpdateMPass())
->setUserGroupId($user->getUserGroupId())
->setUserProfileId($user->getUserProfileId())
->setPreferences(self::getUserPreferences($user->getPreferences()))
->setIsLdap($user->isLdap())
->setIsAdminAcc($user->isAdminAcc())
->setIsAdminApp($user->isAdminApp())
->setIsMigrate($user->isMigrate())
->setIsChangedPass($user->isChangedPass())
->setIsChangePass($user->isChangePass())
->setIsDisabled($user->isDisabled())
->setLastUpdate((int)strtotime($user->getLastUpdate()));
}
/**
* Returns user's preferences object
*/
@@ -251,7 +224,7 @@ final class UserService extends Service implements UserServiceInterface
*/
public function createWithMasterPass(UserModel $itemData, string $userPass, string $masterPass): int
{
$response = $this->userPassService->createMasterPass(
$response = $this->userPassService->create(
$masterPass,
$itemData->getLogin(),
$userPass

View File

@@ -130,10 +130,9 @@ final class User extends BaseRepository implements UserRepository
$query = $this->queryFactory
->newUpdate()
->table(UserModel::TABLE)
->cols($user->toArray(['pass', 'isChangePass', 'isChangedPass']))
->cols($user->toArray(['pass', 'isChangePass', 'isChangedPass', 'isMigrate']))
->set('lastUpdate', 'NOW()')
->set('hashSalt', '')
->set('isMigrate', 0)
->where('id = :id', ['id' => $user->getId()])
->limit(1);

View File

@@ -4,7 +4,7 @@
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org
* @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
@@ -24,7 +24,7 @@
namespace SP\Providers\Auth;
use SP\DataModel\UserLoginData;
use SP\Domain\Auth\Dtos\UserLoginDto;
/**
* Interface AuthInterface
@@ -37,10 +37,10 @@ interface AuthInterface
/**
* Authenticate using user's data
*
* @param UserLoginData $userLoginData
* @param UserLoginDto $userLoginData
* @return T
*/
public function authenticate(UserLoginData $userLoginData): AuthDataBase;
public function authenticate(UserLoginDto $userLoginData): AuthDataBase;
/**
* Indica si es requerida para acceder a la aplicación

View File

@@ -4,7 +4,7 @@
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org
* @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
@@ -25,7 +25,7 @@
namespace SP\Providers\Auth;
use SP\Core\Application;
use SP\DataModel\UserLoginData;
use SP\Domain\Auth\Dtos\UserLoginDto;
use SP\Domain\Auth\Services\AuthException;
use SP\Domain\Core\Exceptions\SPException;
use SP\Providers\Provider;
@@ -87,11 +87,11 @@ class AuthProvider extends Provider implements AuthProviderInterface
/**
* Probar los métodos de autentificación
*
* @param UserLoginData $userLoginData
* @param UserLoginDto $userLoginData
*
* @return false|AuthResult[]
*/
public function doAuth(UserLoginData $userLoginData): array|bool
public function doAuth(UserLoginDto $userLoginData): array|bool
{
$authsResult = [];

View File

@@ -4,7 +4,7 @@
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org
* @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
@@ -24,8 +24,7 @@
namespace SP\Providers\Auth;
use SP\DataModel\UserLoginData;
use SP\Domain\Auth\Services\AuthException;
use SP\Domain\Auth\Dtos\UserLoginDto;
/**
* Class Auth
@@ -39,9 +38,9 @@ interface AuthProviderInterface
/**
* Probar los métodos de autentificación
*
* @param UserLoginData $userLoginData
* @param UserLoginDto $userLoginData
*
* @return false|AuthResult[]
*/
public function doAuth(UserLoginData $userLoginData): array|bool;
public function doAuth(UserLoginDto $userLoginData): array|bool;
}

View File

@@ -4,7 +4,7 @@
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org
* @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
@@ -24,7 +24,7 @@
namespace SP\Providers\Auth\Browser;
use SP\DataModel\UserLoginData;
use SP\Domain\Auth\Dtos\UserLoginDto;
use SP\Domain\Config\Ports\ConfigDataInterface;
use SP\Domain\Http\RequestInterface;
use SP\Providers\Auth\AuthInterface;
@@ -50,10 +50,10 @@ final class BrowserAuth implements BrowserAuthInterface
/**
* Authenticate using user's data
*
* @param UserLoginData $userLoginData
* @param UserLoginDto $userLoginData
* @return BrowserAuthData
*/
public function authenticate(UserLoginData $userLoginData): BrowserAuthData
public function authenticate(UserLoginDto $userLoginData): BrowserAuthData
{
$browserAuthData = new BrowserAuthData($this->isAuthGranted());

View File

@@ -4,7 +4,7 @@
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org
* @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
@@ -26,36 +26,31 @@ namespace SP\Providers\Auth\Database;
use Exception;
use SP\Core\Crypt\Hash;
use SP\DataModel\UserLoginData;
use SP\Domain\User\Ports\UserPassServiceInterface;
use SP\Domain\Auth\Dtos\UserLoginDto;
use SP\Domain\User\Dtos\UserDataDto;
use SP\Domain\User\Ports\UserServiceInterface;
use SP\Domain\User\Services\UserLoginResponse;
use SP\Domain\User\Services\UserService;
use SP\Domain\User\Services\UserPassService;
use function SP\processException;
/**
* Class Database
*
* Autentificación basada en base de datos
*
* @package SP\Providers\Auth\Database
* Class DatabaseAuth
*/
final class DatabaseAuth implements DatabaseAuthInterface
final readonly class DatabaseAuth implements DatabaseAuthService
{
public function __construct(
private readonly UserServiceInterface $userService,
private readonly UserPassServiceInterface $userPassService
private UserServiceInterface $userService,
private UserPassService $userPassService
) {
}
/**
* Authenticate using user's data
*
* @param UserLoginData $userLoginData
* @param UserLoginDto $userLoginData
* @return DatabaseAuthData
*/
public function authenticate(UserLoginData $userLoginData): DatabaseAuthData
public function authenticate(UserLoginDto $userLoginData): DatabaseAuthData
{
$authData = new DatabaseAuthData($this->isAuthGranted());
@@ -72,17 +67,14 @@ final class DatabaseAuth implements DatabaseAuthInterface
return true;
}
protected function authUser(UserLoginData $userLoginData): bool
private function authUser(UserLoginDto $userLoginData): bool
{
try {
$userLoginResponse =
UserService::mapUserLoginResponse($this->userService->getByLogin($userLoginData->getLoginUser()));
$userLoginResponse = new UserDataDto($this->userService->getByLogin($userLoginData->getLoginUser()));
$userLoginData->setUserLoginResponse($userLoginResponse);
$userLoginData->setUserDataDto($userLoginResponse);
if ($userLoginResponse->getIsMigrate()
&& $this->checkMigrateUser($userLoginResponse, $userLoginData)
) {
if ($userLoginResponse->getIsMigrate() && $this->checkMigrateUser($userLoginResponse, $userLoginData)) {
$this->userPassService->migrateUserPassById(
$userLoginResponse->getId(),
$userLoginData->getLoginPass()
@@ -99,7 +91,7 @@ final class DatabaseAuth implements DatabaseAuthInterface
return false;
}
protected function checkMigrateUser(UserLoginResponse $userLoginResponse, UserLoginData $userLoginData): bool
private function checkMigrateUser(UserDataDto $userLoginResponse, UserLoginDto $userLoginData): bool
{
$passHashSha = sha1($userLoginResponse->getHashSalt() . $userLoginData->getLoginPass());

View File

@@ -4,7 +4,7 @@
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org
* @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
@@ -24,7 +24,6 @@
namespace SP\Providers\Auth\Database;
use SP\Providers\Auth\AuthInterface;
/**
@@ -34,7 +33,7 @@ use SP\Providers\Auth\AuthInterface;
*
* @extends AuthInterface<DatabaseAuthData>
*/
interface DatabaseAuthInterface extends AuthInterface
interface DatabaseAuthService extends AuthInterface
{
/**
* Indica si es requerida para acceder a la aplicación

View File

@@ -27,7 +27,7 @@ namespace SP\Providers\Auth\Ldap;
use SP\Core\Events\Event;
use SP\Core\Events\EventDispatcher;
use SP\Core\Events\EventMessage;
use SP\DataModel\UserLoginData;
use SP\Domain\Auth\Dtos\UserLoginDto;
use SP\Domain\Auth\Ports\LdapAuthService;
use SP\Domain\Auth\Ports\LdapService;
use SP\Domain\Config\Ports\ConfigDataInterface;
@@ -60,10 +60,10 @@ final class LdapAuth implements LdapAuthService
/**
* Authenticate using user's data
*
* @param UserLoginData $userLoginData
* @param UserLoginDto $userLoginData
* @return LdapAuthData
*/
public function authenticate(UserLoginData $userLoginData): LdapAuthData
public function authenticate(UserLoginDto $userLoginData): LdapAuthData
{
$ldapAuthData = new LdapAuthData($this->isAuthGranted());

View File

@@ -29,8 +29,9 @@ use PHPUnit\Framework\MockObject\MockObject;
use SP\Core\Language;
use SP\Domain\Config\Ports\ConfigDataInterface;
use SP\Domain\Http\RequestInterface;
use SP\Domain\User\Dtos\UserDataDto;
use SP\Domain\User\Models\User;
use SP\Domain\User\Models\UserPreferences;
use SP\Domain\User\Services\UserLoginResponse;
use SPT\UnitaryTestCase;
/**
@@ -78,9 +79,10 @@ class LanguageTest extends UnitaryTestCase
->method('getSiteLang')
->willReturn(self::$faker->locale);
$userData = $this->context->getUserData();
$user = (new User(['id' => self::$faker->randomNumber(2)]))
->dehydrate(new UserPreferences(['lang' => $locale]));
$userData->setPreferences(new UserPreferences(['lang' => $locale]));
$this->context->setUserData(new UserDataDto($user));
$this->language->setLanguage(true);
@@ -96,7 +98,8 @@ class LanguageTest extends UnitaryTestCase
$appLocale = 'en_US';
$this->context->setLocale($locale);
$this->context->setUserData(new UserLoginResponse());
$this->context->setUserData(new UserDataDto(new User()));
$this->configData
->expects(self::once())
@@ -117,7 +120,8 @@ class LanguageTest extends UnitaryTestCase
$browserLocale = 'en_US';
$this->context->setLocale($locale);
$this->context->setUserData(new UserLoginResponse());
$this->context->setUserData(new UserDataDto(new User()));
$this->configData
->expects(self::once())

View File

@@ -4,7 +4,7 @@
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org
* @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
@@ -33,7 +33,7 @@ use SP\Core\UI\Theme;
use SP\Domain\Core\Context\SessionContextInterface;
use SP\Domain\Core\UI\ThemeContextInterface;
use SP\Domain\Core\UI\ThemeIconsInterface;
use SP\Domain\User\Services\UserLoginResponse;
use SP\Domain\User\Dtos\UserDataDto;
use SPT\Generators\UserDataGenerator;
use SPT\UnitaryTestCase;
@@ -88,21 +88,18 @@ class ThemeTest extends UnitaryTestCase
->method('isLoggedIn')
->willReturn(true);
$userLoginResponse = new UserLoginResponse();
$userPreferencesData = UserDataGenerator::factory()->buildUserPreferencesData();
$userLoginResponse->setPreferences($userPreferencesData);
$userDataDto = new UserDataDto(UserDataGenerator::factory()->buildUserData());
$context->expects(self::once())
->method('getUserData')
->willReturn($userLoginResponse);
->willReturn($userDataDto);
$configData = $this->config->getConfigData();
$configData->setSiteTheme(self::$faker->colorName);
$current = Theme::getThemeName($this->config->getConfigData(), $context);
$this->assertEquals($userPreferencesData->getTheme(), $current);
$this->assertEquals($userDataDto->getPreferences()->getTheme(), $current);
}
public function testGetViewsPath()

View File

@@ -27,6 +27,7 @@ namespace SPT\Domain\Account\Services;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\MockObject\Exception;
use PHPUnit\Framework\MockObject\MockObject;
use SP\Core\Acl\Acl;
use SP\DataModel\Item;
use SP\Domain\Account\Adapters\AccountPermission;
@@ -38,8 +39,11 @@ use SP\Domain\Core\Acl\ActionsInterface;
use SP\Domain\Core\Exceptions\ConstraintException;
use SP\Domain\Core\Exceptions\QueryException;
use SP\Domain\Storage\Ports\FileCacheService;
use SP\Domain\User\Dtos\UserDataDto;
use SP\Domain\User\Models\User;
use SP\Domain\User\Ports\UserToUserGroupServiceInterface;
use SP\Infrastructure\File\FileException;
use SPT\Generators\UserDataGenerator;
use SPT\UnitaryTestCase;
/**
@@ -62,8 +66,9 @@ class AccountAclTest extends UnitaryTestCase
AclActionsInterface::ACCOUNT_COPY_PASS,
AclActionsInterface::ACCOUNT_DELETE,
];
private static array $accounts;
private AccountAcl $accountAcl;
private static array $accounts;
private Acl $acl;
private UserToUserGroupServiceInterface|MockObject $userToUserGroupService;
public static function setUpBeforeClass(): void
{
@@ -192,8 +197,7 @@ class AccountAclTest extends UnitaryTestCase
self::$faker->numberBetween(1, 4),
self::$faker->randomNumber(),
self::$faker->randomNumber(),
1,
0
true
),
$this->getExampleAclForAdmin()
);
@@ -210,10 +214,16 @@ class AccountAclTest extends UnitaryTestCase
AccountAclDto $accountAclDto,
AccountPermission $example
): void {
$accountAcl = new AccountAcl(
$this->application,
$this->acl,
$this->userToUserGroupService
);
foreach (self::ACTIONS as $action) {
$example->setActionId($action);
$aclUnderTest = $this->accountAcl->getAcl($action, $accountAclDto);
$aclUnderTest = $accountAcl->getAcl($action, $accountAclDto);
$this->assertTrue($aclUnderTest->isCompiledAccountAccess());
$this->assertTrue($aclUnderTest->isCompiledShowAccess());
@@ -321,12 +331,18 @@ class AccountAclTest extends UnitaryTestCase
bool $isAdminApp = false,
bool $isAdminAcc = false
): AccountAclDto {
$this->context
->getUserData()
->setId($userId)
->setUserGroupId($groupId)
->setIsAdminApp($isAdminApp)
->setIsAdminAcc($isAdminAcc);
$this->context->setUserData(
new UserDataDto(
new User(
[
'id' => $userId,
'userGroupId' => $groupId,
'isAdminApp' => $isAdminApp,
'isAdminAcc' => $isAdminAcc
]
)
)
);
return new AccountAclDto(
$accountId,
@@ -481,7 +497,6 @@ class AccountAclTest extends UnitaryTestCase
*/
#[Group('acl:admin')]
#[DataProvider('accountPropertiesProvider')]
public function testEditPass(
int $accountId,
int $userId,
@@ -674,6 +689,12 @@ class AccountAclTest extends UnitaryTestCase
$fileCache = $this->createMock(FileCacheService::class);
$actions = $this->createMock(ActionsInterface::class);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()->buildUserData()->mutate(['lastUpdate' => $dto->getDateEdit() + 10])
)
);
$accountAclService = new AccountAcl(
$this->application,
new Acl($this->context, $this->application->getEventDispatcher(), $actions),
@@ -681,8 +702,6 @@ class AccountAclTest extends UnitaryTestCase
$fileCache
);
$this->context->getUserData()->setLastUpdate($dto->getDateEdit() + 10);
$acl = new AccountPermission(self::$faker->randomNumber());
$acl->setTime($dto->getDateEdit() + 10);
@@ -824,20 +843,14 @@ class AccountAclTest extends UnitaryTestCase
$actions = $this->createMock(ActionsInterface::class);
$acl = new Acl($this->context, $this->application->getEventDispatcher(), $actions);
$userToUserGroupService = $this->createMock(UserToUserGroupServiceInterface::class);
$userToUserGroupService->method('getGroupsForUser')
->willReturnMap([
[1, [new Simple(['userGroupId' => 2])]],
[2, [new Simple(['userGroupId' => 1])]],
[3, [new Simple(['userGroupId' => 2])]],
[4, []],
]);
$this->accountAcl = new AccountAcl(
$this->application,
$acl,
$userToUserGroupService
);
$this->acl = new Acl($this->context, $this->application->getEventDispatcher(), $actions);
$this->userToUserGroupService = $this->createMock(UserToUserGroupServiceInterface::class);
$this->userToUserGroupService->method('getGroupsForUser')
->willReturnMap([
[1, [new Simple(['userGroupId' => 2])]],
[2, [new Simple(['userGroupId' => 1])]],
[3, [new Simple(['userGroupId' => 2])]],
[4, []],
]);
}
}

View File

@@ -4,7 +4,7 @@
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org
* @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
@@ -31,6 +31,8 @@ use PHPUnit\Framework\Constraint\Callback;
use PHPUnit\Framework\MockObject\Exception;
use PHPUnit\Framework\MockObject\MockObject;
use SP\Domain\Account\Services\Builders\AccountFilter;
use SP\Domain\User\Dtos\UserDataDto;
use SPT\Generators\UserDataGenerator;
use SPT\UnitaryTestCase;
/**
@@ -149,7 +151,11 @@ class AccountFilterUserTest extends UnitaryTestCase
public function testBuildFilterWithGlobalSearchForAdminAcc()
{
$this->context->getUserData()->setIsAdminAcc(true);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()->buildUserData()->mutate(['isAdminAcc' => true])
)
);
$this->setExpectationForGlobalSearch('Account');
@@ -183,7 +189,11 @@ class AccountFilterUserTest extends UnitaryTestCase
public function testBuildFilterWithGlobalSearchForAdminApp()
{
$this->context->getUserData()->setIsAdminApp(true);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()->buildUserData()->mutate(['isAdminApp' => true])
)
);
$this->setExpectationForGlobalSearch('Account');
@@ -209,7 +219,11 @@ class AccountFilterUserTest extends UnitaryTestCase
public function testBuildFilterHistoryWithGlobalSearchForAdminAcc()
{
$this->context->getUserData()->setIsAdminAcc(true);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()->buildUserData()->mutate(['isAdminAcc' => true])
)
);
$this->setExpectationForGlobalSearch('AccountHistory');
@@ -218,7 +232,11 @@ class AccountFilterUserTest extends UnitaryTestCase
public function testBuildFilterHistoryWithGlobalSearchForAdminApp()
{
$this->context->getUserData()->setIsAdminApp(true);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()->buildUserData()->mutate(['isAdminApp' => true])
)
);
$this->setExpectationForGlobalSearch('AccountHistory');

View File

@@ -28,6 +28,7 @@ use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Constraint\Callback;
use PHPUnit\Framework\MockObject\MockObject;
use SP\DataModel\ItemPreset\AccountPrivate;
use SP\DataModel\ProfileData;
use SP\Domain\Account\Dtos\AccountHistoryCreateDto;
use SP\Domain\Account\Dtos\AccountUpdateBulkDto;
use SP\Domain\Account\Dtos\AccountUpdateDto;
@@ -51,10 +52,12 @@ use SP\Domain\Core\Exceptions\SPException;
use SP\Domain\ItemPreset\Models\ItemPreset;
use SP\Domain\ItemPreset\Ports\ItemPresetInterface;
use SP\Domain\ItemPreset\Ports\ItemPresetService;
use SP\Domain\User\Dtos\UserDataDto;
use SP\Infrastructure\Common\Repositories\NoSuchItemException;
use SP\Infrastructure\Database\QueryResult;
use SPT\Generators\AccountDataGenerator;
use SPT\Generators\ItemSearchDataGenerator;
use SPT\Generators\UserDataGenerator;
use SPT\Stubs\AccountRepositoryStub;
use SPT\UnitaryTestCase;
@@ -86,7 +89,11 @@ class AccountTest extends UnitaryTestCase
$accountDataGenerator = AccountDataGenerator::factory();
$accountUpdateDto = $accountDataGenerator->buildAccountUpdateDto();
$this->context->getUserData()->setIsAdminApp(true);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()->buildUserData()->mutate(['isAdminApp' => true])
)
);
$this->configService->expects(self::once())->method('getByParam')
->with('masterPwd')->willReturn(self::$faker->password);
@@ -115,7 +122,11 @@ class AccountTest extends UnitaryTestCase
$accountDataGenerator = AccountDataGenerator::factory();
$accountUpdateDto = $accountDataGenerator->buildAccountUpdateDto();
$this->context->getUserData()->setIsAdminApp(false);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()->buildUserData()->mutate(['isAdminApp' => false])
)
);
$this->configService->expects(self::once())->method('getByParam')
->with('masterPwd')->willReturn(self::$faker->password);
@@ -144,8 +155,14 @@ class AccountTest extends UnitaryTestCase
$accountDataGenerator = AccountDataGenerator::factory();
$accountUpdateDto = $accountDataGenerator->buildAccountUpdateDto();
$this->context->getUserData()->setIsAdminApp(false);
$this->context->getUserData()->setIsAdminAcc(true);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(['isAdminApp' => false, 'isAdminAcc' => true])
)
);
$this->context->getUserProfile()->setAccPermission(false);
$this->configService->expects(self::once())->method('getByParam')
@@ -175,8 +192,14 @@ class AccountTest extends UnitaryTestCase
$accountDataGenerator = AccountDataGenerator::factory();
$accountUpdateDto = $accountDataGenerator->buildAccountUpdateDto();
$this->context->getUserData()->setIsAdminApp(false);
$this->context->getUserData()->setIsAdminAcc(false);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(['isAdminApp' => false, 'isAdminAcc' => false])
)
);
$this->context->getUserProfile()->setAccPermission(true);
$this->configService->expects(self::once())->method('getByParam')
@@ -216,10 +239,20 @@ class AccountTest extends UnitaryTestCase
'data' => serialize(new AccountPrivate(true, true)),
]);
$userData = $this->context->getUserData();
$userData->setIsAdminApp(true);
$userData->setId($accountUpdateDto->getUserId());
$userData->setUserGroupId($accountUpdateDto->getUserGroupId());
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(
[
'id' => $accountUpdateDto->getUserId(),
'userGroupId' => $accountUpdateDto->getUserGroupId(),
'isAdminApp' => true,
'isAdminAcc' => false
]
)
)
);
$this->configService->expects(self::once())->method('getByParam')
->with('masterPwd')->willReturn(self::$faker->password);
@@ -283,10 +316,20 @@ class AccountTest extends UnitaryTestCase
'data' => serialize(new AccountPrivate(true, true)),
]);
$userData = $this->context->getUserData();
$userData->setIsAdminApp(true);
$userData->setId($accountUpdateDto->getUserId());
$userData->setUserGroupId($accountUpdateDto->getUserGroupId());
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(
[
'id' => $accountUpdateDto->getUserId(),
'userGroupId' => $accountUpdateDto->getUserGroupId(),
'isAdminApp' => true,
'isAdminAcc' => false
]
)
)
);
$this->configService->expects(self::once())->method('getByParam')
->with('masterPwd')->willReturn(self::$faker->password);
@@ -513,7 +556,18 @@ class AccountTest extends UnitaryTestCase
$accountsId = range(0, 4);
$accountUpdateBulkDto = new AccountUpdateBulkDto($accountsId, $accounts);
$this->context->getUserData()->setIsAdminApp(true);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(
[
'isAdminApp' => true,
'isAdminAcc' => false
]
)
)
);
$consecutive = array_merge($accountsId, $accountsId);
sort($consecutive);
@@ -545,7 +599,18 @@ class AccountTest extends UnitaryTestCase
$accountsId = range(0, 4);
$accountUpdateBulkDto = new AccountUpdateBulkDto($accountsId, $accounts);
$this->context->getUserData()->setIsAdminApp(false);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(
[
'isAdminApp' => false,
'isAdminAcc' => false
]
)
)
);
$this->accountRepository->expects(self::exactly(count($accountsId)))->method('getById')
->with(...self::withConsecutive(...array_map(fn($v) => [$v], $accountsId)))
@@ -574,9 +639,20 @@ class AccountTest extends UnitaryTestCase
$accountsId = range(0, 4);
$accountUpdateBulkDto = new AccountUpdateBulkDto($accountsId, $accounts);
$this->context->getUserData()->setIsAdminApp(false);
$this->context->getUserData()->setIsAdminAcc(true);
$this->context->getUserProfile()->setAccPermission(false);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(
[
'isAdminApp' => true,
'isAdminAcc' => false
]
)
)
);
$this->context->setUserProfile(new ProfileData(['accPermission' => false]));
$consecutive = array_merge($accountsId, $accountsId);
sort($consecutive);
@@ -608,9 +684,20 @@ class AccountTest extends UnitaryTestCase
$accountsId = range(0, 4);
$accountUpdateBulkDto = new AccountUpdateBulkDto($accountsId, $accounts);
$this->context->getUserData()->setIsAdminApp(false);
$this->context->getUserData()->setIsAdminAcc(false);
$this->context->getUserProfile()->setAccPermission(true);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(
[
'isAdminApp' => false,
'isAdminAcc' => false
]
)
)
);
$this->context->setUserProfile(new ProfileData(['accPermission' => true]));
$consecutive = array_merge($accountsId, $accountsId);
sort($consecutive);
@@ -623,8 +710,10 @@ class AccountTest extends UnitaryTestCase
$this->accountItemsService->expects(self::exactly(count($accountsId)))->method('updateItems')
->with(
...self::withConsecutive(
...array_map(fn($v) => [$v, true, $accounts[$v]],
$accountsId)
...array_map(
fn($v) => [$v, true, $accounts[$v]],
$accountsId
)
)
);
@@ -669,8 +758,6 @@ class AccountTest extends UnitaryTestCase
$this->accountHistoryService->expects(self::once())->method('create')
->with($accountHistoryCreateDto);
$queryResult = new QueryResult();
$this->accountRepository->expects(self::once())->method('delete')
->with($id)
->willReturn(new QueryResult(null, 1));
@@ -980,7 +1067,18 @@ class AccountTest extends UnitaryTestCase
$accountDataGenerator = AccountDataGenerator::factory();
$accountCreateDto = $accountDataGenerator->buildAccountCreateDto();
$this->context->getUserData()->setIsAdminApp(true);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(
[
'isAdminApp' => true,
'isAdminAcc' => false
]
)
)
);
$encryptedPassword = new EncryptedPassword(self::$faker->password, self::$faker->password);
@@ -1012,10 +1110,24 @@ class AccountTest extends UnitaryTestCase
$id = self::$faker->randomNumber();
$accountDataGenerator = AccountDataGenerator::factory();
$userData = $this->context->getUserData();
$accountCreateDto = $accountDataGenerator->buildAccountCreateDto()->withUserId($userData->getId())
$accountCreateDto = $accountDataGenerator->buildAccountCreateDto()
->withUserId($userData->getId())
->withUserGroupId($userData->getUserGroupId());
$userData->setIsAdminApp(false);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(
[
'id' => $userData->getId(),
'userGroupId' => $userData->getUserGroupId(),
'isAdminApp' => false,
'isAdminAcc' => false
]
)
)
);
$encryptedPassword = new EncryptedPassword(self::$faker->password, self::$faker->password);
@@ -1058,9 +1170,21 @@ class AccountTest extends UnitaryTestCase
'data' => serialize(new AccountPrivate(true, true)),
]);
$userData = $this->context->getUserData();
$userData->setIsAdminApp(true);
$userData->setId($accountCreateDto->getUserId());
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(
[
'id' => $accountCreateDto->getUserId(),
'isAdminApp' => true,
'isAdminAcc' => false
]
)
)
);
$encryptedPassword = new EncryptedPassword(self::$faker->password, self::$faker->password);
@@ -1113,9 +1237,19 @@ class AccountTest extends UnitaryTestCase
'data' => serialize(new AccountPrivate(true, true)),
]);
$userData = $this->context->getUserData();
$userData->setIsAdminApp(true);
$userData->setUserGroupId($accountCreateDto->getUserGroupId());
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(
[
'userGroupId' => $accountCreateDto->getUserGroupId(),
'isAdminApp' => true,
'isAdminAcc' => false
]
)
)
);
$encryptedPassword = new EncryptedPassword(self::$faker->password, self::$faker->password);

View File

@@ -30,6 +30,7 @@ use Defuse\Crypto\KeyProtectedByPassword;
use DOMDocument;
use DOMElement;
use DOMException;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Constraint\Callback;
use PHPUnit\Framework\MockObject\Exception;
use PHPUnit\Framework\MockObject\MockObject;
@@ -46,10 +47,11 @@ use SP\Domain\Export\Ports\XmlClientExportService;
use SP\Domain\Export\Ports\XmlTagExportService;
use SP\Domain\Export\Services\XmlExport;
use SP\Domain\File\Ports\DirectoryHandlerService;
use SP\Domain\User\Dtos\UserDataDto;
use SP\Infrastructure\File\FileException;
use SP\Util\VersionUtil;
use SPT\Generators\UserDataGenerator;
use SPT\UnitaryTestCase;
use PHPUnit\Framework\Attributes\Group;
/**
* Class XmlExportTest
@@ -78,10 +80,18 @@ class XmlExportTest extends UnitaryTestCase
*/
public function testExport()
{
$userData = $this->context->getUserData();
$userData->setLogin('test_user');
$userData->setUserGroupName('test_group');
$this->context->setUserData($userData);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(
[
'login' => 'test_user',
'userGroup.name' => 'test_group',
]
)
)
);
$exportPath = $this->createMock(DirectoryHandlerService::class);
$exportPath->expects(self::once())
@@ -216,10 +226,18 @@ class XmlExportTest extends UnitaryTestCase
*/
public function testExportWithCheckDirectoryException()
{
$userData = $this->context->getUserData();
$userData->setLogin('test_user');
$userData->setUserGroupName('test_group');
$this->context->setUserData($userData);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(
[
'login' => 'test_user',
'userGroup.name' => 'test_group',
]
)
)
);
$exportPath = $this->createMock(DirectoryHandlerService::class);
$exportPath->expects(self::once())
@@ -240,10 +258,18 @@ class XmlExportTest extends UnitaryTestCase
*/
public function testExportWithExportCategoryException()
{
$userData = $this->context->getUserData();
$userData->setLogin('test_user');
$userData->setUserGroupName('test_group');
$this->context->setUserData($userData);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(
[
'login' => 'test_user',
'userGroup.name' => 'test_group',
]
)
)
);
$exportPath = $this->createMock(DirectoryHandlerService::class);
$exportPath->expects(self::once())
@@ -274,10 +300,18 @@ class XmlExportTest extends UnitaryTestCase
*/
public function testExportWithExportClientException()
{
$userData = $this->context->getUserData();
$userData->setLogin('test_user');
$userData->setUserGroupName('test_group');
$this->context->setUserData($userData);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(
[
'login' => 'test_user',
'userGroup.name' => 'test_group',
]
)
)
);
$exportPath = $this->createMock(DirectoryHandlerService::class);
$exportPath->expects(self::once())
@@ -315,10 +349,18 @@ class XmlExportTest extends UnitaryTestCase
*/
public function testExportWithExportTagException()
{
$userData = $this->context->getUserData();
$userData->setLogin('test_user');
$userData->setUserGroupName('test_group');
$this->context->setUserData($userData);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(
[
'login' => 'test_user',
'userGroup.name' => 'test_group',
]
)
)
);
$exportPath = $this->createMock(DirectoryHandlerService::class);
$exportPath->expects(self::once())
@@ -361,10 +403,18 @@ class XmlExportTest extends UnitaryTestCase
*/
public function testExportWithExportAccountException()
{
$userData = $this->context->getUserData();
$userData->setLogin('test_user');
$userData->setUserGroupName('test_group');
$this->context->setUserData($userData);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(
[
'login' => 'test_user',
'userGroup.name' => 'test_group',
]
)
)
);
$exportPath = $this->createMock(DirectoryHandlerService::class);
$exportPath->expects(self::once())
@@ -411,10 +461,18 @@ class XmlExportTest extends UnitaryTestCase
*/
public function testExportWithCryptException()
{
$userData = $this->context->getUserData();
$userData->setLogin('test_user');
$userData->setUserGroupName('test_group');
$this->context->setUserData($userData);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(
[
'login' => 'test_user',
'userGroup.name' => 'test_group',
]
)
)
);
$exportPath = $this->createMock(DirectoryHandlerService::class);
$exportPath->expects(self::once())

View File

@@ -34,9 +34,11 @@ use SP\Domain\Core\Exceptions\QueryException;
use SP\Domain\Notification\Models\Notification as NotificationModel;
use SP\Domain\Notification\Ports\NotificationRepository;
use SP\Domain\Notification\Services\Notification;
use SP\Domain\User\Dtos\UserDataDto;
use SP\Infrastructure\Common\Repositories\NoSuchItemException;
use SP\Infrastructure\Database\QueryResult;
use SPT\Generators\NotificationDataGenerator;
use SPT\Generators\UserDataGenerator;
use SPT\UnitaryTestCase;
/**
@@ -84,15 +86,24 @@ class NotificationTest extends UnitaryTestCase
public function testSearchWithAdmin()
{
$userData = $this->context->getUserData()->setIsAdminApp(true);
$this->context->setUserData($userData);
$userDataDto = new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(
[
'isAdminApp' => true,
]
)
);
$this->context->setUserData($userDataDto);
$itemSearchData = new ItemSearchData();
$this->notificationRepository
->expects($this->once())
->method('searchForAdmin')
->with($itemSearchData, $userData->getId());
->with($itemSearchData, $userDataDto->getId());
$this->notification->search($itemSearchData);
}
@@ -151,8 +162,16 @@ class NotificationTest extends UnitaryTestCase
*/
public function testGetAllActiveForCurrentUserWithAdmin()
{
$userData = $this->context->getUserData()->setIsAdminApp(true);
$this->context->setUserData($userData);
$userDataDto = new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(
[
'isAdminApp' => true,
]
)
);
$this->context->setUserData($userDataDto);
$queryResult = $this->createMock(QueryResult::class);
$queryResult->expects($this->once())
@@ -163,7 +182,7 @@ class NotificationTest extends UnitaryTestCase
$this->notificationRepository
->expects($this->once())
->method('getAllActiveForAdmin')
->with($userData->getId())
->with($userDataDto->getId())
->willReturn($queryResult);
$out = $this->notification->getAllActiveForCurrentUser();

View File

@@ -0,0 +1,761 @@
<?php
/*
* sysPass
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2024, 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 SPT\Domain\User\Services;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\MockObject\MockObject;
use RuntimeException;
use SP\Core\Crypt\Hash;
use SP\Domain\Auth\Dtos\UserLoginDto;
use SP\Domain\Common\Services\ServiceException;
use SP\Domain\Config\Ports\ConfigService;
use SP\Domain\Core\Crypt\CryptInterface;
use SP\Domain\Core\Exceptions\CryptException;
use SP\Domain\User\Dtos\UserDataDto;
use SP\Domain\User\Ports\UserRepository;
use SP\Domain\User\Services\UserMasterPass;
use SP\Domain\User\Services\UserMasterPassStatus;
use SPT\Generators\UserDataGenerator;
use SPT\UnitaryTestCase;
/**
* Class UserMasterPassTest
*/
#[Group('unitary')]
class UserMasterPassTest extends UnitaryTestCase
{
private MockObject|UserRepository $userRepository;
private MockObject|ConfigService $configService;
private MockObject|CryptInterface $crypt;
private UserMasterPass $userMasterPass;
/**
* @throws ServiceException
*/
public function testLoad()
{
$user = UserDataGenerator::factory()
->buildUserData()
->mutate(['isChangedPass' => false, 'lastUpdateMPass' => 10]);
$userDataDto = new UserDataDto($user);
$userLoginDto = new UserLoginDto(self::$faker->userName(), self::$faker->password(), $userDataDto);
$this->configService
->expects($this->exactly(2))
->method('getByParam')
->with(...self::withConsecutive(['masterPwd'], ['lastupdatempass']))
->willReturn(Hash::hashKey('a_master_pass'), '5');
$key = $userLoginDto->getLoginPass() .
$userLoginDto->getLoginUser() .
$this->config->getConfigData()->getPasswordSalt();
$this->crypt
->expects($this->once())
->method('decrypt')
->with($user->getMPass(), $user->getMKey(), $key)
->willReturn('a_master_pass');
$out = $this->userMasterPass->load($userLoginDto);
$this->assertEquals(UserMasterPassStatus::Ok, $out->getUserMasterPassStatus());
$this->assertEquals('a_master_pass', $out->getClearMasterPass());
$this->assertEquals($userDataDto->getMPass(), $out->getCryptMasterPass());
$this->assertEquals($userDataDto->getMKey(), $out->getCryptSecuredKey());
}
/**
* @throws ServiceException
*/
public function testLoadWithUserPass()
{
$user = UserDataGenerator::factory()
->buildUserData()
->mutate(['isChangedPass' => false, 'lastUpdateMPass' => 10]);
$userDataDto = new UserDataDto($user);
$userLoginDto = new UserLoginDto(self::$faker->userName(), self::$faker->password(), $userDataDto);
$this->configService
->expects($this->exactly(2))
->method('getByParam')
->with(...self::withConsecutive(['masterPwd'], ['lastupdatempass']))
->willReturn(Hash::hashKey('a_master_pass'), '5');
$key = 'a_password' .
$userLoginDto->getLoginUser() .
$this->config->getConfigData()->getPasswordSalt();
$this->crypt
->expects($this->once())
->method('decrypt')
->with($user->getMPass(), $user->getMKey(), $key)
->willReturn('a_master_pass');
$out = $this->userMasterPass->load($userLoginDto, 'a_password');
$this->assertEquals(UserMasterPassStatus::Ok, $out->getUserMasterPassStatus());
$this->assertEquals('a_master_pass', $out->getClearMasterPass());
$this->assertEquals($userDataDto->getMPass(), $out->getCryptMasterPass());
$this->assertEquals($userDataDto->getMKey(), $out->getCryptSecuredKey());
}
/**
* @throws ServiceException
*/
public function testLoadWithNotSet()
{
$userLoginDto = new UserLoginDto(self::$faker->userName(), self::$faker->password());
$this->configService
->expects($this->never())
->method('getByParam');
$this->crypt
->expects($this->never())
->method('decrypt');
$out = $this->userMasterPass->load($userLoginDto);
$this->assertEquals(UserMasterPassStatus::NotSet, $out->getUserMasterPassStatus());
}
/**
* @throws ServiceException
*/
public function testLoadWithNotSetAndEmptyPass()
{
$userLoginDto = new UserLoginDto(self::$faker->userName());
$this->configService
->expects($this->never())
->method('getByParam');
$this->crypt
->expects($this->never())
->method('decrypt');
$out = $this->userMasterPass->load($userLoginDto);
$this->assertEquals(UserMasterPassStatus::NotSet, $out->getUserMasterPassStatus());
}
/**
* @throws ServiceException
*/
public function testLoadWithNotSetAndEmptyUser()
{
$userLoginDto = new UserLoginDto();
$this->configService
->expects($this->never())
->method('getByParam');
$this->crypt
->expects($this->never())
->method('decrypt');
$out = $this->userMasterPass->load($userLoginDto);
$this->assertEquals(UserMasterPassStatus::NotSet, $out->getUserMasterPassStatus());
}
/**
* @throws ServiceException
*/
public function testLoadWithNotSetAndNullHash()
{
$user = UserDataGenerator::factory()
->buildUserData()
->mutate(['isChangedPass' => false, 'lastUpdateMPass' => 10]);
$userDataDto = new UserDataDto($user);
$userLoginDto = new UserLoginDto(self::$faker->userName(), self::$faker->password(), $userDataDto);
$this->configService
->expects($this->once())
->method('getByParam')
->willReturn(null);
$this->crypt
->expects($this->never())
->method('decrypt');
$out = $this->userMasterPass->load($userLoginDto);
$this->assertEquals(UserMasterPassStatus::NotSet, $out->getUserMasterPassStatus());
}
/**
* @throws ServiceException
*/
public function testLoadWithChanged()
{
$user = UserDataGenerator::factory()
->buildUserData()
->mutate(['isChangedPass' => false, 'lastUpdateMPass' => 0]);
$userDataDto = new UserDataDto($user);
$userLoginDto = new UserLoginDto(self::$faker->userName(), self::$faker->password(), $userDataDto);
$this->configService
->expects($this->exactly(2))
->method('getByParam')
->with(...self::withConsecutive(['masterPwd'], ['lastupdatempass']))
->willReturn(Hash::hashKey('a_master_pass'), '5');
$key = $userLoginDto->getLoginPass() .
$userLoginDto->getLoginUser() .
$this->config->getConfigData()->getPasswordSalt();
$this->crypt
->expects($this->never())
->method('decrypt');
$out = $this->userMasterPass->load($userLoginDto);
$this->assertEquals(UserMasterPassStatus::Changed, $out->getUserMasterPassStatus());
}
/**
* @throws ServiceException
*/
public function testLoadWithCheckOld()
{
$user = UserDataGenerator::factory()
->buildUserData()
->mutate(['isChangedPass' => true, 'lastUpdateMPass' => 10]);
$userDataDto = new UserDataDto($user);
$userLoginDto = new UserLoginDto(self::$faker->userName(), null, $userDataDto);
$this->configService
->expects($this->exactly(2))
->method('getByParam')
->with(...self::withConsecutive(['masterPwd'], ['lastupdatempass']))
->willReturn(Hash::hashKey('a_master_pass'), '5');
$key = $userLoginDto->getLoginPass() .
$userLoginDto->getLoginUser() .
$this->config->getConfigData()->getPasswordSalt();
$this->crypt
->expects($this->never())
->method('decrypt');
$out = $this->userMasterPass->load($userLoginDto);
$this->assertEquals(UserMasterPassStatus::CheckOld, $out->getUserMasterPassStatus());
}
/**
* @throws ServiceException
*/
public function testLoadWithCryptException()
{
$user = UserDataGenerator::factory()
->buildUserData()
->mutate(['isChangedPass' => false, 'lastUpdateMPass' => 10]);
$userDataDto = new UserDataDto($user);
$userLoginDto = new UserLoginDto(self::$faker->userName(), self::$faker->password(), $userDataDto);
$this->configService
->expects($this->exactly(2))
->method('getByParam')
->with(...self::withConsecutive(['masterPwd'], ['lastupdatempass']))
->willReturn(Hash::hashKey('a_master_pass'), '5');
$key = $userLoginDto->getLoginPass() .
$userLoginDto->getLoginUser() .
$this->config->getConfigData()->getPasswordSalt();
$this->crypt
->expects($this->once())
->method('decrypt')
->willThrowException(CryptException::error('test'));
$out = $this->userMasterPass->load($userLoginDto);
$this->assertEquals(UserMasterPassStatus::CheckOld, $out->getUserMasterPassStatus());
}
/**
* @throws ServiceException
*/
public function testLoadWithInvalid()
{
$user = UserDataGenerator::factory()
->buildUserData()
->mutate(['isChangedPass' => false, 'lastUpdateMPass' => 10]);
$userDataDto = new UserDataDto($user);
$userLoginDto = new UserLoginDto(self::$faker->userName(), self::$faker->password(), $userDataDto);
$this->configService
->expects($this->exactly(2))
->method('getByParam')
->with(...self::withConsecutive(['masterPwd'], ['lastupdatempass']))
->willReturn(Hash::hashKey('a_master_pass'), '5');
$key = $userLoginDto->getLoginPass() .
$userLoginDto->getLoginUser() .
$this->config->getConfigData()->getPasswordSalt();
$this->crypt
->expects($this->once())
->method('decrypt')
->with($user->getMPass(), $user->getMKey(), $key)
->willReturn('a_pass');
$out = $this->userMasterPass->load($userLoginDto);
$this->assertEquals(UserMasterPassStatus::Invalid, $out->getUserMasterPassStatus());
}
/**
* @throws ServiceException
*/
public function testLoadWithException()
{
$user = UserDataGenerator::factory()
->buildUserData()
->mutate(['isChangedPass' => false, 'lastUpdateMPass' => 10]);
$userDataDto = new UserDataDto($user);
$userLoginDto = new UserLoginDto(self::$faker->userName(), self::$faker->password(), $userDataDto);
$this->configService
->expects($this->exactly(2))
->method('getByParam')
->with(...self::withConsecutive(['masterPwd'], ['lastupdatempass']))
->willReturn(Hash::hashKey('a_master_pass'), '5');
$key = $userLoginDto->getLoginPass() .
$userLoginDto->getLoginUser() .
$this->config->getConfigData()->getPasswordSalt();
$this->crypt
->expects($this->once())
->method('decrypt')
->willThrowException(new RuntimeException('test'));
$this->expectException(ServiceException::class);
$this->expectExceptionMessage('test');
$this->userMasterPass->load($userLoginDto);
}
/**
* @throws ServiceException
*/
public function testUpdateFromOldPass()
{
$user = UserDataGenerator::factory()
->buildUserData()
->mutate(['isChangedPass' => false, 'lastUpdateMPass' => 10]);
$userDataDto = new UserDataDto($user);
$userLoginDto = new UserLoginDto(self::$faker->userName(), self::$faker->password(), $userDataDto);
$this->configService
->expects($this->exactly(3))
->method('getByParam')
->with(...self::withConsecutive(['masterPwd'], ['lastupdatempass'], ['masterPwd']))
->willReturn(Hash::hashKey('a_master_pass'), '5', Hash::hashKey('a_master_pass'));
$oldKey = 'an_old_user_pass' .
$userLoginDto->getLoginUser() .
$this->config->getConfigData()->getPasswordSalt();
$this->crypt
->expects($this->once())
->method('decrypt')
->with($user->getMPass(), $user->getMKey(), $oldKey)
->willReturn('a_master_pass');
$key = $userLoginDto->getLoginPass() .
$userLoginDto->getLoginUser() .
$this->config->getConfigData()->getPasswordSalt();
$this->crypt
->expects($this->once())
->method('makeSecuredKey')
->with($key)
->willReturn('a_secure_key');
$this->crypt
->expects($this->once())
->method('encrypt')
->with('a_master_pass', 'a_secure_key', $key)
->willReturn('encrypted');
$this->userRepository
->expects($this->once())
->method('updateMasterPassById')
->with($userDataDto->getId(), 'encrypted', 'a_secure_key');
$out = $this->userMasterPass->updateFromOldPass('an_old_user_pass', $userLoginDto);
$this->assertEquals(UserMasterPassStatus::Ok, $out->getUserMasterPassStatus());
$this->assertEquals('encrypted', $out->getCryptMasterPass());
$this->assertEquals('a_secure_key', $out->getCryptSecuredKey());
$this->assertEquals('a_master_pass', $out->getClearMasterPass());
}
/**
* @throws ServiceException
*/
public function testUpdateFromOldPassWithInvalid()
{
$user = UserDataGenerator::factory()
->buildUserData()
->mutate(['isChangedPass' => false, 'lastUpdateMPass' => 10]);
$userDataDto = new UserDataDto($user);
$userLoginDto = new UserLoginDto(self::$faker->userName(), self::$faker->password(), $userDataDto);
$this->configService
->expects($this->exactly(2))
->method('getByParam')
->with(...self::withConsecutive(['masterPwd'], ['lastupdatempass']))
->willReturn(Hash::hashKey('a_master_pass'), '5');
$oldKey = 'an_old_user_pass' .
$userLoginDto->getLoginUser() .
$this->config->getConfigData()->getPasswordSalt();
$this->crypt
->expects($this->once())
->method('decrypt')
->with($user->getMPass(), $user->getMKey(), $oldKey)
->willReturn('another_master_pass');
$this->crypt
->expects($this->never())
->method('makeSecuredKey');
$this->crypt
->expects($this->never())
->method('encrypt');
$this->userRepository
->expects($this->never())
->method('updateMasterPassById');
$out = $this->userMasterPass->updateFromOldPass('an_old_user_pass', $userLoginDto);
$this->assertEquals(UserMasterPassStatus::Invalid, $out->getUserMasterPassStatus());
}
/**
* @throws ServiceException
*/
public function testUpdateOnLogin()
{
$user = UserDataGenerator::factory()
->buildUserData()
->mutate(['isChangedPass' => false, 'lastUpdateMPass' => 10]);
$userDataDto = new UserDataDto($user);
$userLoginDto = new UserLoginDto(self::$faker->userName(), self::$faker->password(), $userDataDto);
$this->configService
->expects($this->once())
->method('getByParam')
->with('masterPwd')
->willReturn(Hash::hashKey('a_master_pass'));
$key = $userLoginDto->getLoginPass() .
$userLoginDto->getLoginUser() .
$this->config->getConfigData()->getPasswordSalt();
$this->crypt
->expects($this->once())
->method('makeSecuredKey')
->with($key)
->willReturn('a_secure_key');
$this->crypt
->expects($this->once())
->method('encrypt')
->with('a_master_pass', 'a_secure_key', $key)
->willReturn('encrypted');
$this->userRepository
->expects($this->once())
->method('updateMasterPassById')
->with($userDataDto->getId(), 'encrypted', 'a_secure_key');
$out = $this->userMasterPass->updateOnLogin('a_master_pass', $userLoginDto);
$this->assertEquals(UserMasterPassStatus::Ok, $out->getUserMasterPassStatus());
$this->assertEquals('encrypted', $out->getCryptMasterPass());
$this->assertEquals('a_secure_key', $out->getCryptSecuredKey());
$this->assertEquals('a_master_pass', $out->getClearMasterPass());
}
/**
* @throws ServiceException
*/
public function testUpdateOnLoginWithSaveHash()
{
$user = UserDataGenerator::factory()
->buildUserData()
->mutate(['isChangedPass' => false, 'lastUpdateMPass' => 10]);
$userDataDto = new UserDataDto($user);
$userLoginDto = new UserLoginDto(self::$faker->userName(), self::$faker->password(), $userDataDto);
$this->configService
->expects($this->once())
->method('getByParam')
->with('masterPwd')
->willReturn(null);
$this->configService
->expects($this->once())
->method('save')
->with(
'masterPwd',
self::callback(static function (string $hash) {
return Hash::checkHashKey('a_master_pass', $hash);
})
);
$key = $userLoginDto->getLoginPass() .
$userLoginDto->getLoginUser() .
$this->config->getConfigData()->getPasswordSalt();
$this->crypt
->expects($this->once())
->method('makeSecuredKey')
->with($key)
->willReturn('a_secure_key');
$this->crypt
->expects($this->once())
->method('encrypt')
->with('a_master_pass', 'a_secure_key', $key)
->willReturn('encrypted');
$this->userRepository
->expects($this->once())
->method('updateMasterPassById')
->with($userDataDto->getId(), 'encrypted', 'a_secure_key');
$out = $this->userMasterPass->updateOnLogin('a_master_pass', $userLoginDto);
$this->assertEquals(UserMasterPassStatus::Ok, $out->getUserMasterPassStatus());
$this->assertEquals('encrypted', $out->getCryptMasterPass());
$this->assertEquals('a_secure_key', $out->getCryptSecuredKey());
$this->assertEquals('a_master_pass', $out->getClearMasterPass());
}
/**
* @throws ServiceException
*/
public function testUpdateOnLoginWithException()
{
$user = UserDataGenerator::factory()
->buildUserData()
->mutate(['isChangedPass' => false, 'lastUpdateMPass' => 10]);
$userDataDto = new UserDataDto($user);
$userLoginDto = new UserLoginDto(self::$faker->userName(), self::$faker->password(), $userDataDto);
$this->configService
->expects($this->once())
->method('getByParam')
->with('masterPwd')
->willReturn(null);
$this->configService
->expects($this->once())
->method('save')
->with(
'masterPwd',
self::callback(static function (string $hash) {
return Hash::checkHashKey('a_master_pass', $hash);
})
);
$key = $userLoginDto->getLoginPass() .
$userLoginDto->getLoginUser() .
$this->config->getConfigData()->getPasswordSalt();
$this->crypt
->expects($this->once())
->method('makeSecuredKey')
->with($key)
->willReturn('a_secure_key');
$this->crypt
->expects($this->once())
->method('encrypt')
->with('a_master_pass', 'a_secure_key', $key)
->willReturn('encrypted');
$this->userRepository
->expects($this->once())
->method('updateMasterPassById')
->willThrowException(new RuntimeException('test'));
$this->expectException(ServiceException::class);
$this->expectExceptionMessage('test');
$this->userMasterPass->updateOnLogin('a_master_pass', $userLoginDto);
}
/**
* @throws ServiceException
*/
public function testCreate()
{
$key = 'a_password' .
'a_login' .
$this->config->getConfigData()->getPasswordSalt();
$this->crypt
->expects($this->once())
->method('makeSecuredKey')
->with($key)
->willReturn('a_secure_key');
$this->crypt
->expects($this->once())
->method('encrypt')
->with('a_master_pass', 'a_secure_key', $key)
->willReturn('encrypted');
$out = $this->userMasterPass->create('a_master_pass', 'a_login', 'a_password');
$this->assertEquals(UserMasterPassStatus::Ok, $out->getUserMasterPassStatus());
$this->assertEquals('encrypted', $out->getCryptMasterPass());
$this->assertEquals('a_secure_key', $out->getCryptSecuredKey());
$this->assertEquals('a_master_pass', $out->getClearMasterPass());
}
/**
* @throws ServiceException
*/
public function testCreateWithLongKey()
{
$key = 'a_password' .
'a_login' .
$this->config->getConfigData()->getPasswordSalt();
$longKey = str_repeat('a', 1001);
$this->crypt
->expects($this->once())
->method('makeSecuredKey')
->with($key)
->willReturn($longKey);
$this->crypt
->expects($this->never())
->method('encrypt');
$this->expectException(ServiceException::class);
$this->expectExceptionMessage('Internal error');
$this->userMasterPass->create('a_master_pass', 'a_login', 'a_password');
}
/**
* @throws ServiceException
*/
public function testCreateWithLongMasterPass()
{
$key = 'a_password' .
'a_login' .
$this->config->getConfigData()->getPasswordSalt();
$this->crypt
->expects($this->once())
->method('makeSecuredKey')
->with($key)
->willReturn('a_secured_key');
$this->crypt
->expects($this->once())
->method('encrypt')
->with('a_master_pass', 'a_secured_key', $key)
->willReturn(str_repeat('a', 1001));
$this->expectException(ServiceException::class);
$this->expectExceptionMessage('Internal error');
$this->userMasterPass->create('a_master_pass', 'a_login', 'a_password');
}
/**
* @throws ServiceException
*/
public function testCreateWithException()
{
$key = 'a_password' .
'a_login' .
$this->config->getConfigData()->getPasswordSalt();
$this->crypt
->expects($this->once())
->method('makeSecuredKey')
->with($key)
->willReturn('a_secured_key');
$this->crypt
->expects($this->once())
->method('encrypt')
->with('a_master_pass', 'a_secured_key', $key)
->willThrowException(CryptException::error('test'));
$this->expectException(ServiceException::class);
$this->expectExceptionMessage('test');
$this->userMasterPass->create('a_master_pass', 'a_login', 'a_password');
}
protected function setUp(): void
{
parent::setUp();
$this->userRepository = $this->createMock(UserRepository::class);
$this->configService = $this->createMock(ConfigService::class);
$this->crypt = $this->createMock(CryptInterface::class);
$this->userMasterPass = new UserMasterPass(
$this->application,
$this->userRepository,
$this->configService,
$this->crypt
);
}
}

View File

@@ -37,6 +37,6 @@ final class ConfigDataGenerator extends DataGenerator
{
return new ConfigData([
ConfigDataInterface::PASSWORD_SALT => $this->faker->sha1(),
]);
]);
}
}

View File

@@ -38,6 +38,7 @@ use SP\Domain\Common\Models\Simple as SimpleModel;
use SP\Domain\Core\Exceptions\ConstraintException;
use SP\Domain\Core\Exceptions\QueryException;
use SP\Domain\Core\Exceptions\SPException;
use SP\Domain\User\Dtos\UserDataDto;
use SP\Domain\User\Models\User as UserModel;
use SP\Infrastructure\Common\Repositories\DuplicatedItemException;
use SP\Infrastructure\Database\DatabaseInterface;
@@ -599,10 +600,17 @@ class UserTest extends UnitaryTestCase
*/
public function testSearchWithAdmin()
{
$userData = $this->context->getUserData();
$userData->setIsAdminApp(true);
$this->context->setUserData($userData);
$this->context->setUserData(
new UserDataDto(
UserDataGenerator::factory()
->buildUserData()
->mutate(
[
'isAdminApp' => true,
]
)
)
);
$item = new ItemSearchData(self::$faker->name);
@@ -673,11 +681,12 @@ class UserTest extends UnitaryTestCase
$query = $arg->getQuery();
$params = $query->getBindValues();
return count($params) === 4
return count($params) === 5
&& $params['id'] === $user->getId()
&& $params['pass'] === $user->getPass()
&& $params['isChangePass'] === $user->isChangePass()
&& $params['isChangedPass'] === $user->isChangedPass()
&& $params['isMigrate'] === $user->isMigrate()
&& is_a($query, UpdateInterface::class)
&& !empty($query->getStatement());
}

View File

@@ -4,7 +4,7 @@
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org
* @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
@@ -26,7 +26,7 @@ namespace SPT\Providers\Auth;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\MockObject\Exception;
use SP\DataModel\UserLoginData;
use SP\Domain\Auth\Dtos\UserLoginDto;
use SP\Domain\Auth\Services\AuthException;
use SP\Providers\Auth\AuthInterface;
use SP\Providers\Auth\AuthProvider;
@@ -66,7 +66,7 @@ class AuthProviderTest extends UnitaryTestCase
*/
public function testDoAuth()
{
$userLoginData = new UserLoginData();
$userLoginData = new UserLoginDto();
$userLoginData->setLoginUser(self::$faker->userName);
$userLoginData->setLoginPass(self::$faker->password);

View File

@@ -4,7 +4,7 @@
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org
* @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
@@ -26,7 +26,7 @@ namespace SPT\Providers\Auth\Browser;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\MockObject\MockObject;
use SP\DataModel\UserLoginData;
use SP\Domain\Auth\Dtos\UserLoginDto;
use SP\Domain\Config\Ports\ConfigDataInterface;
use SP\Domain\Http\RequestInterface;
use SP\Providers\Auth\Browser\BrowserAuth;
@@ -83,7 +83,7 @@ class BrowserAuthTest extends UnitaryTestCase
$user = self::$faker->userName;
$pass = self::$faker->password;
$userLoginData = new UserLoginData();
$userLoginData = new UserLoginDto();
$userLoginData->setLoginUser($user);
$userLoginData->setLoginPass($pass);
@@ -125,7 +125,7 @@ class BrowserAuthTest extends UnitaryTestCase
->with(...$this->withConsecutive(['PHP_AUTH_USER'], ['PHP_AUTH_PW']))
->willReturn($user, $pass);
$out = $this->browserAuth->authenticate(new UserLoginData());
$out = $this->browserAuth->authenticate(new UserLoginDto());
self::assertInstanceOf(BrowserAuthData::class, $out);
self::assertTrue($out->isOk());
@@ -146,7 +146,7 @@ class BrowserAuthTest extends UnitaryTestCase
->with(...$this->withConsecutive(['PHP_AUTH_USER'], ['REMOTE_USER'], ['PHP_AUTH_PW']))
->willReturn('', '', '', $pass);
$out = $this->browserAuth->authenticate(new UserLoginData());
$out = $this->browserAuth->authenticate(new UserLoginDto());
self::assertInstanceOf(BrowserAuthData::class, $out);
self::assertFalse($out->isOk());
@@ -167,7 +167,7 @@ class BrowserAuthTest extends UnitaryTestCase
->with(...$this->withConsecutive(['PHP_AUTH_USER'], ['PHP_AUTH_PW']))
->willReturn($user, '');
$out = $this->browserAuth->authenticate(new UserLoginData());
$out = $this->browserAuth->authenticate(new UserLoginDto());
self::assertInstanceOf(BrowserAuthData::class, $out);
self::assertFalse($out->isOk());
@@ -188,7 +188,7 @@ class BrowserAuthTest extends UnitaryTestCase
->with('PHP_AUTH_USER')
->willReturn($user);
$userLoginData = new UserLoginData();
$userLoginData = new UserLoginDto();
$userLoginData->setLoginUser($user);
$out = $this->browserAuth->authenticate($userLoginData);
@@ -212,7 +212,7 @@ class BrowserAuthTest extends UnitaryTestCase
->with('PHP_AUTH_USER')
->willReturn($user);
$userLoginData = new UserLoginData();
$userLoginData = new UserLoginDto();
$userLoginData->setLoginUser(self::$faker->userName);
$out = $this->browserAuth->authenticate($userLoginData);

View File

@@ -4,7 +4,7 @@
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org
* @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
@@ -27,9 +27,9 @@ namespace SPT\Providers\Auth\Database;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\MockObject\MockObject;
use SP\Core\Crypt\Hash;
use SP\DataModel\UserLoginData;
use SP\Domain\User\Ports\UserPassServiceInterface;
use SP\Domain\Auth\Dtos\UserLoginDto;
use SP\Domain\User\Ports\UserServiceInterface;
use SP\Domain\User\Services\UserPassService;
use SP\Infrastructure\Common\Repositories\NoSuchItemException;
use SP\Providers\Auth\Database\DatabaseAuth;
use SPT\Generators\UserDataGenerator;
@@ -43,9 +43,9 @@ use SPT\UnitaryTestCase;
class DatabaseAuthTest extends UnitaryTestCase
{
private UserServiceInterface|MockObject $userService;
private MockObject|UserPassServiceInterface $userPassService;
private DatabaseAuth $databaseAuth;
private UserServiceInterface|MockObject $userService;
private MockObject|UserPassService $userPassService;
private DatabaseAuth $databaseAuth;
public function testAuthenticate()
{
@@ -61,7 +61,7 @@ class DatabaseAuthTest extends UnitaryTestCase
->with($user)
->willReturn($userData);
$userLoginData = new UserLoginData();
$userLoginData = new UserLoginDto();
$userLoginData->setLoginUser($user);
$userLoginData->setLoginPass($pass);
@@ -79,7 +79,7 @@ class DatabaseAuthTest extends UnitaryTestCase
->with($user)
->willThrowException(new NoSuchItemException('User does not exist'));
$userLoginData = new UserLoginData();
$userLoginData = new UserLoginDto();
$userLoginData->setLoginUser($user);
$userLoginData->setLoginPass($pass);
@@ -99,7 +99,7 @@ class DatabaseAuthTest extends UnitaryTestCase
->with($user)
->willReturn($userData);
$userLoginData = new UserLoginData();
$userLoginData = new UserLoginDto();
$userLoginData->setLoginUser($user);
$userLoginData->setLoginPass($pass);
@@ -132,7 +132,7 @@ class DatabaseAuthTest extends UnitaryTestCase
->method('migrateUserPassById')
->with($userData->getId(), $pass);
$userLoginData = new UserLoginData();
$userLoginData = new UserLoginDto();
$userLoginData->setLoginUser($user);
$userLoginData->setLoginPass($pass);
@@ -167,7 +167,7 @@ class DatabaseAuthTest extends UnitaryTestCase
->method('migrateUserPassById')
->with($userData->getId(), $pass);
$userLoginData = new UserLoginData();
$userLoginData = new UserLoginDto();
$userLoginData->setLoginUser($user);
$userLoginData->setLoginPass($pass);
@@ -202,7 +202,7 @@ class DatabaseAuthTest extends UnitaryTestCase
->method('migrateUserPassById')
->with($userData->getId(), $pass);
$userLoginData = new UserLoginData();
$userLoginData = new UserLoginDto();
$userLoginData->setLoginUser($user);
$userLoginData->setLoginPass($pass);
@@ -236,7 +236,7 @@ class DatabaseAuthTest extends UnitaryTestCase
->method('migrateUserPassById')
->with($userData->getId(), $pass);
$userLoginData = new UserLoginData();
$userLoginData = new UserLoginDto();
$userLoginData->setLoginUser($user);
$userLoginData->setLoginPass($pass);
@@ -253,7 +253,7 @@ class DatabaseAuthTest extends UnitaryTestCase
parent::setUp();
$this->userService = $this->createMock(UserServiceInterface::class);
$this->userPassService = $this->createMock(UserPassServiceInterface::class);
$this->userPassService = $this->createMock(UserPassService::class);
$this->databaseAuth = new DatabaseAuth($this->userService, $this->userPassService);
}

View File

@@ -29,7 +29,7 @@ use PHPUnit\Framework\Constraint\Callback;
use PHPUnit\Framework\MockObject\Exception;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\MockObject\Rule\InvokedCount;
use SP\DataModel\UserLoginData;
use SP\Domain\Auth\Dtos\UserLoginDto;
use SP\Domain\Auth\Ports\LdapActionsService;
use SP\Domain\Auth\Ports\LdapService;
use SP\Domain\Config\Ports\ConfigDataInterface;
@@ -58,7 +58,7 @@ class LdapAuthTest extends UnitaryTestCase
*/
public function testAuthenticate()
{
$userLoginData = new UserLoginData();
$userLoginData = new UserLoginDto();
$userLoginData->setLoginUser(self::$faker->userName);
$userLoginData->setLoginPass(self::$faker->password);
@@ -140,7 +140,7 @@ class LdapAuthTest extends UnitaryTestCase
*/
public function testAuthenticateWithExpireFail()
{
$userLoginData = new UserLoginData();
$userLoginData = new UserLoginDto();
$userLoginData->setLoginUser(self::$faker->userName);
$userLoginData->setLoginPass(self::$faker->password);
@@ -188,7 +188,7 @@ class LdapAuthTest extends UnitaryTestCase
*/
public function testAuthenticateWithGroupFail()
{
$userLoginData = new UserLoginData();
$userLoginData = new UserLoginDto();
$userLoginData->setLoginUser(self::$faker->userName);
$userLoginData->setLoginPass(self::$faker->password);
@@ -236,7 +236,7 @@ class LdapAuthTest extends UnitaryTestCase
*/
public function testAuthenticateFailConnect()
{
$userLoginData = new UserLoginData();
$userLoginData = new UserLoginDto();
$userLoginData->setLoginUser(self::$faker->userName);
$userLoginData->setLoginPass(self::$faker->password);

View File

@@ -35,7 +35,8 @@ use SP\DataModel\ProfileData;
use SP\Domain\Config\Ports\ConfigFileService;
use SP\Domain\Core\Context\ContextInterface;
use SP\Domain\Core\Events\EventDispatcherInterface;
use SP\Domain\User\Services\UserLoginResponse;
use SP\Domain\User\Dtos\UserDataDto;
use SP\Domain\User\Models\User;
use SPT\Generators\ConfigDataGenerator;
/**
@@ -95,17 +96,9 @@ abstract class UnitaryTestCase extends TestCase
*/
private function mockApplication(): Application
{
$userLogin = new UserLoginResponse();
$userLogin
->setLogin(self::$faker->userName)
->setName(self::$faker->userName)
->setId(self::$faker->randomNumber(2))
->setUserGroupId(self::$faker->randomNumber(2))
->setUserProfileId(self::$faker->randomNumber(2));
$this->context = new StatelessContext();
$this->context->initialize();
$this->context->setUserData($userLogin);
$this->context->setUserData($this->getUserDataDto());
$this->context->setUserProfile(new ProfileData());
return new Application(
@@ -115,6 +108,24 @@ abstract class UnitaryTestCase extends TestCase
);
}
/**
* @return UserDataDto
*/
private function getUserDataDto(): UserDataDto
{
return new UserDataDto(
new User(
[
'login' => self::$faker->userName,
'name' => self::$faker->userName,
'id' => self::$faker->randomNumber(2),
'userGroupId' => self::$faker->randomNumber(2),
'userProfileId' => self::$faker->randomNumber(2)
]
)
);
}
/**
* @throws Exception
*/

View File

@@ -32,7 +32,7 @@ use SP\Core\Context\ContextException;
use SP\DataModel\ProfileData;
use SP\Domain\Core\Context\ContextInterface;
use SP\Domain\Core\Exceptions\FileNotFoundException;
use SP\Domain\User\Services\UserLoginResponse;
use SP\Domain\User\Dtos\UserDataDto;
use SP\Infrastructure\Database\DatabaseConnectionData;
use SP\Infrastructure\Database\DbStorageHandler;
use SP\Infrastructure\Database\MysqlHandler;
@@ -127,7 +127,7 @@ function setupContext(): Container
$context->setTrasientKey('_masterpass', '12345678900');
$userData = new UserLoginResponse();
$userData = new UserDataDto();
$userData->setId(1);
$userData->setLogin('Admin');
$userData->setUserGroupName('Admins');