• Модуль: security
  • Путь к файлу: ~/bitrix/modules/security/lib/mfa/otp.php
  • Класс: BitrixSecurityMfaOtp
  • Вызов: Otp::verifyUser
static function verifyUser(array $params)
{
	global $APPLICATION;

	if (!static::isOtpEnabled()) // OTP disabled in settings
		return true;

	$isSuccess = false;

	// ToDo: review and refactoring needed
	$otp = static::getByUser($params['USER_ID']);

	if (!$otp->isActivated())
	{
		// User does not use OTP
		$isSuccess = true;

		if (
			static::isMandatoryUsing()
			&& !$otp->canSkipMandatory()
		)
		{
			// Grace full period ends. We must reject authorization and defer reject reason
			if (!$otp->isDbRecordExists() && static::getSkipMandatoryDays())
			{
				// If mandatory enabled and user never use OTP - let's deffer initialization
				$otp->defer(static::getSkipMandatoryDays());

				// We forgive the user for the first time
				static::setDeferredParams(null);
				return true;
			}

			// Save a flag which indicates that a OTP is required, but user doesn't use it :-(
			$params[static::REJECTED_KEY] = static::REJECT_BY_MANDATORY;
			static::setDeferredParams($params);
			return false;
		}
	}
	else
	{
		if (!$otp->isUserActive())
		{
			//non-active user can't login by OTP
			return false;
		}
	}


	if (!$isSuccess)
	{
		// User skip OTP on this browser by cookie
		$isSuccess = $otp->canSkipByCookie();
	}

	if (!$isSuccess)
	{
		$isCaptchaChecked = (
			!$otp->isAttemptsReached()
			|| $APPLICATION->captchaCheckCode($params['CAPTCHA_WORD'], $params['CAPTCHA_SID'])
		);
		$isRememberNeeded = (
			$params['OTP_REMEMBER']
			&& Option::get('security', 'otp_allow_remember') === 'Y'
		);

		if (!$isCaptchaChecked && !$APPLICATION->NeedCAPTHA())
		{
			// Backward compatibility with old login page
			$APPLICATION->SetNeedCAPTHA(true);
		}

		$isOtpPassword = (bool) preg_match('/^d{6}$/D', $params['OTP']);
		$isRecoveryCode = (
			static::isRecoveryCodesEnabled()
			&& (bool) preg_match(RecoveryCodesTable::CODE_PATTERN, $params['OTP'])
		);

		if ($isCaptchaChecked && ($isOtpPassword || $isRecoveryCode))
		{
			if ($isOtpPassword)
				$isSuccess = $otp->verify($params['OTP'], true);
			elseif ($isRecoveryCode)
				$isSuccess = RecoveryCodesTable::useCode($otp->getUserId(), $params['OTP']);
			else
				$isSuccess = false;

			if (!$isSuccess)
			{
				$otp
					->setAttempts($otp->getAttempts() + 1)
					->save();
			}
			else
			{
				if ($otp->getAttempts() > 0)
				{
					// Clear OTP input attempts
					$otp
						->setAttempts(0)
						->save();
				}

				if ($isRememberNeeded && $isOtpPassword)
				{
					// If user provide otp password (not recovery codes)
					// Sets cookie for bypass OTP checking
					$otp->setSkipCookie();
				}
			}
		}
	}

	if ($isSuccess)
	{
		static::setDeferredParams(null);
	}
	else
	{
		// Save a flag which indicates that a form for OTP is required
		$params[static::REJECTED_KEY] = static::REJECT_BY_CODE;
		static::setDeferredParams($params);

		//the OTP form will be shown on the next hit, send the event
		static::sendEvent($otp);

		//write to the log ("on" by default)
		if(Option::get("security", "otp_log") <> "N")
		{
			CSecurityEvent::getInstance()->doLog("SECURITY", "SECURITY_OTP", $otp->getUserId(), "");
		}
	}

	return $isSuccess;
}