diff --git a/application/Espo/Core/ApplicationRunners/EntryPoint.php b/application/Espo/Core/ApplicationRunners/EntryPoint.php index 8290c8f5ef..a04a8d5e29 100644 --- a/application/Espo/Core/ApplicationRunners/EntryPoint.php +++ b/application/Espo/Core/ApplicationRunners/EntryPoint.php @@ -43,6 +43,7 @@ use Espo\Core\{ Api\ErrorOutput as ApiErrorOutput, Api\RequestWrapper, Api\ResponseWrapper, + Authentication\AuthToken\AuthTokenManager, }; use Slim\{ @@ -64,19 +65,22 @@ class EntryPoint implements ApplicationRunner protected $entityManager; protected $clientManager; protected $applicationUser; + protected $authTokenManager; public function __construct( InjectableFactory $injectableFactory, EntryPointManager $entryPointManager, EntityManager $entityManager, ClientManager $clientManager, - ApplicationUser $applicationUser + ApplicationUser $applicationUser, + AuthTokenManager $authTokenManager ) { $this->injectableFactory = $injectableFactory; $this->entryPointManager = $entryPointManager; $this->entityManager = $entityManager; $this->clientManager = $clientManager; $this->applicationUser = $applicationUser; + $this->authTokenManager = $authTokenManager; } public function run(?StdClass $params = null) @@ -169,15 +173,14 @@ class EntryPoint implements ApplicationRunner return $_GET['portalId']; } - if (!empty($_COOKIE['auth-token'])) { - $token = $this->entityManager - ->getRepository('AuthToken') - ->where(['token' => $_COOKIE['auth-token']]) - ->findOne(); + if (empty($_COOKIE['auth-token'])) { + return null; + } - if ($token && $token->get('portalId')) { - return $token->get('portalId'); - } + $authToken = $this->authTokenManager->get($_COOKIE['auth-token']); + + if ($authToken) { + return $authToken->getPortalId(); } return null; diff --git a/application/Espo/Core/Authentication/AuthToken/AuthToken.php b/application/Espo/Core/Authentication/AuthToken/AuthToken.php new file mode 100644 index 0000000000..59618c6d34 --- /dev/null +++ b/application/Espo/Core/Authentication/AuthToken/AuthToken.php @@ -0,0 +1,67 @@ +userId; + } + + public function getPortalId() : ?string + { + return $this->portalId; + } + + public function getHash() : ?string + { + return $this->hash; + } + + public function getIpAddress() : ?string + { + return $this->ipAddress; + } + + public function toCreateSecret() : bool + { + return $this->createSecret; + } + + public static function create(array $data) : self + { + $object = new self(); + + $object->userId = $data['userId'] ?? null; + $object->portalId = $data['portalId'] ?? null; + $object->hash = $data['hash'] ?? null; + $object->ipAddress = $data['ipAddress'] ?? null; + $object->createSecret = $data['createSecret'] ?? false; + + $object->validate(); + + return $object; + } + + protected function validate() + { + // @todo Use typed properties when php 7.4 is a min supported version. + if ( + isset($this->userId) && !is_string($this->userId) || + isset($this->portalId) && !is_string($this->portalId) || + isset($this->hash) && !is_string($this->hash) || + isset($this->ipAddress) && !is_string($this->ipAddress) || + !is_bool($this->createSecret) + ) { + throw new RuntimeException("Invalid data."); + } + + if (!$this->userId) { + throw new RuntimeException("No user ID."); + } + } +} diff --git a/application/Espo/Core/Authentication/AuthToken/AuthTokenManager.php b/application/Espo/Core/Authentication/AuthToken/AuthTokenManager.php new file mode 100644 index 0000000000..301595e36a --- /dev/null +++ b/application/Espo/Core/Authentication/AuthToken/AuthTokenManager.php @@ -0,0 +1,56 @@ +entityManager = $entityManager; + + $this->repository = $entityManager->getRepository('AuthToken'); + } + + public function get(string $token) : ?AuthToken + { + $authToken = $this->entityManager + ->getRepository('AuthToken') + ->select([ + 'id', + 'isActive', + 'token', + 'secret', + 'userId', + 'portalId', + 'hash', + 'createdAt', + 'lastAccess', + 'modifiedAt', + ]) + ->where([ + 'token' => $token, + ]) + ->findOne(); + + return $authToken; + } + + public function create(AuthTokenData $authTokenData) : AuthToken + { + $authToken = $this->repository->getNew(); + + $authToken->set([ + 'userId' => $authTokenData->getUserId(), + 'portalId' => $authTokenData->getPortalId(), + 'hash' => $authTokenData->getHash(), + 'ipAddress' => $authTokenData->getIpAddress(), + 'lastAccess' => date('Y-m-d H:i:s'), + 'token' => $this->generateToken(), + ]); + + if ($authTokenData->toCreateSecret()) { + $authToken->set('secret', $this->generateToken()); + } + + $this->validate($authToken); + + $this->repository->save($authToken); + + return $authToken; + } + + public function inactivate(AuthToken $authToken) + { + $this->validateNotChanged($authToken); + + $authToken->set('isActive', false); + + $this->repository->save($authToken); + } + + public function renew(AuthToken $authToken) + { + $this->validateNotChanged($authToken); + + if ($authToken->isNew()) { + throw new RuntimeException("Can renew only not new auth token."); + } + + $authToken->set('lastAccess', date('Y-m-d H:i:s')); + + $this->repository->save($authToken); + } + + protected function validate(AuthToken $authToken) + { + if (!$authToken->getToken()) { + throw new RuntimeException("Empty token."); + } + + if (!$authToken->getUserId()) { + throw new RuntimeException("Empty user ID."); + } + } + + protected function validateNotChanged(AuthToken $authToken) + { + if ( + $authToken->isAttributeChanged('token') || + $authToken->isAttributeChanged('secret') || + $authToken->isAttributeChanged('hash') || + $authToken->isAttributeChanged('userId') || + $authToken->isAttributeChanged('portalId') + ) { + throw new RuntimeException("Auth token was changed."); + } + } + + protected function generateToken() + { + $length = self::TOKEN_RANDOM_LENGTH; + + if (function_exists('random_bytes')) { + return bin2hex(random_bytes($length)); + } + + if (function_exists('mcrypt_create_iv')) { + return bin2hex(mcrypt_create_iv($length, MCRYPT_DEV_URANDOM)); + } + + if (function_exists('openssl_random_pseudo_bytes')) { + return bin2hex(openssl_random_pseudo_bytes($length)); + } + } +} diff --git a/application/Espo/Core/Authentication/Authentication.php b/application/Espo/Core/Authentication/Authentication.php index d5619efa1b..5639037489 100644 --- a/application/Espo/Core/Authentication/Authentication.php +++ b/application/Espo/Core/Authentication/Authentication.php @@ -46,6 +46,8 @@ use Espo\Core\Authentication\{ Result, LoginFactory, TwoFactor\Factory as TwoFAFactory, + AuthToken\AuthTokenManager, + AuthToken\AuthTokenData, }; use Espo\Core\{ @@ -89,7 +91,8 @@ class Authentication Metadata $metadata, EntityManagerProxy $entityManagerProxy, LoginFactory $authLoginFactory, - TwoFAFactory $auth2FAFactory + TwoFAFactory $auth2FAFactory, + AuthTokenManager $authTokenManager ) { $this->allowAnyAccess = $allowAnyAccess; @@ -100,6 +103,7 @@ class Authentication $this->entityManager = $entityManagerProxy; $this->authLoginFactory = $authLoginFactory; $this->auth2FAFactory = $auth2FAFactory; + $this->authTokenManager = $authTokenManager; } protected function getDefaultAuthenticationMethod() @@ -160,15 +164,12 @@ class Authentication $authTokenIsFound = false; if (!$authenticationMethod) { - $authToken = $this->entityManager - ->getRepository('AuthToken') - ->where(['token' => $password]) - ->findOne(); + $authToken = $this->authTokenManager->get($password); - if ($authToken && $authToken->get('secret')) { + if ($authToken && $authToken->getSecret()) { $sentSecret = $request->getCookieParam('auth-token-secret'); - if ($sentSecret !== $authToken->get('secret')) { + if ($sentSecret !== $authToken->getSecret()) { $authToken = null; } } @@ -178,15 +179,15 @@ class Authentication $authTokenIsFound = true; } - if ($authToken && $authToken->get('isActive')) { + if ($authToken && $authToken->isActive()) { if (!$this->allowAnyAccess) { - if ($this->isPortal() && $authToken->get('portalId') !== $this->getPortal()->id) { + if ($this->isPortal() && $authToken->getPortalId() !== $this->getPortal()->id) { $GLOBALS['log']->info("AUTH: Trying to login to portal with a token not related to portal."); return null; } - if (!$this->isPortal() && $authToken->get('portalId')) { + if (!$this->isPortal() && $authToken->getPortalId()) { $GLOBALS['log']->info("AUTH: Trying to login to crm with a token related to portal."); return null; @@ -194,8 +195,8 @@ class Authentication } if ($this->allowAnyAccess) { - if ($authToken->get('portalId') && !$this->isPortal()) { - $portal = $this->entityManager->getEntity('Portal', $authToken->get('portalId')); + if ($authToken->getPortalId() && !$this->isPortal()) { + $portal = $this->entityManager->getEntity('Portal', $authToken->getPortalId()); if ($portal) { $this->setPortal($portal); @@ -265,17 +266,15 @@ class Authentication if (!$result->isSecondStepRequired() && $request->getHeader('Espo-Authorization')) { if (!$authToken) { $authToken = $this->createAuthToken($user, $request); + } else { + $this->authTokenManager->renew($authToken); } - $authToken->set('lastAccess', date('Y-m-d H:i:s')); - - $this->entityManager->saveEntity($authToken); - - $user->set('token', $authToken->get('token')); - $user->set('authTokenId', $authToken->id); + $user->set('token', $authToken->getToken()); + $user->set('authTokenId', $authToken->id ?? null); if ($authLogRecord) { - $authLogRecord->set('authTokenId', $authToken->id); + $authLogRecord->set('authTokenId', $authToken->id ?? null); } } @@ -283,10 +282,11 @@ class Authentication $this->entityManager->saveEntity($authLogRecord); } - if ($authToken && !$authLogRecord) { + if ($authToken && !$authLogRecord && isset($authToken->id)) { $authLogRecord = $this->entityManager ->getRepository('AuthLogRecord') - ->select(['id'])->where([ + ->select(['id']) + ->where([ 'authTokenId' => $authToken->id ]) ->order('requestTime', true) @@ -450,38 +450,35 @@ class Authentication protected function createAuthToken(User $user, Request $request) : AuthToken { - $createTokenSecret = $request->getHeader('Espo-Authorization-Create-Token-Secret') === 'true'; + $createSecret = $request->getHeader('Espo-Authorization-Create-Token-Secret') === 'true'; - if ($createTokenSecret) { + if ($createSecret) { if ($this->config->get('authTokenSecretDisabled')) { - $createTokenSecret = false; + $createSecret = false; } } - $authToken = $this->entityManager->getEntity('AuthToken'); + $arrayData = [ + 'hash' => $user->get('password'), + 'ipAddress' => $request->getServerParam('REMOTE_ADDR'), + 'userId' => $user->id, + 'portalId' => $this->isPortal() ? $this->getPortal()->id : null, + 'createSecret' => $createSecret, + ]; - $token = $this->generateToken(); + $authToken = $this->authTokenManager->create( + AuthTokenData::create($arrayData) + ); - $authToken->set('token', $token); - $authToken->set('hash', $user->get('password')); - $authToken->set('ipAddress', $request->getServerParam('REMOTE_ADDR')); - $authToken->set('userId', $user->id); - - if ($createTokenSecret) { - $secret = $this->generateToken(); - $authToken->set('secret', $secret); - - $this->setSecretInCookie($secret); - } - - if ($this->isPortal()) { - $authToken->set('portalId', $this->getPortal()->id); + if ($createSecret) { + $this->setSecretInCookie($authToken->getSecret()); } if ($this->config->get('authTokenPreventConcurrent')) { $concurrentAuthTokenList = $this->entityManager ->getRepository('AuthToken') - ->select(['id'])->where([ + ->select(['id']) + ->where([ 'userId' => $user->id, 'isActive' => true, ]) @@ -497,48 +494,25 @@ class Authentication return $authToken; } - protected function generateToken() - { - $length = 16; - - if (function_exists('random_bytes')) { - return bin2hex(random_bytes($length)); - } - - if (function_exists('mcrypt_create_iv')) { - return bin2hex(mcrypt_create_iv($length, \MCRYPT_DEV_URANDOM)); - } - - if (function_exists('openssl_random_pseudo_bytes')) { - return bin2hex(openssl_random_pseudo_bytes($length)); - } - } - public function destroyAuthToken(string $token, Request $request) { - $authToken = $this->entityManager - ->getRepository('AuthToken') - ->select(['id', 'isActive', 'secret']) - ->where( - ['token' => $token] - ) - ->findOne(); + $authToken = $this->authTokenManager->get($token); - if ($authToken) { - $authToken->set('isActive', false); - - $this->entityManager->saveEntity($authToken); - - if ($authToken->get('secret')) { - $sentSecret = $request->getCookieParam('auth-token-secret'); - - if ($sentSecret === $authToken->get('secret')) { - $this->setSecretInCookie(null); - } - } - - return true; + if (!$authToken) { + return false; } + + $this->authTokenManager->inactivate($authToken); + + if ($authToken->getSecret()) { + $sentSecret = $request->getCookieParam('auth-token-secret'); + + if ($sentSecret === $authToken->getSecret()) { + $this->setSecretInCookie(null); + } + } + + return true; } protected function createAuthLogRecord( diff --git a/application/Espo/Core/Authentication/Login/ApiKey.php b/application/Espo/Core/Authentication/Login/ApiKey.php index 6693b2efee..e383284f76 100644 --- a/application/Espo/Core/Authentication/Login/ApiKey.php +++ b/application/Espo/Core/Authentication/Login/ApiKey.php @@ -29,15 +29,11 @@ namespace Espo\Core\Authentication\Login; -use Espo\Entities\{ - User, - AuthToken, -}; - use Espo\Core\{ Api\Request, ORM\EntityManager, Authentication\Result, + Authentication\AuthToken\AuthToken, }; class ApiKey implements Login @@ -53,11 +49,13 @@ class ApiKey implements Login { $apiKey = $request->getHeader('X-Api-Key'); - $user = $this->entityManager->getRepository('User')->where([ - 'type' => 'api', - 'apiKey' => $apiKey, - 'authMethod' => 'ApiKey', - ])->findOne(); + $user = $this->entityManager->getRepository('User') + ->where([ + 'type' => 'api', + 'apiKey' => $apiKey, + 'authMethod' => 'ApiKey', + ]) + ->findOne(); if (!$user) { return Result::fail(); diff --git a/application/Espo/Core/Authentication/Login/Espo.php b/application/Espo/Core/Authentication/Login/Espo.php index a1adf4df66..037ac0dec2 100644 --- a/application/Espo/Core/Authentication/Login/Espo.php +++ b/application/Espo/Core/Authentication/Login/Espo.php @@ -29,16 +29,12 @@ namespace Espo\Core\Authentication\Login; -use Espo\Entities\{ - User, - AuthToken, -}; - use Espo\Core\{ ORM\EntityManager, Api\Request, Utils\PasswordHash, Authentication\Result, + Authentication\AuthToken\AuthToken, }; class Espo implements Login @@ -54,26 +50,30 @@ class Espo implements Login public function login(?string $username, ?string $password, ?AuthToken $authToken = null, ?Request $request = null) : Result { - if (!$password) return Result::fail('Empty password'); + if (!$password) { + return Result::fail('Empty password'); + } if ($authToken) { - $hash = $authToken->get('hash'); + $hash = $authToken->getHash(); } else { $hash = $this->passwordHash->hash($password); } - $user = $this->entityManager->getRepository('User')->where([ - 'userName' => $username, - 'password' => $hash, - 'type!=' => ['api', 'system'], - ])->findOne(); + $user = $this->entityManager->getRepository('User') + ->where([ + 'userName' => $username, + 'password' => $hash, + 'type!=' => ['api', 'system'], + ]) + ->findOne(); if (!$user) { return Result::fail(); } if ($authToken) { - if ($user->id !== $authToken->get('userId')) { + if ($user->id !== $authToken->getUserId()) { return Result::fail('User and token mismatch'); } } diff --git a/application/Espo/Core/Authentication/Login/Hmac.php b/application/Espo/Core/Authentication/Login/Hmac.php index d8f22fc7c7..e8d9336107 100644 --- a/application/Espo/Core/Authentication/Login/Hmac.php +++ b/application/Espo/Core/Authentication/Login/Hmac.php @@ -29,17 +29,13 @@ namespace Espo\Core\Authentication\Login; -use Espo\Entities\{ - User, - AuthToken, -}; - use Espo\Core\{ ORM\EntityManager, Api\Request, Utils\Config, Utils\ApiKey, Authentication\Result, + Authentication\AuthToken\AuthToken, }; class Hmac implements Login @@ -59,18 +55,23 @@ class Hmac implements Login list($apiKey, $hash) = explode(':', $authString, 2); - $user = $this->entityManager->getRepository('User')->where([ - 'type' => 'api', - 'apiKey' => $apiKey, - 'authMethod' => 'Hmac', - ])->findOne(); + $user = $this->entityManager->getRepository('User') + ->where([ + 'type' => 'api', + 'apiKey' => $apiKey, + 'authMethod' => 'Hmac', + ]) + ->findOne(); if (!$user) { return Result::fail(); } $secretKey = (new ApiKey($this->config))->getSecretKeyForUserId($user->id); - if (!$secretKey) return null; + + if (!$secretKey) { + return null; + } $string = $request->getMethod() . ' ' . $request->getResourcePath(); diff --git a/application/Espo/Core/Authentication/Login/LDAP.php b/application/Espo/Core/Authentication/Login/LDAP.php index 89f60fd123..70c2fc7041 100644 --- a/application/Espo/Core/Authentication/Login/LDAP.php +++ b/application/Espo/Core/Authentication/Login/LDAP.php @@ -31,11 +31,6 @@ namespace Espo\Core\Authentication\Login; use Espo\Core\Exceptions\Error; -use Espo\Entities\{ - User, - AuthToken, -}; - use Espo\Core\{ ORM\EntityManager, Api\Request, @@ -46,6 +41,7 @@ use Espo\Core\{ Authentication\Result, Authentication\LDAP\Utils as LDAPUtils, Authentication\LDAP\Client as LDAPClient, + Authentication\AuthToken\AuthToken, }; use Exception; @@ -122,6 +118,7 @@ class LDAP extends Espo if ($isPortal) { $useLdapAuthForPortalUser = $this->utils->getOption('portalUserLdapAuth'); + if (!$useLdapAuthForPortalUser) { return parent::login($username, $password, $authToken, $request); } @@ -232,7 +229,8 @@ class LDAP extends Espo return null; } - $userId = $authToken->get('userId'); + $userId = $authToken->getUserId(); + $user = $this->entityManager->getEntity('User', $userId); $tokenUsername = $user->get('userName'); diff --git a/application/Espo/Core/Authentication/Login/Login.php b/application/Espo/Core/Authentication/Login/Login.php index 80e05a1455..981eb89adb 100644 --- a/application/Espo/Core/Authentication/Login/Login.php +++ b/application/Espo/Core/Authentication/Login/Login.php @@ -29,14 +29,10 @@ namespace Espo\Core\Authentication\Login; -use Espo\Entities\{ - User, - AuthToken, -}; - use Espo\Core\{ Api\Request, Authentication\Result, + Authentication\AuthToken\AuthToken, }; /** diff --git a/application/Espo/Core/Console/Commands/AuthTokenCheck.php b/application/Espo/Core/Console/Commands/AuthTokenCheck.php index 52c1b69b03..9f25faad69 100644 --- a/application/Espo/Core/Console/Commands/AuthTokenCheck.php +++ b/application/Espo/Core/Console/Commands/AuthTokenCheck.php @@ -29,36 +29,58 @@ namespace Espo\Core\Console\Commands; -use Espo\Core\ORM\EntityManager; +use Espo\Core\{ + ORM\EntityManager, + Authentication\AuthToken\AuthTokenManager, +}; class AuthTokenCheck implements Command { protected $entityManager; + protected $authTokenManager; - public function __construct(EntityManager $entityManager) + public function __construct(EntityManager $entityManager, AuthTokenManager $authTokenManager) { $this->entityManager = $entityManager; + $this->authTokenManager = $authTokenManager; } public function run(array $options, array $flagList, array $argumentList) : ?string { $token = $argumentList[0] ?? null; - if (empty($token)) return null; - $entityManager = $this->entityManager; + if (empty($token)) { + return null; + } - $authToken = $entityManager->getRepository('AuthToken')->where([ - 'token' => $token, - 'isActive' => true, - ])->findOne(); + $authToken = $this->authTokenManager->get($token); - if (!$authToken) return null; - if (!$authToken->get('userId')) return null; + if (!$authToken) { + return null; + } - $userId = $authToken->get('userId'); + if (!$authToken->isActive()) { + return null; + } - $user = $entityManager->getEntity('User', $userId); - if (!$user) return null; + if (!$authToken->getUserId()) { + return null; + } + + $userId = $authToken->getUserId(); + + $user = $this->entityManager + ->getRepository('User') + ->select('id') + ->where([ + 'id' => $userId, + 'isActive' => true, + ]) + ->findOne(); + + if (!$user) { + return null; + } return $user->id; } diff --git a/application/Espo/Entities/AuthToken.php b/application/Espo/Entities/AuthToken.php index 449b332aeb..8a25d69a04 100644 --- a/application/Espo/Entities/AuthToken.php +++ b/application/Espo/Entities/AuthToken.php @@ -25,12 +25,44 @@ * * In accordance with Section 7(b) of the GNU General Public License version 3, * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. - ************************************************************************/ + ************************************************************************/ namespace Espo\Entities; -class AuthToken extends \Espo\Core\ORM\Entity +use Espo\Core\{ + ORM\Entity as BaseEntity, + Authentication\AuthToken\AuthToken as AuthTokenInterface, +}; + +class AuthToken extends BaseEntity implements AuthTokenInterface { + public function getToken() : string + { + return $this->get('token'); + } + public function getUserId() : string + { + return $this->get('userId'); + } + + public function getPortalId() : ?string + { + return $this->get('portalId'); + } + + public function getSecret() : ?string + { + return $this->get('secret'); + } + + public function isActive() : bool + { + return $this->get('isActive'); + } + + public function getHash() : ?string + { + return $this->get('hash'); + } } - diff --git a/application/Espo/Resources/metadata/app/containerServices.json b/application/Espo/Resources/metadata/app/containerServices.json index 41121a2abc..db492b8002 100644 --- a/application/Espo/Resources/metadata/app/containerServices.json +++ b/application/Espo/Resources/metadata/app/containerServices.json @@ -1,4 +1,7 @@ { + "authTokenManager": { + "className": "Espo\\Core\\Authentication\\AuthToken\\EspoAuthTokenManager" + }, "schema": { "className": "Espo\\Core\\Utils\\Database\\Schema\\Schema" }, diff --git a/tests/integration/Espo/Core/Authentication/AuthToken/AuthTokenManagerTest.php b/tests/integration/Espo/Core/Authentication/AuthToken/AuthTokenManagerTest.php new file mode 100644 index 0000000000..25058237ef --- /dev/null +++ b/tests/integration/Espo/Core/Authentication/AuthToken/AuthTokenManagerTest.php @@ -0,0 +1,120 @@ +getContainer()->get('authTokenManager'); + + $authTokenData = AuthTokenData::create([ + 'hash' => 'test-hash', + 'ipAddress' => 'ip-address', + 'userId' => 'user-id', + 'portalId' => 'portal-id', + 'createSecret' => true, + ]); + + $authToken = $authTokenManager->create($authTokenData); + + $this->assertEquals($authTokenData->getHash(), $authToken->getHash()); + $this->assertEquals($authTokenData->getUserId(), $authToken->getUserId()); + $this->assertEquals($authTokenData->getPortalId(), $authToken->getPortalId()); + + $this->assertTrue($authToken->isActive()); + + $this->assertNotEmpty($authToken->getToken()); + $this->assertNotEmpty($authToken->getSecret()); + + $this->assertNotEmpty($authToken->get('lastAccess')); + } + + public function testCreateWithNoSecretNoPortal() + { + $authTokenManager = $this->getContainer()->get('authTokenManager'); + + $authTokenData = AuthTokenData::create([ + 'hash' => 'test-hash', + 'ipAddress' => 'ip-address', + 'userId' => 'user-id', + 'portalId' => null, + 'createSecret' => false, + ]); + + $authToken = $authTokenManager->create($authTokenData); + + $this->assertEquals($authTokenData->getHash(), $authToken->getHash()); + $this->assertEquals($authTokenData->getUserId(), $authToken->getUserId()); + + $this->assertEmpty($authToken->getPortalId()); + + $this->assertNotEmpty($authToken->getToken()); + $this->assertEmpty($authToken->getSecret()); + } + + public function testRenew() + { + $authTokenManager = $this->getContainer()->get('authTokenManager'); + + $authTokenData = AuthTokenData::create([ + 'hash' => 'test-hash', + 'userId' => 'user-id', + ]); + + $authToken = $authTokenManager->create($authTokenData); + + $authToken = $authTokenManager->get($authToken->getToken()); + + $authTokenManager->renew($authToken); + + $this->assertNotEmpty($authToken->get('lastAccess')); + } + + public function testInactivate() + { + $authTokenManager = $this->getContainer()->get('authTokenManager'); + + $authTokenData = AuthTokenData::create([ + 'hash' => 'test-hash', + 'userId' => 'user-id', + ]); + + $authToken = $authTokenManager->create($authTokenData); + + $authToken = $authTokenManager->get($authToken->getToken()); + + $authTokenManager->inactivate($authToken); + + $this->assertFalse($authToken->isActive()); + } +} diff --git a/tests/unit/Espo/Core/Authentication/AuthToken/AuthTokenDataTest.php b/tests/unit/Espo/Core/Authentication/AuthToken/AuthTokenDataTest.php new file mode 100644 index 0000000000..9a601f2f65 --- /dev/null +++ b/tests/unit/Espo/Core/Authentication/AuthToken/AuthTokenDataTest.php @@ -0,0 +1,52 @@ + 'hash', + 'ipAddress' => 'ip-address', + 'userId' => 'user-id', + 'portalId' => 'portal-id', + 'createSecret' => true, + ]); + + $this->assertEquals('hash', $authTokenData->getHash()); + $this->assertEquals('ip-address', $authTokenData->getIpAddress()); + $this->assertEquals('user-id', $authTokenData->getUserId()); + $this->assertEquals('portal-id', $authTokenData->getPortalId()); + $this->assertTrue($authTokenData->toCreateSecret()); + } +}