Использование REST hook для авторизации пользователя по домену¶
Описание¶
Допустим, возникла задача ограничить доступ пользователей к серверу, основываясь на домене, например, разрешить просмотр видео только с определенного домена. Эта задача может быть решена при помощи REST hooks.
WCS передает в REST-запросе типа 1 connect
к бэкенд-серверу поле origin
, содержащее доменное имя WCS-сервера, по которому к нему обратился пользователь, например
{
"nodeId" : "5tWOFn5ZoMQs22KrEls2Ulhee57hQO9D",
"appKey" : "defaultApp",
"sessionId" : "/192.168.1.45:53438/abcdef0123456789",
"useWsTunnel" : false,
"useWsTunnelPacketization2" : false,
"useBase64BinaryEncoding" : false,
"mediaProviders" : [ "WebRTC", "MSE", "WSPlayer" ],
"clientVersion" : "0.5.28",
"clientOSVersion" : "5.0 (Windows)",
"clientBrowserVersion" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0",
"keepAlive" : false,
"origin" : "https://test2.flashphoner.com:8888"
}
Таким образом, для авторизации пользователя по домену необходимо реализовать REST hook /connect
.
Пример реализации REST hook типа 1 для авторизации по домену¶
Требования к реализации¶
-
REST hook типа 1 должен быть доступен на веб-сервере как
http://yourhost/rest-hooks/connect
-
REST hook должен обрабатывать POST
application/json
HTTP запросы. -
REST hook должен возвращать в теле ответа в точности то же, что он получил в запросе (зеркало), за исключением настройки
restClientConfig
. -
WCS сервер должен быть настроен на работу с REST hook следующим образом:
Обращения к REST методу можно отслеживать в логе сервера
Код примера и его разбор¶
В первых строках скрипта определяется метод и декодируется тело запроса. Здесь же задаем домен для авторизации:
<?php
$api_method = array_pop(explode("/", $_SERVER['REQUEST_URI']));
$incoming_data = json_decode(file_get_contents('php://input'), true);
$domain = "yourdomain.com";
Начинается обработка метода connect
. Здесь определяется переданное в запросе поле origin
и заполняется поле restClientConfig
для ответа:
switch($api_method) {
case"connect":
$origin = $incoming_data['origin'];
//logs
error_log("sessionId: " . $incoming_data['sessionId']);
error_log("origin: " . $origin);
$rest_client_config = json_decode(file_get_contents('rest_client_config.json'), true);
$incoming_data['restClientConfig'] = $rest_client_config;
...
}
Проверка домена. Если домен не найден, вызывается функция ubnormalResponse
для формирования отрицательного ответа 403
switch($api_method) {
case"connect":
...
$found = strpos($origin, $domain);
if ($found !== false){
error_log("User authorized by domain " . $domain);
} else {
error_log("User not authorized by domain: " . $domain . " Connection failed with 403 status.");
ubnormalResponse(403);
}
break;
...
}
Вывод ответа на запрос
Функция ubnormalResponse()
завершает скрипт:
function ubnormalResponse($code) {
if ($code == 403) {
header('HTTP/1.1 403 Forbidden', true, $code);
} else {
header(':', true, $code);
}
die();
}
?>
Пример скрипта целиком
<?php
$api_method = array_pop(explode("/", $_SERVER['REQUEST_URI']));
$incoming_data = json_decode(file_get_contents('php://input'), true);
$domain = "yourdomain.com";
switch($api_method) {
case"connect":
$origin = $incoming_data['origin'];
//logs
error_log("sessionId: " . $incoming_data['sessionId']);
error_log("origin: " . $origin);
$rest_client_config = json_decode(file_get_contents('rest_client_config.json'), true);
$incoming_data['restClientConfig'] = $rest_client_config;
$found = strpos($origin, $domain);
if ($found !== false){
error_log("User authorized by domain " . $domain);
} else {
error_log("User not authorized by domain: " . $domain . " Connection failed with 403 status.");
ubnormalResponse(403);
}
break;
}
header('Content-Type: application/json');
echo json_encode($incoming_data);
function ubnormalResponse($code) {
if ($code == 403) {
header('HTTP/1.1 403 Forbidden', true, $code);
} else {
header(':', true, $code);
}
die();
}
?>
Содержимое restClientConfig
из файла rest_client_config.json
для ответа на запрос /connect
{
"ConnectionStatusEvent" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"RegistrationStatusEvent" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"sendXcapRequest" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"XcapStatusEvent" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"sendDtmf" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"call" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"OnCallEvent" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"answer" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"hangup" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"hold" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"unhold" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"transfer" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"OnTransferEvent" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "FAIL",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"TransferStatusEvent" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"CallStatusEvent" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"sendMessage" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "FAIL",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"OnMessageEvent" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"MessageStatusEvent" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"publishStream" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"unPublishStream" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"playStream" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"stopStream" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"StreamStatusEvent" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"subscribe" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"SubscriptionStatusEvent" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"OnDataEvent" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"DataStatusEvent" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"submitBugReport" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"BugReportStatusEvent" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"pushLogs" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"RecordingStatusEvent" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"ErrorStatusEvent" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
},
"disconnect" : {
"clientExclude" : "",
"restExclude" : "",
"restOnError" : "LOG",
"restPolicy" : "NOTIFY",
"restOverwrite" : ""
}
}