69 $this->
class =
$params['CLASS'];
70 $this->method = $toLowerMethod ? mb_strtolower(
$params[
'METHOD']) :
$params[
'METHOD'];
71 $this->query =
$params[
'QUERY'];
73 $this->transport =
$params[
'TRANSPORT'] ??
null;
75 $this->securityClientState =
$params[
'STATE'] ??
null;
79 $this->transport =
'json';
82 if(self::$instance ===
null)
84 self::$instance = $this;
93 return self::$instance;
105 $this->timeStart = microtime(
true);
107 if(!defined(
'BX24_REST_SKIP_SEND_HEADERS'))
109 \CRestUtil::sendHeaders();
116 $handler =
new $this->
class();
118 $this->arServiceDesc = $handler->getDescription();
122 if($this->checkScope())
130 $logger->logRequest();
132 if($this->tokenCheck)
152 throw new RestException(
'Method not found!', RestException::ERROR_METHOD_NOT_FOUND, self::STATUS_NOT_FOUND);
160 $e = RestException::initFromException($e->getOriginalException());
166 $e = RestException::initFromException($e);
174 $this->error->setApplicationException($ex);
180 return $this->outputError();
188 $methodDescription = $this->getMethodDescription();
189 if(!$methodDescription)
191 throw new RestException(
'Method not found!', RestException::ERROR_METHOD_NOT_FOUND, self::STATUS_NOT_FOUND);
194 return in_array($this->method,
array(
195 \CRestUtil::METHOD_DOWNLOAD,
196 \CRestUtil::METHOD_UPLOAD,
197 )) || isset($this->query[
'token']);
202 $token = $this->query[
"token"];
204 [$this->scope, $queryString, $querySignature] = explode(\CRestUtil::TOKEN_DELIMITER, $token);
208 if($signature === $querySignature)
210 $queryString = base64_decode($queryString);
213 parse_str($queryString,
$query);
217 $callback = $this->getMethodCallback();
221 throw new RestException(
'Method not found!', RestException::ERROR_METHOD_NOT_FOUND, self::STATUS_NOT_FOUND);
244 throw new RestException(
'Method is blocked due to operation time limit.', RestException::ERROR_OPERATION_TIME_LIMIT, self::STATUS_TO_MANY_REQUESTS);
248 if(isset($this->query[
'start']))
250 $start = intval($this->query[
'start']);
251 unset($this->query[
'start']);
254 $callback = $this->getMethodCallback();
258 throw new RestException(
'Method not found!', RestException::ERROR_METHOD_NOT_FOUND, self::STATUS_NOT_FOUND);
261 $this->timeProcessStart = microtime(
true);
265 $this->usage = getrusage();
269 LoadLimiter::registerStarting(
275 LoadLimiter::registerEnding(
280 $this->timeProcessFinish = microtime(
true);
282 if (!empty(
$result[
'error']) && !empty(
$result[
'error_description']))
288 if(is_array(
$result[
'result']))
290 if(isset(
$result[
'result'][
'next']))
293 unset(
$result[
'result'][
'next']);
297 if(array_key_exists(
'total',
$result[
'result']))
300 unset(
$result[
'result'][
'total']);
304 if($this->securityClientState !=
null && $this->securityMethodState !=
null)
316 return $this->transport;
326 return $this->authData;
331 if ($this->authScope ==
null)
333 $this->authScope =
array();
336 $this->authScope = explode(
',',
$authData[
'scope']);
339 return $this->authScope;
349 return $this->authType;
369 return $this->passwordId;
384 $this->securityMethodState = $state;
394 return array_keys($this->arServiceDesc);
399 return $this->arServiceDesc;
404 if(!\
Bitrix\
Rest\OAuthService::getEngine()->isRegistered())
418 $signatureState = mb_strtolower(
$method)
419 .\CRestUtil::TOKEN_DELIMITER.($this->scope === \CRestUtil::GLOBAL_SCOPE ?
'' : $this->scope)
420 .\CRestUtil::TOKEN_DELIMITER.$queryString
421 .\CRestUtil::TOKEN_DELIMITER.implode(\CRestUtil::TOKEN_DELIMITER, $this->auth);
423 return $this->makeSignature(
$key, $signatureState);
433 $methodState = is_array($this->securityMethodState)
434 ? $this->securityMethodState
435 :
array(
'data' => $this->securityMethodState);
437 $methodState[
'state'] = $this->securityClientState;
439 $signature = $this->makeSignature(
$arRes[
'SHARED_KEY'], $methodState);
469 !isset($this->authData[
'parameters'])
470 || !isset($this->authData[
'parameters'][
'notify_allow'])
471 || !array_key_exists($this->method, $this->authData[
'parameters'][
'notify_allow'])
475 $notify->send($this->
getClientId(), $this->authData[
'access_token'], $this->method,
$message);
477 $this->authData[
'parameters'][
'notify_allow'][
$this->method] = 0;
479 if($this->authData[
'parameters_callback'] && is_callable($this->authData[
'parameters_callback']))
481 call_user_func_array($this->authData[
'parameters_callback'],
array($this->authData));
485 if($this->authData[
'parameters'][
'notify_allow'][$this->method] === 0)
487 throw new RestException(
'Waiting for confirmation',
'METHOD_CONFIRM_WAITING', static::STATUS_UNAUTHORIZED);
489 elseif($this->authData[
'parameters'][
'notify_allow'][$this->method] < 0)
491 throw new RestException(
'Method call denied',
'METHOD_CONFIRM_DENIED', static::STATUS_FORBIDDEN);
497 private function init()
499 if(!in_array($this->transport,
array(
'json',
'xml')))
501 throw new RestException(
'Wrong transport!', RestException::ERROR_INTERNAL_WRONG_TRANSPORT, self::STATUS_INTERNAL);
503 elseif(!$this->checkSite())
505 throw new RestException(
'Portal was deleted', RestException::ERROR_INTERNAL_PORTAL_DELETED, self::STATUS_FORBIDDEN);
507 elseif(!class_exists($this->
class) || !method_exists($this->
class,
'getDescription'))
509 throw new RestException(
'Wrong handler class!', RestException::ERROR_INTERNAL_WRONG_HANDLER_CLASS, self::STATUS_INTERNAL);
513 if(array_key_exists(
"state", $this->query))
515 $this->securityClientState = $this->query[
"state"];
516 unset($this->query[
"state"]);
523 private function checkSite()
525 return \Bitrix\Main\Config\Option::get(
"main",
"site_stopped",
"N") !==
'Y';
528 private function getMethodDescription()
532 foreach($this->arServiceDesc as $scope => $arMethods)
534 if(array_key_exists($this->method, $arMethods))
542 if(!isset($this->arServiceDesc[$this->scope]) || !isset($this->arServiceDesc[$this->scope][$this->method]))
549 if (!isset($this->arServiceDesc[
$result[
'scope']]) || !is_array($this->arServiceDesc[
$result[
'scope']]))
554 $this->scope =
$result[
'scope'];
566 private function getMethodCallback()
568 $methodDescription = $this->getMethodDescription();
570 if($methodDescription)
572 $callback = isset($methodDescription[
'callback'])
573 ? $methodDescription[
'callback']
574 : $methodDescription;
577 if(!is_callable($callback) && is_array($callback) &&
count($callback) > 2)
579 $callback =
array($callback[0], $callback[1]);
582 if(is_callable($callback))
591 private function checkScope()
593 if($this->tokenCheck)
595 if(isset($this->query[
"token"]) && $this->query[
"token"] <>
'')
597 [
$scope] = explode(\CRestUtil::TOKEN_DELIMITER, $this->query[
"token"], 2);
602 $callback = $this->getMethodCallback();
615 if(\CRestUtil::checkAuth($this->query, $this->scope,
$res))
617 $this->authType =
$res[
'auth_type'];
619 $this->clientId = isset(
$res[
'client_id']) ?
$res[
'client_id'] :
null;
620 $this->passwordId = isset(
$res[
'password_id']) ?
$res[
'password_id'] :
null;
622 $this->authData =
$res;
625 (isset($this->authData[
'auth_connector']))
629 throw new \Bitrix\Rest\LicenseException(
'auth_connector');
632 if(isset(
$res[
'parameters_clear']) && is_array(
$res[
'parameters_clear']))
634 foreach(
$res[
'parameters_clear'] as $param)
636 if(array_key_exists($param, $this->query))
638 $this->auth[$param] = $this->query[$param];
639 unset($this->query[$param]);
644 $arAdditionalParams =
$res[
'parameters'] ??
null;
645 if(isset($arAdditionalParams[\
Bitrix\
Rest\
Event\Session::PARAM_SESSION]))
654 throw new \Bitrix\Rest\OAuthException(
$res);
661 || Feature::isFeatureEnabled(
'rest_auth_connector');
666 $methodDescription = $this->getMethodDescription();
667 return is_array($methodDescription[
'options'])
668 ? $methodDescription[
'options']
672 private function makeSignature(
$key, $state)
676 if(Loader::includeModule(
'socialservices'))
678 $signer =
new Bitrix24Signer();
679 $signer->setKey(
$key);
680 $signature = $signer->sign($state);
688 private function outputError()
691 'error' => $this->error->getErrorCode(),
692 'error_description' => $this->error->getMessage(),
693 ), $this->error->getAdditional());
702 \CHTTP::setStatus($this->error->getStatus());
704 elseif($this->resultStatus)
706 \CHTTP::setStatus($this->resultStatus);
710 \CHTTP::setStatus(self::STATUS_OK);
713 switch($this->transport)
716 Header(
'Content-Type: application/json; charset=utf-8');
719 Header(
'Content-Type: text/xml; charset=utf-8');
732 Header(
'X-Bitrix-Rest-Application: '.$this->clientId);
735 Header(
'X-Bitrix-Rest-Time: '.number_format($this->timeProcessFinish - $this->timeProcessStart, 10,
'.',
''));
737 if($this->usage && function_exists(
'getrusage'))
741 Header(
'X-Bitrix-Rest-User-Time: '.number_format(
$usage[
'ru_utime.tv_sec'] - $this->usage[
'ru_utime.tv_sec'] + (
$usage[
'ru_utime.tv_usec'] - $this->usage[
'ru_utime.tv_usec']) / 1000000, 10,
'.',
''));
742 Header(
'X-Bitrix-Rest-System-Time: '.number_format(
$usage[
'ru_stime.tv_sec'] - $this->usage[
'ru_stime.tv_sec'] + (
$usage[
'ru_stime.tv_usec'] - $this->usage[
'ru_stime.tv_usec']) / 1000000, 10,
'.',
''));
752 isset(
$data[
'result'])
756 return $data[
'result'];
759 switch($this->transport)
762 return $this->outputJson(
$data);
765 return $this->outputXml(
array(
'response' =>
$data));
774 'start' => $this->timeStart,
775 'finish' => microtime(
true),
778 $data[
'time'][
'duration'] =
$data[
'time'][
'finish'] -
$data[
'time'][
'start'];
779 $data[
'time'][
'processing'] = $this->timeProcessFinish - $this->timeProcessStart;
781 $data[
'time'][
'date_start'] = date(
'c',
$data[
'time'][
'start']);
782 $data[
'time'][
'date_finish'] = date(
'c',
$data[
'time'][
'finish']);
784 if (LoadLimiter::isActive())
786 $reset = LoadLimiter::getResetTime(
793 $data[
'time'][
'operating_reset_at'] = $reset;
796 $data[
'time'][
'operating'] = LoadLimiter::getRestTime(
806 private function outputJson(
$data)
812 catch(\Bitrix\Main\ArgumentException $e)
814 $res =
'{"error":"WRONG_ENCODING","error_description":"Wrong request encoding"}';
820 private function outputXml(
$data)
828 $res .=
'<'.$key.
'>';
831 $res .= $this->outputXml($value);
833 $res .= \CDataXML::xmlspecialchars($value);
835 $res .=
'</'.$key.
'>';
842 self::$instance =
null;
static includeModule($moduleName)
static isModuleInstalled($moduleName)
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)