Wiki
Clone wikisinops / Home
Добро пожаловать
Это страница документации пакета sinops.
Sinops (signed operations) - пакет для создания в django приложении представлений, допускающих исполнение, только при наличии специального документа подписанного с использованием ЭЦП.
Содержание:
Схема взаимодействия с клиентом.
Предположим имеется клиент-серверное приложение, которое предоставляет своим пользователям (клиентам) совершить определенные действия, только после предоставления специального документа, защищенного электроно-цифровой подписью. При использовании приложением пакета sinops процесс взаимодействия клиента с сервером будет следующим:
- Клиент посылает запрос на выполнение некоторого действия.
- Сервер определяет, что для успешного выполнения действия
клиенту необходимо выполнить подписание некотрого документа.
- Сервер генерирует документ, описывающий действие которое клиент намеревается выполнить, a также определяет доступные способы его подписания.
- Сервер возвращает клиенту ответ, содержащий сгенерированный документ и информацию о допустимых форматах подписи, с помощю которых он может быть подписан.
- Клиент подписывает документ и выполняет действие повторно, но на этот раз добавляет к запросу также и информацию о подписанном документе (подпись и некоторые дополнительные атрибуты)
- Сервер снова принимает запрос и:
- Выполняется проверка, что формат подписи среди допустимых форматов.
- Выполняется проверка, подписан именно тот документ, который был сгенерирован сервером в пункте 2.1
- Выполняется проверка на то, что выполняемое в текущем запросе действие такое же как и в пункте 2. (т.е результат будет аналогичным.)
- Выполняется проверка валидности ЭЦП.
- В случае успешной проверки предыдущих пунктов, производиться попытка выполнить запрошенное действие.
- Производиться логирование операции.
- Клиенту возвращается ответ со статусом операции.
- Клиент получает ответ. Пользователю дается ссылка на загрузку подписанного документа либо сообщение об ошибке.
Установка и настройка
Для начала нужно добавить пакет sinops в INSTALLED_APPS, выполнить миграции и настроить интерфейс взаимодействия с клиентской частью:
# settings.py
INSTALLED_APPS += ('sinops',)
SINOPS_SETTINGS = {
'DEFAULT_CLIENT_INTERFACE_CLASS': 'mypackage.MyInterface'
}
# mypackage.py
from django.shortcuts import render_to_response
from sinops.interface import ClientInterfaceBase
class MyInterface(ClientInterfaceBase)
def ask_signature_result(self, request, operation):
u"""
Возвращает результат представления, который должен сообщить
клиенту информацию о том, что совершаемая им операция
должна быть подписана с использованием ЭЦП.
"""
return render_to_response(
'ask_sign_template.html', {msg: operation.get_content_to_sign()}
)
def operation_completed_result(self, request, operation, value):
u"""
Возвращает результат представления, информирующий
клиента о том, что совершаемая им операция успешно выполненна.
"""
return render_to_response('my_sucess_template.html', {value: value})
def verify_error_result(self, request, operation, err):
u"""
Возвращает результат представления, информирующий
клиента о том, что во время верификации операции произошла ошибка.
"""
return render_to_response('my_error.html', {'err': unicode(err)})
def operation_error_result(self, request, operation, err):
u"""
Возвращает результат представления, информирующий
клиента о том, что операция была успешно верифицированна, однако
соответствующим представлением было выброшено исключение.
"""
return render_to_response('my_error.html', {'err': unicode(err)})
Теперь пакет готов к использованию.
Модуль sinops.operations
Концепция
Концепция работы с данным пакетом предполагает, что любое представление, требующее подписания, должно быть связанно с некоторым экземпляром "operation" класса, инкапсулирующим всю логику работы с ЭЦП начиная от формирования документа до верификации подписи и логирования.
Note
Под "operation" классом, здесь и далее будет пониматься любой потомок класса sinops.operation.SignedOperationBase. В модуле sinops.operation реализовано два "operation" класса SignedOperation и StatefulSignedOperation.
Любой "operation" класс должен реализовывать 3 метода:
- get_client_interface - метод, который возвращает интерфейс взаимодействия с клиентом, необходимый для подписания операции и сообщения об ошибках.
- verify - метод, который должен выполнять верификацию операции
- save - Метод, определяющий как после успешного выполнения операции ее нужно залогировать.
Абстрагируясь от конкретной реализации "operation" классов, рассмотрим сначала примеры их использования.
Пример использования с декоратором
Самый простой способ добавить в представление функциональность запроса подписи - это использовать декоратор sign_required:
from django.http import HttpResponse
from sinops.utils import sign_required
from sinops.operation import SignedOperation
@sign_required(SignedOperation(u'Это сообщение(документ) для подписания'))
def foo_view(request):
# Тут можно быть уверенным что документ подписан и успешно верифицирован.
# Совершаем требуемые действия:
execute_something()
return HttpResponse(u'Операция выполнена успешно!')
В данный декоратор первым аргументом можно также передать callable объект, который вернет экземпляр SignedOperation. Переданный callable будет вызван с тем же набором аргументов что и декорируемое представление:
def get_operation(request, arg1, arg2):
message = (
u'Пользоваетель {0} Вы делаете что то серьезное.'
u'Данное сообщение сформированно {1}'
).format(request.user.username, datetime.now())
return SignedOperation(message)
@sign_required(get_operation)
def bar_view(request, arg1, arg2):
# Тут можно быть уверенным что документ подписан и успешно верифицирован.
# Совершаем требуемые действия:
execute_something()
return HttpResponse(u'Операция выполнена успешно!')
Расширенный пример использования
Приведем пример view с подробными комментариями, демонстрирующее использование "operation" классов, более явно:
# SignedOperation - некоторая реализация SignedOperationBase
from sinops.operation import SignedOperation
def delete_obj_view(request):
u"""
Некоторая view (без особых ограничений можно также
считать что это метод run произвольного экшена)
Для выполнения данной view может понадобиться подписанный
с помощю ЭЦП документ.
"""
response = HttpResponse(u'Удаление успешно выполнено')
obj = get_object(request.REQUEST['object_id'])
if obj in IMPORTANT_OBJECTS:
# удаляется какой-либо важный объект
# Считаем это значимым действием, требующее подписания.
message = (
u'Пользоваетель ПЕТРОВ Вы удаляете объект такой-то'
u' данное сообщение подписано тогда-то'
)
signed_op = SignedOperation(
message,
# Каждая операция должна быть определенного типа.
# Если тип не будет указан, то будет использован тип по умолчанию
operation_type_id=DELETE_IMPORTATN_OBJECT_OPERATION_ID
)
# Перед верификацией signed_op должна быть инициализированна
signed_op.initialize(request)
# Получаем интерфейс взаимодействия с клиентом
interface = signed_op.get_client_interface()
try:
signed_op.verify()
except exceptions.SignatureDoesNotSupplied:
# Ошибка проверки, но только потому что данные ЭЦП
# отсутствуют в параметрах запроса.
# Поэтому вернем респонс который запрашивает ЭЦП
response = interface.ask_signature_result(request, signed_op)
except exceptions.VerificationError as err:
# Ошибка проверки выполняемой операции
# Поэтому вернем респонс который сообщает об ошибке
response = interface.verify_error_result(request, signed_op, err)
else:
# Операция была успешно верифицирована. Выполняем удаление
obj.delete()
# Респонс будет сообщать об успешном выполнении операции
response = interface.operation_completed_result(
request, signed_op, response,
)
else:
# удаляется какой-либо не важный объект
# Это можно сделать без ЭЦП
obj.delete()
return response
Класс sinops.operation.SignedOperation
Основной "operation" класс модуля.
При инстанцировании принимает некоторые данные для подписания и не обязательный аргумент - id типа операции.
По умолчанию использует классы подписей, интерфейс взаимодействия с клиентом и бэкенды верификации ЭЦП задаваемые соответствующими настройками пакета:
SINOPS_SETTINGS = { 'DEFAULT_SIGNATURE_CLASSES': [ 'sinops.signature.cades.CAdESXLT1', ], 'DEFAULT_VERIFIER_CLASSES': [ 'sinops.verification.smev.SMEVVerifier', ], 'DEFAULT_CLIENT_INTERFACE_CLASS': 'sinops.interface.ClientInterfaceBase', }
Класс sinops.operation.StatefulSignedOperation
Operation класс сохраняющий хеш данных для подписания.
Основное отличие данного класса от sinops.operation.SignedOperation состоит в том, что он обладает измененной логикой верификации операции (см. метод .verify_operation()). Так как подписанные клиентом данные могут отличаться от данных которые предполагаются быть подписанными, но при этом все же быть валидными (например, могут отличаются временным маркером содержащимся в сообщении), то это может привести к ошибке верификации. Для обхода этой проблемы, верификация операции в данном клаcсе реализуется не путем сравнения подписанного сообщения и сообщения которое планируется быть подписанным, а несколько иначе:
- При получении данных для подписания, вычисляется хеш от от этих данных и некоторых параметров запроса. Вычисленный хеш сохраняется в сессии.
- При верификации операции, снова вычисляется хеш, но теперь уже от подписанных данных и новых параметров запроса. Сравнивается только что вычисленных хеш и хеш сохраненный в сесси.
Дополнительно, добавлена возможность ограничить время на выполнение подписания. см StatefulSignedOperation.OPERATION_TIMEOUT.
Интерфейс взаимодействия с клиентом
Интерфейс взаимодействия с клиентом - это любой класс наследуемый от абстрактного класса sinops.interface.ClientInterfaceBase.
Назначение данных интерфейс-классов легко понять рассмотрев код базового класса и прочитав докстринги его методов:
class ClientInterfaceBase(object):
u"""Базовый интерфейс для взаимодействия с клиентом."""
def ask_signature_result(self, request, operation):
u"""
Возвращает результат представления, который должен сообщить
клиенту информацию о том, что совершаемая им операция
должна быть подписана с использованием ЭЦП.
:param request: Объект запроса.
:type request: django.http.HttpRequest.
:param operation: Выполняющаяся операция.
:type operation: Потомок sinops.operation.SignedOperationBase.
"""
raise NotImplementedError
def operation_completed_result(self, request, operation, value):
u"""
Возвращает результат представления, информирующий
клиента о том, что совершаемая им операция успешно выполненна.
:param request: Объект зпроса.
:type request: django.http.HttpRequest.
:param operation: Выполняющаяся операция.
:type operation: Потомок sinops.operation.SignedOperationBase.
:param value: Результат выполнения представления.
"""
raise NotImplementedError
def verify_error_result(self, request, operation, err):
u"""
Возвращает результат представления, информирующий
клиента о том, что во время верификации операции произошла ошибка.
.. note::
Если метод не знает тип ошибки, то он должен вернуть None.
:param request: Объект запроса.
:type request: django.http.HttpRequest.
:param operation: Выполняющаяся операция.
:type operation: Потомок sinops.operation.SignedOperationBase.
:param err: Отловленная ошибка верификации.
"""
raise NotImplementedError
def operation_error_result(self, request, operation, err):
u"""
Возвращает результат представления, информирующий
клиента о том, что операция была успешно верифицированна, однако
соответствующим представлением было выброшено исключение.
.. note::
Если метод не знает тип ошибки, то он должен вернуть None.
:param request: Объект запроса.
:type request: django.http.HttpRequest.
:param operation: Выполняющаяся операция.
:type operation: Потомок sinops.operation.SignedOperationBase.
:param err: Отловленная ошибка выполнения представления.
"""
raise NotImplementedError
Бэкенды верификации ЭЦП
Бэкенд верификации ЭЦП - это отдельная сущность, назначение которой проверка валидности ЭЦП.
Note
Несмотря на то что верификация операции - это комплексная проверка, которая проверяет не только ЭЦП, но некоторые другие параметры (см. схема взаимодействия), бекенд верификации ЭЦП, должен верифицировать только ЭЦП сообщения.
Бекенд должен наследовать базовый класс sinops.verification.base.VerifierBase и определять два метода:
- verify - метод должен проверять ЭЦП сообщения. В случае успешного выполнения должен возвращать None, иначе выбрасывать исключение VerificationError.
- is_suitable - classmethod, должен определять может ли данный бэкенд верифицировать подпись signature.
Оба метода принимают единственный аргумент экземпляр класса сигнатуры.
Бэкенд верификации SMEV
Данный бекенд находится в модуле sinops.verification.smev.SMEVVerifier. Бэкенд выполняет проверку отправляя запрос специальному сервису СМЭВ.
Адрес этого сервиса задается настройкой:
SINOPS_SETTINGS = { 'SMEV_VERIFIER_SERVICE_URL': 'http://195.245.214.33:7777/esv/SignatureTool.asmx' }
На данный момент бэкенд поддерживает подпись формата CAdES, определяемую классом сигнатуры sinops.signature.cades.CAdESXLT1, однако, так как СМЭВ сервис может проверять и другие типы подписи, то данный бэкенд может быть легко расширен.
Пример использования
from sinops.signature.cades import CAdESXLT1
from sinops.verification.smev import SMEVVerifier
from sinops.exceptions import VerificationError
signature = CAdESXLT1(open('/signed_message.der', 'rb').read())
verifier = SMEVVerifier()
try:
verifier.verify(signature)
except VerificationError as err:
print u'Подпись неверна ' + err.message
else:
print u'Подпись верна'
Updated