37 self::TYPE_HOTP =>
'\Bitrix\Main\Security\Mfa\HotpAlgorithm',
38 self::TYPE_TOTP =>
'\Bitrix\Main\Security\Mfa\TotpAlgorithm',
69 if ($algorithm ===
null)
71 $this->
setType(static::getDefaultType());
75 $this->algorithmClass = $algorithm;
95 'filter' => array(
'=USER_ID' =>
$userId),
96 'select' => array(
'ACTIVE',
'USER_ID',
'SECRET',
'INIT_PARAMS',
'PARAMS',
'TYPE',
'ATTEMPTS',
'INITIAL_DATE',
'SKIP_MANDATORY',
'DEACTIVATE_UNTIL',
'USER_ACTIVE' =>
'USER.ACTIVE')
99 $userInfo = $userInfo->fetch();
104 $instance =
new static;
106 $instance->setActive(
false);
111 $userInfo[
'SECRET'] = pack(
'H*', $userInfo[
'SECRET']);
112 $userInfo[
'ACTIVE'] = ($userInfo[
'ACTIVE'] ===
'Y');
113 $userInfo[
'USER_ACTIVE'] = ($userInfo[
'USER_ACTIVE'] ===
'Y');
114 $userInfo[
'SKIP_MANDATORY'] = $userInfo[
'SKIP_MANDATORY'] ===
'Y';
116 $instance = static::getByType(
$type);
117 $instance->setUserInfo($userInfo);
132 if (!in_array(
$type, static::$availableTypes))
135 $algo = static::$typeMap[
$type];
136 $instance =
new static($algo);
137 $instance->setType(
$type);
150 if (!in_array(
$type, static::$availableTypes))
153 $this->algorithmClass = static::$typeMap[
$type];
196 public function getAlgorithm()
199 $algorithm =
new $this->algorithmClass($this->
getInitParams());
200 $algorithm->setSecret($this->
getSecret());
215 $opts += array(
'issuer' =>
$issuer);
235 $newSecret = Random::getBytes(static::SECRET_LENGTH);
238 $this->regenerated =
true;
240 ->setType(static::getDefaultType())
242 ->setSkipMandatory(
false)
244 ->setDeactivateUntil(
null)
246 ->setSecret($newSecret)
258 public function verify($input, $updateParams =
true)
260 [$result, $newParams] = $this->getAlgorithm()->verify($input, $this->
getParams());
264 && $newParams !==
null
269 ->setParams($newParams)
302 return $this->getAlgorithm()->getSyncParameters((
string) $inputA, (
string) $inputB);
319 elseif (!preg_match(
'/^\d{6}$/D', $inputA))
320 throw new OtpException(getMessage(
'SECURITY_OTP_ERROR_PASS1_INVALID'));
322 if ($this->getAlgorithm()->isTwoCodeRequired())
326 elseif (!preg_match(
'/^\d{6}$/D', $inputB))
364 if ($this->regenerated)
367 throw new OtpException(
'Missing OTP params, forgot to call syncParameters?');
387 return $result->isSuccess();
395 public function delete()
412 throw new OtpException(
'OTP not initialized, if your activate it - user can\'t login anymore. Do you forgot to call regenerate?');
416 ->setDeactivateUntil(
null)
432 throw new OtpException(
'Otp not activated. Do your mean deffer?');
443 $deactivateDate = Type\DateTime::createFromTimestamp(time() + $days * 86400);
462 throw new OtpException(
'Otp already activated. Do your mean deactivate?');
471 $deactivateDate = Type\DateTime::createFromTimestamp(time() + $days * 86400);
497 ->setActive($userInfo[
'ACTIVE'])
498 ->setUserActive($userInfo[
'USER_ACTIVE'])
499 ->setUserId($userInfo[
'USER_ID'])
500 ->setAttempts($userInfo[
'ATTEMPTS'])
501 ->setSecret($userInfo[
'SECRET'])
502 ->setInitParams($userInfo[
'INIT_PARAMS'])
503 ->setParams($userInfo[
'PARAMS'])
504 ->setSkipMandatory($userInfo[
'SKIP_MANDATORY'])
509 if ($userInfo[
'INITIAL_DATE'])
512 if ($userInfo[
'DEACTIVATE_UNTIL'])
526 $this->initialDate = $date;
549 $this->deactivateUntil = $date;
570 $this->skipMandatory = $isSkipped;
630 $this->active = $isActive;
647 $this->userActive = $isActive;
680 $this->attempts = $attemptsCount;
774 $secret = pack(
'H*', $hexValue);
800 if ($this->issuer ===
null)
827 if ($this->label ===
null)
852 if ($this->context ===
null)
878 $this->userLogin = $login;
891 if ($this->userLogin ===
null && $this->userId)
893 $this->userLogin = \Bitrix\Main\UserTable::query()
899 $this->userLogin = $this->userLogin[
'LOGIN'];
912 $host = Option::get(
'main',
'server_name');
915 return preg_replace(
'#:\d+$#D',
'', $host);
919 return Option::get(
'security',
'otp_issuer',
'Bitrix');
947 return (
int) $this->
getPolicy()->getLoginAttempts();
960 return ((
int) $this->
getPolicy()->getStoreTimeout()) * 60;
971 return '255.255.255.255';
973 return $this->
getPolicy()->getStoreIpMask();
1006 $targetRights = static::getMandatoryRights();
1007 $userRights = \CAccess::getUserCodesArray($this->
getUserId());
1008 $existedRights = array_intersect($targetRights, $userRights);
1009 $result = empty($existedRights);
1021 if (Option::get(
'security',
'otp_allow_remember') !==
'Y')
1024 $signedValue = $this->
getContext()->getRequest()->getCookie(static::SKIP_COOKIE);
1026 if (!$signedValue || !is_string($signedValue))
1034 ->unsign($signedValue,
'MFA_SAVE');
1054 $userIp = $this->
getContext()->getRequest()->getRemoteAddress();
1055 return md5(ip2long($rememberMask) & ip2long($userIp));
1066 global $APPLICATION;
1070 $rememberLifetime += time();
1073 $signedValue = $signer
1075 ->sign($rememberValue, $rememberLifetime,
'MFA_SAVE');
1078 Option::get(
'main',
'use_secure_password_cookies',
'N') ===
'Y'
1079 && $this->
getContext()->getRequest()->isHttps()
1082 $APPLICATION->set_cookie(
1083 static::SKIP_COOKIE,
1114 if (!$this->userGroupPolicy)
1116 $this->userGroupPolicy = \CUser::getPolicy($this->
getUserId());
1129 $cache_dir =
'/otp/user_id/' . substr(md5($this->
getUserId()), -2) .
'/' . $this->
getUserId() .
'/';
1130 $cache = new \CPHPCache;
1131 $cache->CleanDir($cache_dir);
1145 global $APPLICATION;
1147 if (!static::isOtpEnabled())
1153 $otp = static::getByUser(
$params[
'USER_ID']);
1155 if (!$otp->isActivated())
1161 static::isMandatoryUsing()
1162 && !$otp->canSkipMandatory()
1166 if (!$otp->isDbRecordExists() && static::getSkipMandatoryDays())
1169 $otp->defer(static::getSkipMandatoryDays());
1172 static::setDeferredParams(
null);
1177 $params[static::REJECTED_KEY] = static::REJECT_BY_MANDATORY;
1178 static::setDeferredParams(
$params);
1184 if (!$otp->isUserActive())
1195 $isSuccess = $otp->canSkipByCookie();
1200 $isCaptchaChecked = (
1201 !$otp->isAttemptsReached()
1202 || $APPLICATION->captchaCheckCode(
$params[
'CAPTCHA_WORD'],
$params[
'CAPTCHA_SID'])
1204 $isRememberNeeded = (
1206 && Option::get(
'security',
'otp_allow_remember') ===
'Y'
1209 if (!$isCaptchaChecked && !$APPLICATION->NeedCAPTHA())
1212 $APPLICATION->SetNeedCAPTHA(
true);
1215 $isOtpPassword = (bool) preg_match(
'/^\d{6}$/D',
$params[
'OTP']);
1217 static::isRecoveryCodesEnabled()
1221 if ($isCaptchaChecked && ($isOtpPassword || $isRecoveryCode))
1224 $isSuccess = $otp->verify(
$params[
'OTP']);
1225 elseif ($isRecoveryCode)
1233 ->setAttempts($otp->getAttempts() + 1)
1238 if ($otp->getAttempts() > 0)
1246 if ($isRememberNeeded && $isOtpPassword)
1250 $otp->setSkipCookie();
1258 static::setDeferredParams(
null);
1263 $params[static::REJECTED_KEY] = static::REJECT_BY_CODE;
1264 static::setDeferredParams(
$params);
1267 static::sendEvent($otp);
1270 if(Option::get(
"security",
"otp_log") <>
"N")
1272 \CSecurityEvent::getInstance()->doLog(
"SECURITY",
"SECURITY_OTP", $otp->getUserId(),
"");
1282 $algo = $otp->getAlgorithm();
1285 if($algo instanceof \
Bitrix\Main\Security\Mfa\TotpAlgorithm)
1288 $timeCode = $algo->timecode(time());
1289 $code = $algo->generateOTP($timeCode);
1297 $event = new \Bitrix\Main\Event(
"security",
"onOtpRequired", $eventParams);
1308 return static::getDeferredParams() !==
null;
1318 $params = static::getDeferredParams();
1321 || !isset(
$params[static::REJECTED_KEY])
1327 return $params[static::REJECTED_KEY] === static::REJECT_BY_MANDATORY;
1337 $params = static::getDeferredParams();
1342 $otp = static::getByUser(
$params[
'USER_ID']);
1343 return $otp && $otp->isAttemptsReached();
1354 if (isset($kernelSession[
'BX_SECURITY_OTP']) && is_array($kernelSession[
'BX_SECURITY_OTP']))
1356 return $kernelSession[
'BX_SECURITY_OTP'];
1373 unset($kernelSession[
'BX_SECURITY_OTP']);
1379 if (isset(
$params[
'PASSWORD']))
1382 $kernelSession[
'BX_SECURITY_OTP'] =
$params;
1394 Option::set(
'security',
'otp_mandatory_skip_days', (
int) $days,
null);
1404 return (
int) Option::get(
'security',
'otp_mandatory_skip_days');
1415 Option::set(
'security',
'otp_mandatory_using', $isMandatory?
'Y':
'N',
null);
1425 return (Option::get(
'security',
'otp_mandatory_using') ===
'Y');
1436 Option::set(
'security',
'otp_mandatory_rights', serialize($rights),
null);
1446 $targetRights = Option::get(
'security',
'otp_mandatory_rights');
1447 $targetRights = unserialize($targetRights, [
'allowed_classes' =>
false]);
1448 if (!is_array($targetRights))
1449 $targetRights = array();
1451 return $targetRights;
1463 if (!in_array($value, static::$availableTypes))
1466 Option::set(
'security',
'otp_default_algo', $value,
null);
1477 return Option::get(
'security',
'otp_default_algo');
1487 return static::$availableTypes;
1498 self::TYPE_HOTP => array(
1499 'type' => self::TYPE_HOTP,
1501 'required_two_code' =>
true,
1503 self::TYPE_TOTP => array(
1504 'type' => self::TYPE_TOTP,
1506 'required_two_code' =>
false,
1518 return (Option::get(
'security',
'otp_enabled') ===
'Y');
1528 return (Option::get(
'security',
'otp_allow_recovery_codes') ===
'Y');
static loadMessages($file)
static getMessage($code, $replace=null, $language=null)
static getList(array $parameters=array())
static update($primary, array $data)
static decode($base32String)
static isCaptchaRequired()
regenerate($newSecret=null)
getProvisioningUri(array $opts=array())
static setMandatoryRights(array $rights)
static isRecoveryCodesEnabled()
canSkipMandatoryByRights()
static sendEvent(Otp $otp)
const REJECT_BY_MANDATORY
__construct($algorithm=null)
setSkipMandatory($isSkipped=true)
static getAvailableTypes()
static isOtpRequiredByMandatory()
getSyncParameters($inputA, $inputB)
setInitParams(array $params)
syncParameters($inputA, $inputB=null)
static getTypesDescription()
static getByUser($userId)
static getMandatoryRights()
setDeactivateUntil($date)
static isMandatoryUsing()
setContext(\Bitrix\Main\Context $context)
setAttempts($attemptsCount)
verify($input, $updateParams=true)
static verifyUser(array $params)
static setDeferredParams($params)
static setDefaultType($value)
setInitialDate(Type\DateTime $date)
const TAGGED_CACHE_TEMPLATE
setUserInfo(array $userInfo)
static getSkipMandatoryDays()
static getDeferredParams()
generateLabel($issuer=null)
static setMandatoryUsing($isMandatory=true)
static setSkipMandatoryDays($days=2)
static useCode($userId, $searchCode)
static clearByUser($userId)