5.2. Распределение доступа и аутентификация¶
5.2.1. Доступ пользователей¶
В теле любого запроса по API используется идентификатор пользователя user_id. Чтобы посмотреть список идентификаторов, войдите под учетной записью администратора сервера (роль Суперадмин) и перейдите на вкладку пользователей Admin ➝ Users:
Распределение доступа пользователей по API осуществляется на тех же принципах, что и распределение доступа в веб-интерфейсе:
Владелец проекта имеет права администратора.
Другие пользователи могут быть добавлены в проект с правами на полное управление или только на просмотр (См. Совместный доступ к проекту в Boro UG).
Если пользователь не имеет доступа к проекту, тогда для его идентификатора пользователя проект будет также недоступен по API.
Для пользователей с ролями Суперадмин или Супервайзер сохраняются возможности, предусмотренные их ролями, при работе по API.
5.2.2. Подпись API запросов¶
Аутентификация¶
Для аутентификации пользователя при запросе используется метод цифровой подписи HMAC-SHA2. Данный метод использует устойчивые алгоритмы, при этом является довольно простым в реализации на стороне клиента.
Каждый пользователь, который будет управлять сервером через API, получает собственный ключ API. Данный ключ используется в создании цифровой подписи HTTP-запроса. Подпись генерируется на основании набора данных (тела сообщения, даты и др.) и позволяет однозначно аутентифицировать пользователя, от которого осуществляется запрос. Кроме этого, метод позволяет оправлять запросы по незашифрованному протоколу HTTP, т.к. любые модификации тела сообщения приводят к несоответствию оригинальной подписи. Подпись становится недействительной через 1 минуту для защиты от атаки повторного воспроизведения.
Как включить подпись сообщений¶
Важно
Предварительно необходимо указать имя хоста в поле Host name во вкладке Email. Без указания Host name не удастся применить настройки API
Чтобы включить аутентификацию пользователей при использовании API, войдите под учетной записью администратора сервера (роль Суперадмин). Перейдите на вкладки настроек сервера Admin ➝ Control API и включите настройку Проверять HMAC-подписи.
Далее каждый пользователь, который будет управлять сервером, должен сгенерировать свой ключ API. Кликните на имя пользователя в правом верхнем углу и в выпадающем меню выберите пункт Мой профиль. В разделе «CONTROL API» необходимо нажать кнопку Создать ключ. Если вы считаете, что ключ API был дискредитирован, можно удалить ключ и сгенерировать новый.
Полезные ссылки¶
Как это работает¶
Решение по аутентификации пользователей базируется на основе библиотеки ApiAuth, написанной на языке Ruby. Если ваш клиент написан на языке Ruby, вы можете использовать упомянутую библиотеку.
В данном руководстве используется пример кода, написанный на языке Python. Код предназначен для описания базовых принципов генерации подписи. Для создания подписи необходимо выполнить следующие шаги:
Зафиксировать время и дату создания запроса в формате
DAY, DD MON YYYY hh:mm:ss GMT
в соответствии с RFC 1123.Важно
Подпись становится недействительной через 1 минуту для защиты от атаки повторного воспроизведения. Необходимо убедиться в синхронизированности времени на Elecard Boro Server и на компьютере, откуда осуществляются запросы на управление по API. В случае несовпадения времени возникнет ошибка верификации подписи. Оптимальным решением является использование NTP-сервера на обоих компьютерах.
Вычислить значение заголовка
X-Authorization-Content-SHA256
. Это результат хеш-функции SHA-256 от тела HTTP запроса, закодированный в Base64. В примере кода тело запроса содержится в переменнойrequest_parameters
. Приuser_id = '625721355'
результат должен быть следующим:x_authorization_content_sha256 = 'OniJqRAkzQHN8KgmAZm/yT5dP94m8CmVVaSTRVg/ptQ='
Сформировать каноническую строку (canonical string). Строка содержит строгую последовательность значений следующих заголовков, разделенных символом запятой:
Request Method
,Content-Type
,X-Authorization-Content-SHA256
,Request URI
иDate
. Обратите внимание, чтоRequest URI
это не полный URI, а только его часть - путь (Path). Пример канонической строки выглядит следующим образом:canonical_string = 'POST,application/json,OniJqRAkzQHN8KgmAZm/yT5dP94m8CmVVaSTRVg/ptQ=,/ctrl_api/v1/json,Thu, 25 Aug 2022 04:27:52 GMT'
Вычислить код аутентификации сообщения (подпись). Это результат хеш-функции SHA-256 HMAC от канонической строки с использованием ключа API пользователя, закодированный в Base64. Ключ API пользователя предоставляется в формате Base64, поэтому требуется предварительное преобразование ключа в формат Bytes Objects. При
secret_key = 'AGnO/VenzHB9xkLYZG1i70kQ9iyFBBvugGXSFyTQaB0='
значение подписи будет следующим:signature = 'vPI9MMRwBZLWNrCcnLnbJjZRna0+XP7yFMhc9KMUFdw='
Добавить подпись в HTTP-запрос. Необходимо убедиться, что все заголовки, которые участвовали в создании канонической строки, передаются в вашем запросе (порядок не учитывается). Обратите внимание, что подпись передается в заголовке
Authorization
в следующем формате:'APIAuth-HMAC-SHA256 ' + user_id + ':' + signature
. Пример значения заголовка:authorization_header = 'APIAuth-HMAC-SHA256 625721355:vPI9MMRwBZLWNrCcnLnbJjZRna0+XP7yFMhc9KMUFdw='
Отправить HTTP запрос.
Как запустить пример кода на Python¶
Для запуска скрипта в вашей системе должен быть установлен Python 3. При попытке запуска система может выдать сообщение об отсутствии необходимых зависимых пакетов. Установите необходимые пакеты, используя пакет pip.
Создайте файл с расширением .py и скопируйте в него содержимое примера кода. В теле скрипта укажите корректные значения полей: адрес сервера
request_host
, идентификатор пользователяuser_id
, идентификатор проектаproject_id
.Откройте командную строку и передайте в виде переменной окружения ваш ключ API:
Windows
set SECRET_ACCESS_KEY=AGnO/VenzHB9xkLYZG1i70kQ9iyFBBvugGXSFyTQaB0=
Linux
export SECRET_ACCESS_KEY=AGnO/VenzHB9xkLYZG1i70kQ9iyFBBvugGXSFyTQaB0=
В этом же окне командной строки перейдите к файлу со скриптом и запустите его:
python api_hmac_signing.py
Результат с успешной верификацией запроса изображен ниже:
Пример кода на Python¶
#!/usr/bin/env python3
# © Copyright 2000-2024 Elecard: Video Compression Guru. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# The initial release of this source code was created by Amazon.com, Inc. or its affiliates on 2010-2019.
# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
# Elecard Boro Solution
# API request signing example
# See: https://boro.elecard.com/docs/en/boro-solution-userguide/chapter.ControlAPI/Signing.Requests.html
# This version makes a POST request and passes request parameters
# in the body (payload) of the request. Auth information is passed in
# the Authorization header.
import base64
import hashlib
import hmac
import json
import os
import sys
import requests # pip install requests
# To get a date in RFC 1123 Date Representation
from wsgiref.handlers import format_date_time
from datetime import datetime
from time import mktime
# ************* REQUEST VALUES *************
http_method = 'POST'
content_type = 'application/json'
request_host = '172.16.1.82'
request_path = '/ctrl_api/v1/json'
user_id: int = 1
project_id: int = 1
# "AppList" request body passed in a JSON block.
body = {
"user_id": user_id,
"methods": [
{
"method": "AppList",
"params": {
"project_id": project_id,
"app_status": "all"
}
}
]
}
request_parameters = json.dumps(body)
# Best practice is NOT to embed credentials in a code.
# Get API key value from an environment variable
secret_key = os.environ.get('SECRET_ACCESS_KEY')
if secret_key is None:
print('No API key is available.')
sys.exit()
# ************* CREATE THE STRING TO SIGN *************
# Create a date in RFC 1123 Date Representation ('Fri, 05 Aug 2022 03:09:30 GMT') format for headers and the credential string
now = datetime.now()
timestamp = format_date_time(mktime(now.timetuple()))
# Calculate payload (body of the request) hash.
# X-Authorization-Content-SHA256 header is the Base64-encoded SHA-256 hash value used to generate the signature base string.
# This header should be provided by the client, to ensure the request body it sent was not tampered with on its way to the server.
payload_hash = hashlib.sha256(request_parameters.encode('utf-8')).digest()
payload_hash_base64 = base64.b64encode(payload_hash)
x_authorization_content_sha256 = payload_hash_base64.decode('utf-8')
# A canonical string is created using your HTTP headers containing the content-type,
# X-Authorization-Content-SHA256, request path and the date/time stamp.
# This string is then used to create the signature.
canonical_string = http_method + ',' + content_type + ',' + x_authorization_content_sha256 + ',' + request_path + ',' + timestamp
# ************* CALCULATE THE SIGNATURE *************
# Calculate the signature which is a Base64 encoded SHA256 HMAC, using the client's Base64 encoded private API key.
signature_hash = hmac.new(base64.b64decode(secret_key), canonical_string.encode('utf-8'), hashlib.sha256).digest()
signature_hash_base64 = base64.b64encode(signature_hash)
signature = signature_hash_base64.decode('utf-8')
# ************* ADD SIGNING INFORMATION TO THE REQUEST *************
# Put the signature to the Authorization HTTP header in the form: Authorization = APIAuth-HMAC-SHA256 "user_id:signature"
authorization_header = 'APIAuth-HMAC-SHA256 ' + str(user_id) + ':' + signature
# Prepare the necessary headers to be passed in the HTTP request. Order here is not significant.
headers = {'Content-Type': content_type,
'Date': timestamp,
'X-Authorization-Content-SHA256': x_authorization_content_sha256,
'Authorization': authorization_header}
# ************* SEND THE REQUEST *************
request_uri = 'http://' + request_host + request_path
print('\nBEGIN REQUEST:')
print('Request URL = ' + request_uri)
print('Request body = ' + request_parameters)
r = requests.post(request_uri, data=request_parameters, headers=headers)
print('\nRESPONSE:')
print('Response code: %d\n' % r.status_code)
print(r.json())