Skip to content
Snippets Groups Projects
Unverified Commit 1591d253 authored by Georgiy Eterevskiy's avatar Georgiy Eterevskiy Committed by GitHub
Browse files

Merge pull request #3 from perxteam/feature/auth

Добавлен плагин аутентификации
parents 1ab3eb45 3fd99447
No related branches found
No related tags found
No related merge requests found
[flake8]
max-line-length = 120
inline-quotes = single
multiline-quotes = double
avoid-escape = False
exclude = .git,.tox,.env,.github,.pytest_cache,__pycache__,files,dist,build,*.egg-info,*_pb2.py,*_pb2_grpc.py
\ No newline at end of file
......@@ -10,6 +10,7 @@ help:
@echo "clean-pyc - remove Python file artifacts"
@echo "clean-test - remove test and coverage artifacts"
@echo "clean-proto - remove generated gRPC code"
@echo "lint - check python code by flake8"
@echo "generate - generate gRPC code"
@echo "dist - package"
@echo "release - package and upload a release"
......@@ -42,6 +43,9 @@ clean-proto:
install-requirements:
pip install -r build-requirements.txt
lint: install-requirements
flake8 perxis/
generate: clean-proto install-requirements
find $(PROTO_FILES_DIR) -name '*.proto' -exec python3 -m grpc_tools.protoc -I${PROTO_FILES_DIR} --python_out=$(OUTPUT_FILES_DIR) --grpc_python_out=$(OUTPUT_FILES_DIR) {} +
......
# perxis-python
## Аутентификация
gRPC Python предоставляет способ перехвата RPC и добавления метаданных,
связанных с аутентификацией, через AuthMetadataPlugin.
Те, кому нужен специальный метод аутентификации, могут просто предоставить конкретную реализацию следующего интерфейса:
```python
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 напрямую, но мы рекомендуем наследовать от базового класса,
чтобы гарантировать правильность реализации.
```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, например:
```python
call_credentials = grpc.metadata_call_credentials(my_foo_plugin)
stub.FooRpc(request, credentials=call_credentials)
```
### Пример авторизации и аутентификации OAuth2
```python
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)
```
......@@ -7,18 +7,27 @@ devpi-client==5.2.2
devpi-common==3.6.0
distlib==0.3.1
filelock==3.0.12
flake8==3.9.0
grpcio==1.36.1
grpcio-tools==1.36.1
idna==2.10
importlib-metadata==3.10.0
lazy==1.4
mccabe==0.6.1
packaging==20.9
pep517==0.9.1
pkginfo==1.7.0
pluggy==0.13.1
protobuf==3.15.7
py==1.10.0
pycodestyle==2.7.0
pyflakes==2.3.1
pyparsing==2.4.7
requests==2.25.1
six==1.15.0
toml==0.10.2
tox==3.23.0
typing-extensions==3.7.4.3
urllib3==1.26.3
virtualenv==20.4.2
zipp==3.4.1
import time
from typing import Type
import grpc
from oauthlib.oauth2 import Client, OAuth2Token
from requests_oauthlib import OAuth2Session
class OAuth2Plugin(grpc.AuthMetadataPlugin):
_token = None
def __init__(self, client: Type[Client], client_secret: str, token_url: str, audience: str,
signature_header_key: str = 'Authorization', token_type: str = 'Bearer') -> None:
self._client_secret = client_secret
self._token_url = token_url
self._audience = audience
self._signature_header_key = signature_header_key
self._token_type = token_type
self.oauth2 = OAuth2Session(client=client)
def __call__(self, context: grpc.AuthMetadataContext,
callback: grpc.AuthMetadataPluginCallback) -> None:
callback(((self._signature_header_key, f'{self._token_type} {self.token["access_token"]}'),), None)
@property
def token(self) -> OAuth2Token:
def fetch_token() -> OAuth2Token:
return self.oauth2.fetch_token(
token_url=self._token_url,
client_secret=self._client_secret,
audience=self._audience
)
def refresh_token() -> OAuth2Token:
return self.oauth2.refresh_token(
token_url=self._token_url,
client_secret=self._client_secret,
audience=self._audience
)
if self._token is None:
self._token = fetch_token()
if self._token.expires_at and self._token.expires_at < time.time():
if 'refresh_token' in self._token:
self._token = refresh_token()
else:
self._token = fetch_token()
return self._token
certifi==2020.12.5
chardet==4.0.0
grpcio==1.36.1
idna==2.10
oauthlib==3.1.0
protobuf==3.15.4
PyJWT==2.0.1
requests==2.25.1
requests-oauthlib==1.3.0
six==1.15.0
urllib3==1.26.4
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment