Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
otp.php
1<?php
2
4
17
18Loc::loadMessages(__FILE__);
19
20class Otp
21{
22 const TYPE_HOTP = 'hotp';
23 const TYPE_TOTP = 'totp';
25 const SECRET_LENGTH = 20; // Must be power of 5 for "nicely" App Secret view
26
27 const SKIP_COOKIE = 'OTPH';
28
29 const REJECTED_KEY = 'OTP_REJECT_REASON';
30 const REJECT_BY_CODE = 'code';
31 const REJECT_BY_MANDATORY = 'mandatory';
32
33 const TAGGED_CACHE_TEMPLATE = 'USER_OTP_%d';
34
35 protected static $availableTypes = array(self::TYPE_HOTP, self::TYPE_TOTP);
36 protected static $typeMap = array(
37 self::TYPE_HOTP => '\Bitrix\Main\Security\Mfa\HotpAlgorithm',
38 self::TYPE_TOTP => '\Bitrix\Main\Security\Mfa\TotpAlgorithm',
39 );
40 protected $algorithmClass = null;
41 protected array $initParams = [];
42 protected $regenerated = false;
43 /* @var \Bitrix\Main\Context $context */
44 protected $context = null;
45
46 protected $userId = null;
47 protected $userLogin = null;
48 /* @var Policy\RulesCollection*/
50 protected $active = null;
51 protected $userActive = null;
52 protected $secret = null;
53 protected $issuer = null;
54 protected $label = null;
55 protected $params = null;
56 protected $attempts = null;
57 protected $type = null;
59 protected $initialDate = null;
60 protected $skipMandatory = null;
62 protected $deactivateUntil = null;
63
67 public function __construct($algorithm = null)
68 {
69 if ($algorithm === null)
70 {
71 $this->setType(static::getDefaultType());
72 }
73 else
74 {
75 $this->algorithmClass = $algorithm;
76 }
77 }
78
87 public static function getByUser($userId)
88 {
89 $userId = (int) $userId;
90
91 if ($userId <= 0)
92 throw new ArgumentTypeException('userId', 'positive integer');
93
94 $userInfo = UserTable::getList(array(
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')
97 ));
98
99 $userInfo = $userInfo->fetch();
100
101 if (!$userInfo)
102 {
103 // OTP not available for this user
104 $instance = new static;
105 $instance->setUserId($userId);
106 $instance->setActive(false);
107 }
108 else
109 {
110 $type = $userInfo['TYPE'] ?: self::TYPE_DEFAULT;
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';
115
116 $instance = static::getByType($type);
117 $instance->setUserInfo($userInfo);
118 }
119
120 return $instance;
121 }
122
130 public static function getByType($type)
131 {
132 if (!in_array($type, static::$availableTypes))
133 throw new ArgumentOutOfRangeException('type', static::$availableTypes);
134
135 $algo = static::$typeMap[$type];
136 $instance = new static($algo);
137 $instance->setType($type);
138 return $instance;
139 }
140
148 public function setType($type)
149 {
150 if (!in_array($type, static::$availableTypes))
151 throw new ArgumentOutOfRangeException('type', static::$availableTypes);
152
153 $this->algorithmClass = static::$typeMap[$type];
154 $this->type = $type;
155
156 return $this;
157 }
158
165 public function setInitParams(array $params)
166 {
167 $this->initParams = $params;
168 return $this;
169 }
170
176 public function getInitParams(): array
177 {
178 return $this->initParams;
179 }
180
186 public function getType()
187 {
188 return $this->type;
189 }
190
196 public function getAlgorithm()
197 {
199 $algorithm = new $this->algorithmClass($this->getInitParams());
200 $algorithm->setSecret($this->getSecret());
201
202 return $algorithm;
203 }
204
212 public function getProvisioningUri(array $opts = array())
213 {
214 $issuer = $this->getIssuer();
215 $opts += array('issuer' => $issuer);
216
217 return $this
218 ->getAlgorithm()
219 ->generateUri(
220 $this->getLabel($issuer),
221 $opts
222 );
223 }
224
231 public function regenerate($newSecret = null)
232 {
233 if (!$newSecret)
234 {
235 $newSecret = Random::getBytes(static::SECRET_LENGTH);
236 }
237
238 $this->regenerated = true;
239 return $this
240 ->setType(static::getDefaultType())
241 ->setAttempts(0)
242 ->setSkipMandatory(false)
243 ->setInitialDate(new Type\DateTime)
244 ->setDeactivateUntil(null)
245 ->setParams(null)
246 ->setSecret($newSecret)
247 ->setActive(true)
248 ;
249 }
250
258 public function verify($input, $updateParams = true)
259 {
260 [$result, $newParams] = $this->getAlgorithm()->verify($input, $this->getParams());
261
262 if (
263 $updateParams
264 && $newParams !== null
265 && $this->isActivated()
266 )
267 {
268 $this
269 ->setParams($newParams)
270 ->save()
271 ;
272 }
273 return $result;
274 }
275
282 public function isAttemptsReached()
283 {
284 $attempts = $this->getAttempts();
285 $maxAttempts = $this->getMaxLoginAttempts();
286 return (
287 $maxAttempts > 0
288 && $attempts >= $maxAttempts
289 );
290 }
291
300 public function getSyncParameters($inputA, $inputB)
301 {
302 return $this->getAlgorithm()->getSyncParameters((string) $inputA, (string) $inputB);
303 }
304
315 public function syncParameters($inputA, $inputB = null)
316 {
317 if (!$inputA)
318 throw new OtpException(Loc::getMessage('SECURITY_OTP_ERROR_PASS1_EMPTY'));
319 elseif (!preg_match('/^\d{6}$/D', $inputA))
320 throw new OtpException(getMessage('SECURITY_OTP_ERROR_PASS1_INVALID'));
321
322 if ($this->getAlgorithm()->isTwoCodeRequired())
323 {
324 if (!$inputB)
325 throw new OtpException(Loc::getMessage('SECURITY_OTP_ERROR_PASS2_EMPTY'));
326 elseif (!preg_match('/^\d{6}$/D', $inputB))
327 throw new OtpException(Loc::getMessage('SECURITY_OTP_ERROR_PASS2_INVALID'));
328 }
329
330 try
331 {
332 $params = $this->getSyncParameters($inputA, $inputB);
333 }
334 catch (\Bitrix\Main\Security\OtpException)
335 {
336 throw new OtpException(Loc::getMessage('SECURITY_OTP_ERROR_SYNC_ERROR'));
337 }
338
339 $this->setParams($params);
340
341 return $this;
342 }
343
350 public function save()
351 {
352 $fields = array(
353 'ACTIVE' => $this->isActivated()? 'Y': 'N',
354 'TYPE' => $this->getType(),
355 'INIT_PARAMS' => $this->getInitParams(),
356 'ATTEMPTS' => $this->getAttempts(),
357 'SECRET' => $this->getHexSecret(),
358 'INITIAL_DATE' => $this->getInitialDate()?: new Type\DateTime,
359 'PARAMS' => $this->getParams(),
360 'SKIP_MANDATORY' => $this->isMandatorySkipped()? 'Y': 'N',
361 'DEACTIVATE_UNTIL' => $this->getDeactivateUntil()
362 );
363
364 if ($this->regenerated)
365 {
366 if (!$this->isInitialized())
367 throw new OtpException('Missing OTP params, forgot to call syncParameters?');
368
369 // Clear recovery codes when we connect new device
371 }
372
373 if ($this->isDbRecordExists())
374 {
375 $result = UserTable::update($this->getUserId(), $fields);
376 }
377 else
378 {
379 $fields += array(
380 'USER_ID' => $this->getUserId(),
381 );
382 $result = UserTable::add($fields);
383 }
384
385 $this->clearGlobalCache();
386
387 return $result->isSuccess();
388 }
389
395 public function delete()
396 {
398
399 return $this;
400 }
401
409 public function activate()
410 {
411 if (!$this->isInitialized())
412 throw new OtpException('OTP not initialized, if your activate it - user can\'t login anymore. Do you forgot to call regenerate?');
413
414 $this
415 ->setActive(true)
416 ->setDeactivateUntil(null)
417 ->save();
418
419 return $this;
420 }
421
429 public function deactivate($days = 0)
430 {
431 if (!$this->isActivated())
432 throw new OtpException('Otp not activated. Do your mean deffer?');
433
434 $this->setActive(false);
435 $this->setSkipMandatory();
436
437 if ($days <= 0)
438 {
439 $this->setDeactivateUntil(null);
440 }
441 else
442 {
443 $deactivateDate = Type\DateTime::createFromTimestamp(time() + $days * 86400);
444 $this->setDeactivateUntil($deactivateDate);
445 }
446
447 $this->save();
448
449 return $this;
450 }
451
459 public function defer($days = 0)
460 {
461 if ($this->isActivated())
462 throw new OtpException('Otp already activated. Do your mean deactivate?');
463
464 $this->setSkipMandatory();
465 if ($days <= 0)
466 {
467 $this->setDeactivateUntil(null);
468 }
469 else
470 {
471 $deactivateDate = Type\DateTime::createFromTimestamp(time() + $days * 86400);
472 $this->setDeactivateUntil($deactivateDate);
473 }
474
475 $this->save();
476
477 return $this;
478 }
479
494 public function setUserInfo(array $userInfo)
495 {
496 $this
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'])
505 ;
506
507 // Old users haven't INITIAL_DATE and DEACTIVATE_UNTIL
508 // ToDo: maybe it's not the best approach, think about it later
509 if ($userInfo['INITIAL_DATE'])
510 $this->setInitialDate($userInfo['INITIAL_DATE']);
511
512 if ($userInfo['DEACTIVATE_UNTIL'])
513 $this->setDeactivateUntil($userInfo['DEACTIVATE_UNTIL']);
514
515 return $this;
516 }
517
524 protected function setInitialDate(Type\DateTime $date)
525 {
526 $this->initialDate = $date;
527
528 return $this;
529 }
530
536 public function getInitialDate()
537 {
538 return $this->initialDate;
539 }
540
547 protected function setDeactivateUntil($date)
548 {
549 $this->deactivateUntil = $date;
550
551 return $this;
552 }
553
557 public function getDeactivateUntil()
558 {
560 }
561
568 protected function setSkipMandatory($isSkipped = true)
569 {
570 $this->skipMandatory = $isSkipped;
571
572 return $this;
573 }
574
580 public function isMandatorySkipped()
581 {
583 }
584
590 protected function getInitialTimestamp()
591 {
592 $initialDate = $this->getInitialDate();
593 if (!$initialDate)
594 return 0;
595
596 return $initialDate->getTimestamp();
597 }
598
605 protected function setUserId($userId)
606 {
607 $this->userId = $userId;
608
609 return $this;
610 }
611
617 public function getUserId()
618 {
619 return (int) $this->userId;
620 }
621
628 public function setActive($isActive)
629 {
630 $this->active = $isActive;
631
632 return $this;
633 }
634
640 public function isActivated()
641 {
642 return (bool) $this->active;
643 }
644
645 public function setUserActive($isActive)
646 {
647 $this->userActive = $isActive;
648
649 return $this;
650 }
651
652 public function isUserActive()
653 {
654 return (bool) $this->userActive;
655 }
656
660 public function isInitialized()
661 {
662 if ($this->isActivated())
663 {
664 // Without "hacks" OTP can't be activated without initialization
665 return true;
666 }
667
668 // ToDo: maybe better add new property with column?
669 return (bool) $this->getSecret();
670 }
671
678 protected function setAttempts($attemptsCount)
679 {
680 $this->attempts = $attemptsCount;
681
682 return $this;
683 }
684
690 public function getAttempts()
691 {
692 return (int) $this->attempts;
693 }
694
702 protected function setParams($params)
703 {
704 $this->params = $params;
705
706 return $this;
707 }
708
714 public function getParams()
715 {
716 return (string) $this->params;
717 }
718
724 public function getSecret()
725 {
726 return $this->secret;
727 }
728
734 public function getHexSecret()
735 {
736 $secret = $this->getSecret();
737
738 return bin2hex($secret);
739 }
740
746 public function getAppSecret()
747 {
748 $secret = $this->getSecret();
750
751 return rtrim($secret, '=');
752 }
753
760 public function setSecret($secret)
761 {
762 $this->secret = $secret;
763 return $this;
764 }
765
772 public function setHexSecret($hexValue)
773 {
774 $secret = pack('H*', $hexValue);
775
776 return $this->setSecret($secret);
777 }
778
785 public function setAppSecret($value)
786 {
787 $secret = Base32::decode($value);
788
789 return $this->setSecret($secret);
790 }
791
798 public function getIssuer()
799 {
800 if ($this->issuer === null)
801 $this->issuer = $this->getDefaultIssuer();
802
803 return $this->issuer;
804 }
805
812 public function setIssuer($issuer)
813 {
814 $this->issuer = $issuer;
815 return $this;
816 }
817
825 public function getLabel($issuer = null)
826 {
827 if ($this->label === null)
828 $this->label = $this->generateLabel($issuer);
829
830 return $this->label;
831 }
832
839 public function setLabel($label)
840 {
841 $this->label = $label;
842 return $this;
843 }
844
850 public function getContext()
851 {
852 if ($this->context === null)
853 $this->context = Application::getInstance()->getContext();
854
855 return $this->context;
856 }
857
864 public function setContext(\Bitrix\Main\Context $context)
865 {
866 $this->context = $context;
867 return $this;
868 }
869
876 public function setUserLogin($login)
877 {
878 $this->userLogin = $login;
879
880 return $this;
881 }
882
889 public function getUserLogin()
890 {
891 if ($this->userLogin === null && $this->userId)
892 {
893 $this->userLogin = \Bitrix\Main\UserTable::query()
894 ->addFilter('=ID', $this->getUserId())
895 ->addSelect('LOGIN')
896 ->exec()
897 ->fetch();
898
899 $this->userLogin = $this->userLogin['LOGIN'];
900 }
901
902 return $this->userLogin;
903 }
904
910 protected function getDefaultIssuer()
911 {
912 $host = Option::get('main', 'server_name');
913 if($host)
914 {
915 return preg_replace('#:\d+$#D', '', $host);
916 }
917 else
918 {
919 return Option::get('security', 'otp_issuer', 'Bitrix');
920 }
921 }
922
929 protected function generateLabel($issuer = null)
930 {
931 if ($issuer)
932 return sprintf('%s:%s', $issuer, $this->getUserLogin());
933 else
934 return $this->getUserLogin();
935 }
936
942 protected function getMaxLoginAttempts()
943 {
944 if (!$this->isActivated())
945 return 0;
946
947 return (int) $this->getPolicy()->getLoginAttempts();
948 }
949
955 protected function getRememberLifetime()
956 {
957 if (!$this->isActivated())
958 return 0;
959
960 return ((int) $this->getPolicy()->getStoreTimeout()) * 60;
961 }
962
968 protected function getRememberIpMask()
969 {
970 if (!$this->isActivated())
971 return '255.255.255.255';
972
973 return $this->getPolicy()->getStoreIpMask();
974 }
975
986 public function canSkipMandatory()
987 {
988 $result = $this->isMandatorySkipped();
989
990 if (!$result)
991 {
992 // Check mandatory rights
993 $result = $this->canSkipMandatoryByRights();
994 }
995
996 return $result;
997 }
998
1005 {
1006 $targetRights = static::getMandatoryRights();
1007 $userRights = \CAccess::getUserCodesArray($this->getUserId());
1008 $existedRights = array_intersect($targetRights, $userRights);
1009 $result = empty($existedRights);
1010
1011 return $result;
1012 }
1013
1019 protected function canSkipByCookie()
1020 {
1021 if (Option::get('security', 'otp_allow_remember') !== 'Y')
1022 return false;
1023
1024 $signedValue = $this->getContext()->getRequest()->getCookie(static::SKIP_COOKIE);
1025
1026 if (!$signedValue || !is_string($signedValue))
1027 return false;
1028
1029 try
1030 {
1031 $signer = new TimeSigner();
1032 $value = $signer
1033 ->setKey($this->getSecret())
1034 ->unsign($signedValue, 'MFA_SAVE');
1035 }
1036 catch (BadSignatureException)
1037 {
1038 return false;
1039 }
1040
1041 return ($value === $this->getSkipCookieValue());
1042 }
1043
1050 protected function getSkipCookieValue()
1051 {
1052 // ToDo: must be tied to the ID of "computer" when it will appear in the main module
1053 $rememberMask = $this->getRememberIpMask();
1054 $userIp = $this->getContext()->getRequest()->getRemoteAddress();
1055 return md5(ip2long($rememberMask) & ip2long($userIp));
1056 }
1057
1063 protected function setSkipCookie()
1064 {
1066 global $APPLICATION;
1067
1068 $signer = new TimeSigner();
1069 $rememberLifetime = $this->getRememberLifetime();
1070 $rememberLifetime += time();
1071 $rememberValue = $this->getSkipCookieValue();
1072
1073 $signedValue = $signer
1074 ->setKey($this->getSecret())
1075 ->sign($rememberValue, $rememberLifetime, 'MFA_SAVE');
1076
1077 $isSecure = (
1078 Option::get('main', 'use_secure_password_cookies', 'N') === 'Y'
1079 && $this->getContext()->getRequest()->isHttps()
1080 );
1081
1082 $APPLICATION->set_cookie(
1083 static::SKIP_COOKIE, // $name
1084 $signedValue, // $value
1085 $rememberLifetime, // $time = false
1086 '/', // $folder = "/"
1087 false, // $domain = false
1088 $isSecure, // $secure = false
1089 true, // $spread = true
1090 false, // $name_prefix = false
1091 true // $httpOnly = false
1092 );
1093
1094 return $this;
1095 }
1096
1102 protected function isDbRecordExists()
1103 {
1104 return UserTable::getRowById($this->getUserId()) !== null;
1105 }
1106
1112 protected function getPolicy()
1113 {
1114 if (!$this->userGroupPolicy)
1115 {
1116 $this->userGroupPolicy = \CUser::getPolicy($this->getUserId());
1117 }
1118
1120 }
1121
1127 protected function clearGlobalCache()
1128 {
1129 $cache_dir = '/otp/user_id/' . substr(md5($this->getUserId()), -2) . '/' . $this->getUserId() . '/';
1130 $cache = new \CPHPCache;
1131 $cache->CleanDir($cache_dir);
1132
1133 return $this;
1134 }
1135
1143 public static function verifyUser(array $params)
1144 {
1145 global $APPLICATION;
1146
1147 if (!static::isOtpEnabled()) // OTP disabled in settings
1148 return true;
1149
1150 $isSuccess = false;
1151
1152 // ToDo: review and refactoring needed
1153 $otp = static::getByUser($params['USER_ID']);
1154
1155 if (!$otp->isActivated())
1156 {
1157 // User does not use OTP
1158 $isSuccess = true;
1159
1160 if (
1161 static::isMandatoryUsing()
1162 && !$otp->canSkipMandatory()
1163 )
1164 {
1165 // Grace full period ends. We must reject authorization and defer reject reason
1166 if (!$otp->isDbRecordExists() && static::getSkipMandatoryDays())
1167 {
1168 // If mandatory enabled and user never use OTP - let us deffer initialization
1169 $otp->defer(static::getSkipMandatoryDays());
1170
1171 // We forgive the user for the first time
1172 static::setDeferredParams(null);
1173 return true;
1174 }
1175
1176 // Save a flag which indicates that an OTP is required, but user doesn't use it :-(
1177 $params[static::REJECTED_KEY] = static::REJECT_BY_MANDATORY;
1178 static::setDeferredParams($params);
1179 return false;
1180 }
1181 }
1182 else
1183 {
1184 if (!$otp->isUserActive())
1185 {
1186 //non-active user can't log in by OTP
1187 return false;
1188 }
1189 }
1190
1191
1192 if (!$isSuccess)
1193 {
1194 // User skip OTP on this browser by cookie
1195 $isSuccess = $otp->canSkipByCookie();
1196 }
1197
1198 if (!$isSuccess)
1199 {
1200 $isCaptchaChecked = (
1201 !$otp->isAttemptsReached()
1202 || $APPLICATION->captchaCheckCode($params['CAPTCHA_WORD'], $params['CAPTCHA_SID'])
1203 );
1204 $isRememberNeeded = (
1205 $params['OTP_REMEMBER']
1206 && Option::get('security', 'otp_allow_remember') === 'Y'
1207 );
1208
1209 if (!$isCaptchaChecked && !$APPLICATION->NeedCAPTHA())
1210 {
1211 // Backward compatibility with old login page
1212 $APPLICATION->SetNeedCAPTHA(true);
1213 }
1214
1215 $isOtpPassword = (bool) preg_match('/^\d{6}$/D', $params['OTP']);
1216 $isRecoveryCode = (
1217 static::isRecoveryCodesEnabled()
1218 && preg_match(RecoveryCodesTable::CODE_PATTERN, $params['OTP'])
1219 );
1220
1221 if ($isCaptchaChecked && ($isOtpPassword || $isRecoveryCode))
1222 {
1223 if ($isOtpPassword)
1224 $isSuccess = $otp->verify($params['OTP']);
1225 elseif ($isRecoveryCode)
1226 $isSuccess = RecoveryCodesTable::useCode($otp->getUserId(), $params['OTP']);
1227 else
1228 $isSuccess = false;
1229
1230 if (!$isSuccess)
1231 {
1232 $otp
1233 ->setAttempts($otp->getAttempts() + 1)
1234 ->save();
1235 }
1236 else
1237 {
1238 if ($otp->getAttempts() > 0)
1239 {
1240 // Clear OTP input attempts
1241 $otp
1242 ->setAttempts(0)
1243 ->save();
1244 }
1245
1246 if ($isRememberNeeded && $isOtpPassword)
1247 {
1248 // If user provide otp password (not recovery codes)
1249 // Sets cookie for bypass OTP checking
1250 $otp->setSkipCookie();
1251 }
1252 }
1253 }
1254 }
1255
1256 if ($isSuccess)
1257 {
1258 static::setDeferredParams(null);
1259 }
1260 else
1261 {
1262 // Save a flag which indicates that a form for OTP is required
1263 $params[static::REJECTED_KEY] = static::REJECT_BY_CODE;
1264 static::setDeferredParams($params);
1265
1266 //the OTP form will be shown on the next hit, send the event
1267 static::sendEvent($otp);
1268
1269 //write to the log ("on" by default)
1270 if(Option::get("security", "otp_log") <> "N")
1271 {
1272 \CSecurityEvent::getInstance()->doLog("SECURITY", "SECURITY_OTP", $otp->getUserId(), "");
1273 }
1274 }
1275
1276 return $isSuccess;
1277 }
1278
1279 protected static function sendEvent(Otp $otp)
1280 {
1281 $code = null;
1282 $algo = $otp->getAlgorithm();
1283
1284 //code value only for TOTP
1285 if($algo instanceof \Bitrix\Main\Security\Mfa\TotpAlgorithm)
1286 {
1287 //value based on the current time
1288 $timeCode = $algo->timecode(time());
1289 $code = $algo->generateOTP($timeCode);
1290 }
1291
1292 $eventParams = [
1293 "userId" => $otp->getUserId(),
1294 "code" => $code,
1295 ];
1296
1297 $event = new \Bitrix\Main\Event("security", "onOtpRequired", $eventParams);
1298 $event->send();
1299 }
1300
1306 public static function isOtpRequired()
1307 {
1308 return static::getDeferredParams() !== null;
1309 }
1310
1316 public static function isOtpRequiredByMandatory()
1317 {
1318 $params = static::getDeferredParams();
1319 if (
1320 !$params
1321 || !isset($params[static::REJECTED_KEY])
1322 )
1323 {
1324 return false;
1325 }
1326
1327 return $params[static::REJECTED_KEY] === static::REJECT_BY_MANDATORY;
1328 }
1329
1335 public static function isCaptchaRequired()
1336 {
1337 $params = static::getDeferredParams();
1338
1339 if (!$params || !isset($params['USER_ID']))
1340 return false;
1341
1342 $otp = static::getByUser($params['USER_ID']);
1343 return $otp && $otp->isAttemptsReached();
1344 }
1345
1351 public static function getDeferredParams()
1352 {
1353 $kernelSession = Application::getInstance()->getKernelSession();
1354 if (isset($kernelSession['BX_SECURITY_OTP']) && is_array($kernelSession['BX_SECURITY_OTP']))
1355 {
1356 return $kernelSession['BX_SECURITY_OTP'];
1357 }
1358
1359 return null;
1360 }
1361
1368 public static function setDeferredParams($params)
1369 {
1370 $kernelSession = Application::getInstance()->getKernelSession();
1371 if ($params === null)
1372 {
1373 unset($kernelSession['BX_SECURITY_OTP']);
1374 }
1375 else
1376 {
1377 // Probably we do not need saving password in deferred params
1378 // Or need? I don't know right now...
1379 if (isset($params['PASSWORD']))
1380 unset($params['PASSWORD']);
1381
1382 $kernelSession['BX_SECURITY_OTP'] = $params;
1383 }
1384 }
1385
1392 public static function setSkipMandatoryDays($days = 2)
1393 {
1394 Option::set('security', 'otp_mandatory_skip_days', (int) $days, null);
1395 }
1396
1402 public static function getSkipMandatoryDays()
1403 {
1404 return (int) Option::get('security', 'otp_mandatory_skip_days');
1405 }
1406
1413 public static function setMandatoryUsing($isMandatory = true)
1414 {
1415 Option::set('security', 'otp_mandatory_using', $isMandatory? 'Y': 'N', null);
1416 }
1417
1423 public static function isMandatoryUsing()
1424 {
1425 return (Option::get('security', 'otp_mandatory_using') === 'Y');
1426 }
1427
1434 public static function setMandatoryRights(array $rights)
1435 {
1436 Option::set('security', 'otp_mandatory_rights', serialize($rights), null);
1437 }
1438
1444 public static function getMandatoryRights()
1445 {
1446 $targetRights = Option::get('security', 'otp_mandatory_rights');
1447 $targetRights = unserialize($targetRights, ['allowed_classes' => false]);
1448 if (!is_array($targetRights))
1449 $targetRights = array();
1450
1451 return $targetRights;
1452 }
1453
1461 public static function setDefaultType($value)
1462 {
1463 if (!in_array($value, static::$availableTypes))
1464 throw new ArgumentOutOfRangeException('value', static::$availableTypes);
1465
1466 Option::set('security', 'otp_default_algo', $value, null);
1467 }
1468
1469
1475 public static function getDefaultType()
1476 {
1477 return Option::get('security', 'otp_default_algo');
1478 }
1479
1485 public static function getAvailableTypes()
1486 {
1487 return static::$availableTypes;
1488 }
1489
1495 public static function getTypesDescription()
1496 {
1497 return array(
1498 self::TYPE_HOTP => array(
1499 'type' => self::TYPE_HOTP,
1500 'title' => Loc::getMessage('SECURITY_HOTP_TITLE'),
1501 'required_two_code' => true,
1502 ),
1503 self::TYPE_TOTP => array(
1504 'type' => self::TYPE_TOTP,
1505 'title' => Loc::getMessage('SECURITY_TOTP_TITLE'),
1506 'required_two_code' => false,
1507 )
1508 );
1509 }
1510
1516 public static function isOtpEnabled()
1517 {
1518 return (Option::get('security', 'otp_enabled') === 'Y');
1519 }
1520
1526 public static function isRecoveryCodesEnabled()
1527 {
1528 return (Option::get('security', 'otp_allow_recovery_codes') === 'Y');
1529 }
1530}
static loadMessages($file)
Definition loc.php:64
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29
static getList(array $parameters=array())
static update($primary, array $data)
static decode($base32String)
Definition base32.php:119
static encode($string)
Definition base32.php:60
setHexSecret($hexValue)
Definition otp.php:772
static isCaptchaRequired()
Definition otp.php:1335
regenerate($newSecret=null)
Definition otp.php:231
getProvisioningUri(array $opts=array())
Definition otp.php:212
static setMandatoryRights(array $rights)
Definition otp.php:1434
static isRecoveryCodesEnabled()
Definition otp.php:1526
static sendEvent(Otp $otp)
Definition otp.php:1279
setActive($isActive)
Definition otp.php:628
getLabel($issuer=null)
Definition otp.php:825
const REJECT_BY_MANDATORY
Definition otp.php:31
__construct($algorithm=null)
Definition otp.php:67
setSkipMandatory($isSkipped=true)
Definition otp.php:568
static getAvailableTypes()
Definition otp.php:1485
static isOtpRequiredByMandatory()
Definition otp.php:1316
getSyncParameters($inputA, $inputB)
Definition otp.php:300
setInitParams(array $params)
Definition otp.php:165
syncParameters($inputA, $inputB=null)
Definition otp.php:315
static getTypesDescription()
Definition otp.php:1495
static getByUser($userId)
Definition otp.php:87
static getMandatoryRights()
Definition otp.php:1444
setDeactivateUntil($date)
Definition otp.php:547
static isMandatoryUsing()
Definition otp.php:1423
setContext(\Bitrix\Main\Context $context)
Definition otp.php:864
setAttempts($attemptsCount)
Definition otp.php:678
verify($input, $updateParams=true)
Definition otp.php:258
deactivate($days=0)
Definition otp.php:429
static verifyUser(array $params)
Definition otp.php:1143
static getDefaultType()
Definition otp.php:1475
static setDeferredParams($params)
Definition otp.php:1368
static setDefaultType($value)
Definition otp.php:1461
static isOtpRequired()
Definition otp.php:1306
setUserActive($isActive)
Definition otp.php:645
setInitialDate(Type\DateTime $date)
Definition otp.php:524
const TAGGED_CACHE_TEMPLATE
Definition otp.php:33
setUserInfo(array $userInfo)
Definition otp.php:494
static getByType($type)
Definition otp.php:130
static getSkipMandatoryDays()
Definition otp.php:1402
static getDeferredParams()
Definition otp.php:1351
generateLabel($issuer=null)
Definition otp.php:929
static isOtpEnabled()
Definition otp.php:1516
static $availableTypes
Definition otp.php:35
static setMandatoryUsing($isMandatory=true)
Definition otp.php:1413
static setSkipMandatoryDays($days=2)
Definition otp.php:1392
static useCode($userId, $searchCode)