perxis-python
Работа с perxis
В модуле perxis.provider
есть классы:
-
PerxisItemsWrapper
- является обёрткой надgrpc
, реализует внутри себя логику для работы сitems
. ЭкземплярItemsStub
ожидается внешний, поэтому класс можно использовать, например, при разработке расширений -
PerxisReferencesWrapper
- является обёрткой надgrpc
, реализует внутри себя логику для работы сreferences
. ЭкземплярReferencesStub
ожидается внешний, поэтому класс можно использовать, например, при разработке расширений -
PerxisFilesWrapper
- обёртка надgrpc
для работы с файлами. ЭкземплярFilesStub
ожидается внешний, класс можно использовать при разработке расширений -
PerxisDataProvider
- Провайдер данных для работы сitems
иreferences
для конкретного пространства и окружения. Сама логика работы реализована в классахPerxisItemsWrapper
иPerxisReferencesWrapper
, экземплярPerxisDataProvider
создаётstub'ы
сервисов на базе переданного при инициализации канала. Данный класс можно использовать во внешних приложениях. Использовать его при разработке расширений не получится так как класс подразумевает динамическое создниеstub'ов
-
PerxisFileProvider
- Провайдер для работы с файлами, создающийFilesStub
на базе переданного при инициализации канала. Внутри себя задействует экземплярPerxisFilesWrapper
Расширения
Локальная разработка
Для работы расширения требуется указание системного контекста при выполнении запросов. Это возможно только в случае прямой работы с сервисами perxis минуя envoy, для этого они должны быть в одной сети. Поэтому нужен локально запущенный экземпляр perxis. Для его запуска нужно в каталоге perxis выполнить команду make run-local. После этого в контейнере в той же сети можно запустить сервис с расширением
Пример написания сервиса расширений
Готовый пример с реализацией простого сервиса можно посмотреть в каталоге examples/extension_service
Аутентификация
gRPC Python предоставляет способ перехвата RPC и добавления метаданных, связанных с аутентификацией, через AuthMetadataPlugin. Те, кому нужен специальный метод аутентификации, могут просто предоставить конкретную реализацию следующего интерфейса:
class AuthMetadataPlugin:
"""A specification for custom authentication."""
def __call__(self, context, callback):
"""Implements authentication by passing metadata to a callback.
Implementations of this method must not block.
Args:
context: An AuthMetadataContext providing information on the RPC that
the plugin is being called to authenticate.
callback: An AuthMetadataPluginCallback to be invoked either
synchronously or asynchronously.
"""
Затем передайте экземпляр конкретной реализации в функцию grpc.metadata_call_credentials, которая будет преобразована в объект CallCredentials.
ОБРАТИТЕ ВНИМАНИЕ, что можно передать объект функции Python напрямую, но мы рекомендуем наследовать от базового класса, чтобы гарантировать правильность реализации.
def metadata_call_credentials(metadata_plugin, name=None):
"""Construct CallCredentials from an AuthMetadataPlugin.
Args:
metadata_plugin: An AuthMetadataPlugin to use for authentication.
name: An optional name for the plugin.
Returns:
A CallCredentials.
"""
Объект CallCredentials можно передать непосредственно в RPC, например:
call_credentials = grpc.metadata_call_credentials(my_foo_plugin)
stub.FooRpc(request, credentials=call_credentials)
Пример авторизации и аутентификации OAuth2
import grpc
from oauthlib.oauth2 import Client
from oauthlib.oauth2.rfc6749.parameters import prepare_token_request
from auth import OAuth2Plugin
from users.users_pb2 import GetRequest
from users.users_pb2_grpc import UsersStub
# Могут быть использованы как встроенные клиенты, такие как WebApplicationClient, BackendApplicationClient,
# так и допускается реализовать собственный класс с кастомным поведением
class ExtendedClient(Client):
def __init__(self, client_id, grant_type, username, password, **kwargs):
self.grant_type = grant_type
self.username = username
self.password = password
super(ExtendedClient, self).__init__(client_id, **kwargs)
def prepare_request_body(self, body='', scope=None, include_client_id=False, **kwargs):
kwargs['client_id'] = self.client_id
kwargs['include_client_id'] = include_client_id
return prepare_token_request(self.grant_type, body=body, username=self.username,
password=self.password, scope=scope, **kwargs)
oauth2_plugin = OAuth2Plugin(
client=ExtendedClient(client_id='client_id', grant_type='password', username='user', password='pass'),
client_secret='client_secret',
token_url='https://example.com/oauth/token',
audience='audience'
)
call_credentials = grpc.metadata_call_credentials(oauth2_plugin)
with grpc.insecure_channel('localhost:50051') as channel:
request = GetRequest(user_id=1)
stub = UsersStub(channel)
stub.Get(request, credentials=call_credentials)