Skip to content
Snippets Groups Projects
Maxim Podosochnyy's avatar
Podosochnyy Maxim authored
dae36cef

perxis-python

Работа с perxis

В модуле perxis.provider есть классы:

  1. PerxisItemsWrapper - является обёрткой над grpc, реализует внутри себя логику для работы с items. Экземпляр ItemsStub ожидается внешний, поэтому класс можно использовать, например, при разработке расширений
  2. PerxisReferencesWrapper - является обёрткой над grpc, реализует внутри себя логику для работы с references. Экземпляр ReferencesStub ожидается внешний, поэтому класс можно использовать, например, при разработке расширений
  3. PerxisFilesWrapper - обёртка над grpc для работы с файлами. Экземпляр FilesStub ожидается внешний, класс можно использовать при разработке расширений
  4. PerxisDataProvider - Провайдер данных для работы с items и references для конкретного пространства и окружения. Сама логика работы реализована в классах PerxisItemsWrapper и PerxisReferencesWrapper, экземпляр PerxisDataProvider создаёт stub'ы сервисов на базе переданного при инициализации канала. Данный класс можно использовать во внешних приложениях. Использовать его при разработке расширений не получится так как класс подразумевает динамическое создние stub'ов
  5. 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)