Current Path : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/lib/authentication/ |
Current File : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/lib/authentication/shortcode.php |
<?php /** * Bitrix Framework * @package bitrix * @subpackage main * @copyright 2001-2020 Bitrix */ namespace Bitrix\Main\Authentication; use Bitrix\Main; use Bitrix\Main\Security\Mfa; use Bitrix\Main\Authentication\Internal\UserAuthCodeTable; class ShortCode { /** @var Context */ protected $context; protected $type; /** @var Internal\EO_UserAuthCode */ protected $code; protected $checkInterval = 300; //seconds, a half of the real time window protected $resendInterval = 60; //seconds /** * ShortCode constructor. * @param Context $context Contains userId * @param string $type Currently 'email' only */ public function __construct(Context $context, $type = UserAuthCodeTable::TYPE_EMAIL) { $this->context = $context; $this->type = $type; if(!$this->load()) { throw new Main\ObjectException("User probably not found: ".$context->getUserId()); } } /** * Generates a 6-number code. * @return bool|string */ public function generate() { $totp = new Mfa\TotpAlgorithm(); $totp->setInterval($this->checkInterval); $totp->setSecret($this->code->getOtpSecret()); $timecode = $totp->timecode(time()); $shortCode = $totp->generateOTP($timecode); return $shortCode; } /** * Verifies the 6-number code. * @param string $code * @return Main\Result */ public function verify($code) { $result = new Main\Result(); $attempts = (int)$this->code->getAttempts(); if($attempts >= 3) { $result->addError(new Main\Error("Retry count exceeded.", "ERR_RETRY_COUNT")); return $result; } $totp = new Main\Security\Mfa\TotpAlgorithm(); $totp->setInterval($this->checkInterval); $totp->setSecret($this->code->getOtpSecret()); $otpResult = false; try { list($otpResult, ) = $totp->verify($code); } catch(Main\ArgumentException $e) { } if($otpResult) { $this->code->setDateSent(null); $this->code->setDateResent(null); } else { $result->addError(new Main\Error("Incorrect code.", "ERR_CONFIRM_CODE")); $this->code->setAttempts($attempts + 1); } $this->code->save(); return $result; } /** * Checks if previous dispatch time is outside the interval. * @return Main\Result */ public function checkDateSent() { $result = new Main\Result(); $resultData = [ "checkInterval" => $this->checkInterval*2, "resendInterval" => $this->resendInterval, ]; //alowed only once in a interval if($this->code->getDateResent()) { $currentDateTime = new Main\Type\DateTime(); $interval = $currentDateTime->getTimestamp() - $this->code->getDateResent()->getTimestamp(); if($interval < $this->resendInterval) { $resultData["secondsLeft"] = $this->resendInterval - $interval; $resultData["secondsPassed"] = $interval; $result->addError(new Main\Error("Timeout not expired yet.")); } } $result->setData($resultData); return $result; } /** * Saves last sent date. * @return bool */ public function saveDateSent() { $currentDateTime = new Main\Type\DateTime(); if($this->code->getDateSent()) { if(($currentDateTime->getTimestamp() - $this->code->getDateSent()->getTimestamp()) > $this->checkInterval*2) { //reset attempts only for the new code (when time passes) $this->code->setAttempts(0); $this->code->setDateSent(null); } } if(!$this->code->getDateSent()) { //first time only $this->code->setDateSent($currentDateTime); } $this->code->setDateResent($currentDateTime); $this->code->save(); return true; } /** * @return Main\EO_User */ public function getUser() { return $this->code->fillUser(); } /** * @param int $userId */ public static function deleteByUser($userId) { UserAuthCodeTable::deleteByFilter(["=USER_ID" => $userId]); } protected function load() { $userId = $this->context->getUserId(); $primaryKey = [ "USER_ID" => $userId, "CODE_TYPE" => $this->type, ]; $code = UserAuthCodeTable::getById($primaryKey)->fetchObject(); if(!$code) { //first time for the user, should create a record $code = UserAuthCodeTable::createObject(); $code->setUserId($userId); $code->setCodeType($this->type); $result = $code->save(); if(!$result->isSuccess()) { return false; } } $this->code = $code; return true; } }