auth hooks
This commit is contained in:
@@ -31,6 +31,7 @@ namespace Espo\Core\Api;
|
||||
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\ServiceUnavailable;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
use Espo\Core\Api\Request;
|
||||
use Espo\Core\Api\Response;
|
||||
@@ -202,7 +203,7 @@ class Auth
|
||||
if (
|
||||
$e instanceof BadRequest ||
|
||||
$e instanceof ServiceUnavailable ||
|
||||
$e instanceof BadRequest
|
||||
$e instanceof Forbidden
|
||||
) {
|
||||
$reason = $e->getMessage();
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ use Espo\Core\Authentication\{
|
||||
AuthToken\AuthTokenManager,
|
||||
AuthToken\AuthTokenData,
|
||||
AuthToken\AuthToken,
|
||||
Hook\Manager as HookManager,
|
||||
};
|
||||
|
||||
use Espo\Core\{
|
||||
@@ -59,13 +60,13 @@ use Espo\Core\{
|
||||
Utils\Log,
|
||||
};
|
||||
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* Handles authentication. The entry point of the auth process.
|
||||
*/
|
||||
class Authentication
|
||||
{
|
||||
private const LOGOUT_USERNAME = '**logout';
|
||||
|
||||
private $allowAnyAccess;
|
||||
|
||||
private $portal;
|
||||
@@ -82,6 +83,8 @@ class Authentication
|
||||
|
||||
private $auth2FAFactory;
|
||||
|
||||
private $hookManager;
|
||||
|
||||
private $log;
|
||||
|
||||
public function __construct(
|
||||
@@ -92,6 +95,7 @@ class Authentication
|
||||
LoginFactory $authLoginFactory,
|
||||
TwoFAFactory $auth2FAFactory,
|
||||
AuthTokenManager $authTokenManager,
|
||||
HookManager $hookManager,
|
||||
Log $log,
|
||||
bool $allowAnyAccess = false
|
||||
) {
|
||||
@@ -99,12 +103,12 @@ class Authentication
|
||||
|
||||
$this->applicationUser = $applicationUser;
|
||||
$this->applicationState = $applicationState;
|
||||
|
||||
$this->configDataProvider = $configDataProvider;
|
||||
$this->entityManager = $entityManagerProxy;
|
||||
$this->authLoginFactory = $authLoginFactory;
|
||||
$this->auth2FAFactory = $auth2FAFactory;
|
||||
$this->authTokenManager = $authTokenManager;
|
||||
$this->hookManager = $hookManager;
|
||||
$this->log = $log;
|
||||
}
|
||||
|
||||
@@ -130,24 +134,23 @@ class Authentication
|
||||
"AUTH: Trying to use not allowed authentication method '{$authenticationMethod}'."
|
||||
);
|
||||
|
||||
return Result::fail('Not allowed authentication method');
|
||||
return $this->processFail(
|
||||
Result::fail(FailReason::METHOD_NOT_ALLOWED),
|
||||
$data,
|
||||
$request
|
||||
);
|
||||
}
|
||||
|
||||
$isByTokenOnly = !$authenticationMethod && $request->getHeader('Espo-Authorization-By-Token') === 'true';
|
||||
|
||||
if (!$isByTokenOnly) {
|
||||
$this->checkFailedAttemptsLimit($request);
|
||||
}
|
||||
|
||||
$authToken = null;
|
||||
$authTokenIsFound = false;
|
||||
$this->hookManager->processBeforeLogin($data, $request);
|
||||
|
||||
if (!$authenticationMethod && $password === null) {
|
||||
$this->log->error("AUTH: Trying to login w/o password.");
|
||||
|
||||
return Result::fail('No password');
|
||||
return Result::fail(FailReason::NO_PASSWORD);
|
||||
}
|
||||
|
||||
$authToken = null;
|
||||
|
||||
if (!$authenticationMethod) {
|
||||
$authToken = $this->authTokenManager->get($password);
|
||||
}
|
||||
@@ -160,9 +163,7 @@ class Authentication
|
||||
}
|
||||
}
|
||||
|
||||
if ($authToken) {
|
||||
$authTokenIsFound = true;
|
||||
}
|
||||
$authTokenIsFound = $authToken !== null;
|
||||
|
||||
if ($authToken && !$authToken->isActive()) {
|
||||
$authToken = null;
|
||||
@@ -172,10 +173,12 @@ class Authentication
|
||||
$authTokenCheckResult = $this->processAuthTokenCheck($authToken);
|
||||
|
||||
if (!$authTokenCheckResult) {
|
||||
return Result::fail('Denied');
|
||||
return Result::fail(FailReason::DENIED);
|
||||
}
|
||||
}
|
||||
|
||||
$isByTokenOnly = !$authenticationMethod && $request->getHeader('Espo-Authorization-By-Token') === 'true';
|
||||
|
||||
if ($isByTokenOnly && !$authToken) {
|
||||
if ($username) {
|
||||
$this->log->info(
|
||||
@@ -183,7 +186,11 @@ class Authentication
|
||||
);
|
||||
}
|
||||
|
||||
return Result::fail('Token not found');
|
||||
return $this->processFail(
|
||||
Result::fail(FailReason::TOKEN_NOT_FOUND),
|
||||
$data,
|
||||
$request
|
||||
);
|
||||
}
|
||||
|
||||
if (!$authenticationMethod) {
|
||||
@@ -210,11 +217,20 @@ class Authentication
|
||||
}
|
||||
|
||||
if ($result->isFail()) {
|
||||
return $result;
|
||||
return $this->processFail(
|
||||
$result,
|
||||
$data,
|
||||
$request
|
||||
);
|
||||
}
|
||||
|
||||
if (!$user) {
|
||||
return Result::fail();
|
||||
// Supposed not to ever happen.
|
||||
return $this->processFail(
|
||||
Result::fail(FailReason::USER_NOT_FOUND),
|
||||
$data,
|
||||
$request
|
||||
);
|
||||
}
|
||||
|
||||
if (!$user->isAdmin() && $this->configDataProvider->isMaintenanceMode()) {
|
||||
@@ -222,7 +238,11 @@ class Authentication
|
||||
}
|
||||
|
||||
if (!$this->processUserCheck($user, $authLogRecord)) {
|
||||
return Result::fail('Denied');
|
||||
return $this->processFail(
|
||||
Result::fail(FailReason::DENIED),
|
||||
$data,
|
||||
$request
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->isPortal()) {
|
||||
@@ -245,7 +265,11 @@ class Authentication
|
||||
$result = $this->processTwoFactor($result, $request);
|
||||
|
||||
if ($result->isFail()) {
|
||||
return $result;
|
||||
return $this->processFail(
|
||||
$result,
|
||||
$data,
|
||||
$request
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,7 +431,7 @@ class Authentication
|
||||
|
||||
if ($code) {
|
||||
if (!$impl->verifyCode($loggedUser, $code)) {
|
||||
return Result::fail('Code not verified');
|
||||
return Result::fail(FailReason::CODE_NOT_VERIFIED);
|
||||
}
|
||||
|
||||
return $result;
|
||||
@@ -441,47 +465,6 @@ class Authentication
|
||||
return $method;
|
||||
}
|
||||
|
||||
private function checkFailedAttemptsLimit(Request $request): void
|
||||
{
|
||||
$failedAttemptsPeriod = $this->configDataProvider->getFailedAttemptsPeriod();
|
||||
$maxFailedAttempts = $this->configDataProvider->getMaxFailedAttemptNumber();
|
||||
|
||||
$requestTime = intval($request->getServerParam('REQUEST_TIME_FLOAT'));
|
||||
|
||||
$requestTimeFrom = (new DateTime('@' . $requestTime))->modify('-' . $failedAttemptsPeriod);
|
||||
|
||||
$failAttemptCount = 0;
|
||||
|
||||
$ip = $request->getServerParam('REMOTE_ADDR');
|
||||
|
||||
$where = [
|
||||
'requestTime>' => $requestTimeFrom->format('U'),
|
||||
'ipAddress' => $ip,
|
||||
'isDenied' => true,
|
||||
];
|
||||
|
||||
$wasFailed = (bool) $this->entityManager
|
||||
->getRDBRepository('AuthLogRecord')
|
||||
->select(['id'])
|
||||
->where($where)
|
||||
->findOne();
|
||||
|
||||
if ($wasFailed) {
|
||||
$failAttemptCount = $this->entityManager
|
||||
->getRDBRepository('AuthLogRecord')
|
||||
->where($where)
|
||||
->count();
|
||||
}
|
||||
|
||||
if ($failAttemptCount > $maxFailedAttempts) {
|
||||
$this->log->warning(
|
||||
"AUTH: Max failed login attempts exceeded for IP '{$ip}'."
|
||||
);
|
||||
|
||||
throw new Forbidden("Max failed login attempts exceeded.");
|
||||
}
|
||||
}
|
||||
|
||||
private function createAuthToken(User $user, Request $request, Response $response): AuthToken
|
||||
{
|
||||
$createSecret = $request->getHeader('Espo-Authorization-Create-Token-Secret') === 'true';
|
||||
@@ -560,7 +543,7 @@ class Authentication
|
||||
?string $authenticationMethod = null
|
||||
): ?AuthLogRecord {
|
||||
|
||||
if ($username === '**logout') {
|
||||
if ($username === self::LOGOUT_USERNAME) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -627,4 +610,11 @@ class Authentication
|
||||
|
||||
$response->addHeader('Set-Cookie', $headerValue);
|
||||
}
|
||||
|
||||
private function processFail(Result $result, AuthenticationData $data, Request $request): Result
|
||||
{
|
||||
$this->hookManager->processOnFail($result, $data, $request);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM 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.
|
||||
*
|
||||
* EspoCRM 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 EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* 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\Core\Authentication;
|
||||
|
||||
class FailReason
|
||||
{
|
||||
public const DENIED = 'Denied';
|
||||
|
||||
public const CODE_NOT_VERIFIED = 'Code not verified';
|
||||
|
||||
public const NO_PASSWORD = 'No password';
|
||||
|
||||
public const TOKEN_NOT_FOUND = 'Token not found';
|
||||
|
||||
public const USER_NOT_FOUND = 'User not found';
|
||||
|
||||
public const WRONG_CREDENTIALS = 'Wrong credentials';
|
||||
|
||||
public const USER_TOKEN_MISMATCH = 'User and token mismatch';
|
||||
|
||||
public const HASH_NOT_MATCHED = 'Hash not matched';
|
||||
|
||||
public const METHOD_NOT_ALLOWED = 'Not allowed authentication method';
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM 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.
|
||||
*
|
||||
* EspoCRM 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 EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* 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\Core\Authentication\Hook;
|
||||
|
||||
use Espo\Core\Authentication\AuthenticationData;
|
||||
use Espo\Core\Api\Request;
|
||||
|
||||
/**
|
||||
* Before logging in, before credentials are checked.
|
||||
*
|
||||
* @throws Forbidden
|
||||
* @throws ServiceUnavailable
|
||||
*/
|
||||
interface BeforeLogin
|
||||
{
|
||||
public function process(AuthenticationData $data, Request $request): void;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM 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.
|
||||
*
|
||||
* EspoCRM 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 EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* 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\Core\Authentication\Hook\Hooks;
|
||||
|
||||
use Espo\Core\Authentication\Hook\BeforeLogin;
|
||||
use Espo\Core\Authentication\AuthenticationData;
|
||||
use Espo\Core\Api\Request;
|
||||
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Authentication\ConfigDataProvider;
|
||||
use Espo\ORM\EntityManager;
|
||||
use Espo\Core\Utils\Log;
|
||||
|
||||
use Espo\Entities\AuthLogRecord;
|
||||
|
||||
use DateTime;
|
||||
|
||||
class FailedAttemptsLimit implements BeforeLogin
|
||||
{
|
||||
private $configDataProvider;
|
||||
|
||||
private $entityManager;
|
||||
|
||||
private $log;
|
||||
|
||||
public function __construct(ConfigDataProvider $configDataProvider, EntityManager $entityManager, Log $log)
|
||||
{
|
||||
$this->configDataProvider = $configDataProvider;
|
||||
$this->entityManager = $entityManager;
|
||||
$this->log = $log;
|
||||
}
|
||||
|
||||
public function process(AuthenticationData $data, Request $request): void
|
||||
{
|
||||
$isByTokenOnly = !$data->getMethod() && $request->getHeader('Espo-Authorization-By-Token') === 'true';
|
||||
|
||||
if ($isByTokenOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
$failedAttemptsPeriod = $this->configDataProvider->getFailedAttemptsPeriod();
|
||||
$maxFailedAttempts = $this->configDataProvider->getMaxFailedAttemptNumber();
|
||||
|
||||
$requestTime = intval($request->getServerParam('REQUEST_TIME_FLOAT'));
|
||||
|
||||
$requestTimeFrom = (new DateTime('@' . $requestTime))->modify('-' . $failedAttemptsPeriod);
|
||||
|
||||
$ip = $request->getServerParam('REMOTE_ADDR');
|
||||
|
||||
$where = [
|
||||
'requestTime>' => $requestTimeFrom->format('U'),
|
||||
'ipAddress' => $ip,
|
||||
'isDenied' => true,
|
||||
];
|
||||
|
||||
$wasFailed = (bool) $this->entityManager
|
||||
->getRDBRepository(AuthLogRecord::ENTITY_TYPE)
|
||||
->select(['id'])
|
||||
->where($where)
|
||||
->findOne();
|
||||
|
||||
if (!$wasFailed) {
|
||||
return;
|
||||
}
|
||||
|
||||
$failAttemptCount = $this->entityManager
|
||||
->getRDBRepository(AuthLogRecord::ENTITY_TYPE)
|
||||
->where($where)
|
||||
->count();
|
||||
|
||||
if ($failAttemptCount <= $maxFailedAttempts) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->log->warning("AUTH: Max failed login attempts exceeded for IP '{$ip}'.");
|
||||
|
||||
throw new Forbidden("Max failed login attempts exceeded.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM 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.
|
||||
*
|
||||
* EspoCRM 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 EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* 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\Core\Authentication\Hook;
|
||||
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\Core\InjectableFactory;
|
||||
|
||||
use Espo\Core\Authentication\AuthenticationData;
|
||||
use Espo\Core\Api\Request;
|
||||
use Espo\Core\Authentication\Result;
|
||||
|
||||
class Manager
|
||||
{
|
||||
private $metadata;
|
||||
|
||||
private $injectableFactory;
|
||||
|
||||
public function __construct(Metadata $metadata, InjectableFactory $injectableFactory)
|
||||
{
|
||||
$this->metadata = $metadata;
|
||||
$this->injectableFactory = $injectableFactory;
|
||||
}
|
||||
|
||||
public function processBeforeLogin(AuthenticationData $data, Request $request): void
|
||||
{
|
||||
foreach ($this->getBeforeLoginHookList() as $hook) {
|
||||
$hook->process($data, $request);
|
||||
}
|
||||
}
|
||||
|
||||
public function processOnFail(Result $result, AuthenticationData $data, Request $request): void
|
||||
{
|
||||
foreach ($this->getOnFailHookList() as $hook) {
|
||||
$hook->process($result, $data, $request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function getHookClassNameList(string $type): array
|
||||
{
|
||||
$key = $type . 'HookClassNameList';
|
||||
|
||||
return $this->metadata->get(['app', 'authentication', $key]) ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BeforeLogin[]
|
||||
*/
|
||||
private function getBeforeLoginHookList(): array
|
||||
{
|
||||
$list = [];
|
||||
|
||||
foreach ($this->getHookClassNameList('beforeLogin') as $className) {
|
||||
$list[] = $this->injectableFactory->create($className);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OnFail[]
|
||||
*/
|
||||
private function getOnFailHookList(): array
|
||||
{
|
||||
$list = [];
|
||||
|
||||
foreach ($this->getHookClassNameList('onFail') as $className) {
|
||||
$list[] = $this->injectableFactory->create($className);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM 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.
|
||||
*
|
||||
* EspoCRM 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 EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* 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\Core\Authentication\Hook;
|
||||
|
||||
use Espo\Core\Authentication\AuthenticationData;
|
||||
use Espo\Core\Api\Request;
|
||||
use Espo\Core\Authentication\Result;
|
||||
|
||||
/**
|
||||
* Once logging in is failed. Not fired when an exception is thrown.
|
||||
*/
|
||||
interface OnFail
|
||||
{
|
||||
public function process(Result $result, AuthenticationData $data, Request $request): void;
|
||||
}
|
||||
@@ -35,6 +35,7 @@ use Espo\Core\{
|
||||
Authentication\LoginData,
|
||||
Authentication\Result,
|
||||
Authentication\Helpers\UserFinder,
|
||||
Authentication\FailReason,
|
||||
};
|
||||
|
||||
class ApiKey implements Login
|
||||
@@ -53,7 +54,7 @@ class ApiKey implements Login
|
||||
$user = $this->userFinder->findApiApiKey($apiKey);
|
||||
|
||||
if (!$user) {
|
||||
return Result::fail('User not found');
|
||||
return Result::fail(FailReason::WRONG_CREDENTIALS);
|
||||
}
|
||||
|
||||
return Result::success($user);
|
||||
|
||||
@@ -36,6 +36,7 @@ use Espo\Core\{
|
||||
Authentication\LoginData,
|
||||
Authentication\Result,
|
||||
Authentication\Helpers\UserFinder,
|
||||
Authentication\FailReason,
|
||||
};
|
||||
|
||||
class Espo implements Login
|
||||
@@ -57,7 +58,7 @@ class Espo implements Login
|
||||
$authToken = $loginData->getAuthToken();
|
||||
|
||||
if (!$password) {
|
||||
return Result::fail('Empty password');
|
||||
return Result::fail(FailReason::NO_PASSWORD);
|
||||
}
|
||||
|
||||
$hash = $authToken ?
|
||||
@@ -67,11 +68,11 @@ class Espo implements Login
|
||||
$user = $this->userFinder->find($username, $hash);
|
||||
|
||||
if (!$user) {
|
||||
return Result::fail('Wrong credentials');
|
||||
return Result::fail(FailReason::WRONG_CREDENTIALS);
|
||||
}
|
||||
|
||||
if ($authToken && $user->id !== $authToken->getUserId()) {
|
||||
return Result::fail('User and token mismatch');
|
||||
return Result::fail(FailReason::USER_TOKEN_MISMATCH);
|
||||
}
|
||||
|
||||
return Result::success($user);
|
||||
|
||||
@@ -37,6 +37,7 @@ use Espo\Core\{
|
||||
Authentication\Result,
|
||||
Authentication\Helpers\UserFinder,
|
||||
Exceptions\Error,
|
||||
Authentication\FailReason,
|
||||
};
|
||||
|
||||
class Hmac implements Login
|
||||
@@ -60,13 +61,13 @@ class Hmac implements Login
|
||||
$user = $this->userFinder->findApiHmac($apiKey);
|
||||
|
||||
if (!$user) {
|
||||
return Result::fail('User not found');
|
||||
return Result::fail(FailReason::WRONG_CREDENTIALS);
|
||||
}
|
||||
|
||||
$secretKey = $this->apiKeyUtil->getSecretKeyForUserId($user->id);
|
||||
$secretKey = $this->apiKeyUtil->getSecretKeyForUserId($user->getId());
|
||||
|
||||
if (!$secretKey) {
|
||||
throw new Error("No secret key for API user '" . $user->id . "'.");
|
||||
throw new Error("No secret key for API user '" . $user->getId() . "'.");
|
||||
}
|
||||
|
||||
$string = $request->getMethod() . ' ' . $request->getResourcePath();
|
||||
@@ -75,6 +76,6 @@ class Hmac implements Login
|
||||
return Result::success($user);
|
||||
}
|
||||
|
||||
return Result::fail('Hash not matched');
|
||||
return Result::fail(FailReason::HASH_NOT_MATCHED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ use Espo\Core\{
|
||||
Authentication\LDAP\Utils as LDAPUtils,
|
||||
Authentication\LDAP\Client as LDAPClient,
|
||||
Authentication\AuthToken\AuthToken,
|
||||
Authentication\FailReason,
|
||||
};
|
||||
|
||||
use Exception;
|
||||
@@ -121,12 +122,12 @@ class LDAP implements Login
|
||||
return Result::success($user);
|
||||
}
|
||||
else {
|
||||
return Result::fail();
|
||||
return Result::fail(FailReason::WRONG_CREDENTIALS);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$password || $username == '**logout') {
|
||||
return Result::fail();
|
||||
return Result::fail(FailReason::NO_PASSWORD);
|
||||
}
|
||||
|
||||
if ($isPortal) {
|
||||
@@ -211,7 +212,7 @@ class LDAP implements Login
|
||||
"LDAP: Authentication success for user {$username}, but user is not created in EspoCRM."
|
||||
);
|
||||
|
||||
return Result::fail();
|
||||
return Result::fail(FailReason::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$userData = $ldapClient->getEntry($userDn);
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"beforeLoginHookClassNameList": [
|
||||
"Espo\\Core\\Authentication\\Hook\\Hooks\\FailedAttemptsLimit"
|
||||
],
|
||||
"onFailHookClassNameList": []
|
||||
}
|
||||
@@ -12,6 +12,7 @@
|
||||
["app", "templateHelpers"],
|
||||
["app", "appParams"],
|
||||
["app", "cleanup"],
|
||||
["app", "authentication"],
|
||||
["app", "pdfEngines", "__ANY__", "implementationClassNameMap"],
|
||||
["app", "addressFormats", "__ANY__", "formatterClassName"],
|
||||
["app", "auth2FAMethods", "__ANY__", "implementationClassName"],
|
||||
|
||||
Reference in New Issue
Block a user