mirror of
https://github.com/espocrm/espocrm.git
synced 2026-06-28 06:56:05 +00:00
2fa code attempts period separate param
This commit is contained in:
@@ -37,6 +37,7 @@ use Espo\Core\Utils\Metadata;
|
||||
class ConfigDataProvider
|
||||
{
|
||||
private const FAILED_ATTEMPTS_PERIOD = '60 seconds';
|
||||
private const FAILED_CODE_ATTEMPTS_PERIOD = '5 minutes';
|
||||
private const MAX_FAILED_ATTEMPT_NUMBER = 10;
|
||||
|
||||
public function __construct(private Config $config, private Metadata $metadata)
|
||||
@@ -50,6 +51,14 @@ class ConfigDataProvider
|
||||
return $this->config->get('authFailedAttemptsPeriod', self::FAILED_ATTEMPTS_PERIOD);
|
||||
}
|
||||
|
||||
/**
|
||||
* A period for max failed 2FA code attempts checking.
|
||||
*/
|
||||
public function getFailedCodeAttemptsPeriod(): string
|
||||
{
|
||||
return $this->config->get('authFailedCodeAttemptsPeriod', self::FAILED_CODE_ATTEMPTS_PERIOD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Max failed log in attempts.
|
||||
*/
|
||||
|
||||
@@ -61,32 +61,20 @@ class FailedAttemptsLimit implements BeforeLogin
|
||||
{
|
||||
$isByTokenOnly = !$data->getMethod() && $request->getHeader(HeaderKey::AUTHORIZATION_BY_TOKEN) === 'true';
|
||||
|
||||
if ($isByTokenOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->configDataProvider->isAuthLogDisabled()) {
|
||||
if ($isByTokenOnly || $this->configDataProvider->isAuthLogDisabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$isSecondStep = $request->getHeader(HeaderKey::AUTHORIZATION_CODE) !== null;
|
||||
|
||||
$failedAttemptsPeriod = $this->configDataProvider->getFailedAttemptsPeriod();
|
||||
$maxFailedAttempts = $this->configDataProvider->getMaxFailedAttemptNumber();
|
||||
|
||||
$requestTime = intval($request->getServerParam('REQUEST_TIME_FLOAT'));
|
||||
|
||||
try {
|
||||
$requestTimeFrom = (new DateTime('@' . $requestTime))->modify('-' . $failedAttemptsPeriod);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
throw new RuntimeException($e->getMessage());
|
||||
}
|
||||
$failedAttemptsPeriod = $isSecondStep ?
|
||||
$this->configDataProvider->getFailedCodeAttemptsPeriod() :
|
||||
$this->configDataProvider->getFailedAttemptsPeriod();
|
||||
|
||||
$ipAddress = $this->util->obtainIpFromRequest($request);
|
||||
|
||||
$where = [
|
||||
'requestTime>' => $requestTimeFrom->format('U'),
|
||||
'requestTime>' => $this->getTimeFrom($request, $failedAttemptsPeriod)->format('U'),
|
||||
'isDenied' => true,
|
||||
];
|
||||
|
||||
@@ -113,7 +101,7 @@ class FailedAttemptsLimit implements BeforeLogin
|
||||
->where($where)
|
||||
->count();
|
||||
|
||||
if ($failAttemptCount <= $maxFailedAttempts) {
|
||||
if ($failAttemptCount <= $this->configDataProvider->getMaxFailedAttemptNumber()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -125,4 +113,18 @@ class FailedAttemptsLimit implements BeforeLogin
|
||||
|
||||
throw new Forbidden("Max failed login attempts exceeded for IP address $ipAddress.");
|
||||
}
|
||||
|
||||
private function getTimeFrom(Request $request, string $failedAttemptsPeriod): DateTime
|
||||
{
|
||||
$requestTime = intval($request->getServerParam('REQUEST_TIME_FLOAT'));
|
||||
|
||||
try {
|
||||
$requestTimeFrom = (new DateTime('@' . $requestTime))->modify('-' . $failedAttemptsPeriod);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
throw new RuntimeException($e->getMessage());
|
||||
}
|
||||
|
||||
return $requestTimeFrom;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +104,7 @@ return [
|
||||
'authLogDisabled',
|
||||
'authApiUserLogDisabled',
|
||||
'authFailedAttemptsPeriod',
|
||||
'authFailedCodeAttemptsPeriod',
|
||||
'authMaxFailedAttemptNumber',
|
||||
'ipAddressServerParam',
|
||||
'jobNoTableLocking',
|
||||
|
||||
Reference in New Issue
Block a user