From c59ba3e88a678e004b897702b0ee1f0c077fa98a Mon Sep 17 00:00:00 2001 From: Sergei Borodin Date: Fri, 5 Jul 2024 12:31:37 +0200 Subject: [PATCH] new-feature(auth): BI-5509 custom endpoints for Yandex OAuth (#527) --- lib/dl_auth_api_lib/dl_auth_api_lib/app.py | 9 ++++- .../dl_auth_api_lib/oauth/yandex.py | 34 +++++++++++++++---- .../dl_auth_api_lib/settings.py | 19 ++++------- .../dl_auth_api_lib_tests/conftest.py | 15 +++++--- .../dl_auth_api_lib_tests/unit/test_yandex.py | 4 +++ 5 files changed, 57 insertions(+), 24 deletions(-) diff --git a/lib/dl_auth_api_lib/dl_auth_api_lib/app.py b/lib/dl_auth_api_lib/dl_auth_api_lib/app.py index cc9324960..aebe0e2f2 100644 --- a/lib/dl_auth_api_lib/dl_auth_api_lib/app.py +++ b/lib/dl_auth_api_lib/dl_auth_api_lib/app.py @@ -12,7 +12,11 @@ from dl_api_commons.aio.middlewares.request_id import RequestId from dl_api_commons.aio.middlewares.tracing import TracingService from dl_api_commons.aio.typing import AIOHTTPMiddleware -from dl_auth_api_lib.settings import AuthAPISettings +from dl_auth_api_lib.oauth.yandex import YandexOAuthClient +from dl_auth_api_lib.settings import ( + AuthAPISettings, + register_auth_client, +) from dl_auth_api_lib.views import yandex as yandex_views from dl_core.aio.ping_view import PingView @@ -53,3 +57,6 @@ def create_app(self) -> web.Application: app["clients"] = self._settings.auth_clients return app + + +register_auth_client("yandex", YandexOAuthClient) diff --git a/lib/dl_auth_api_lib/dl_auth_api_lib/oauth/yandex.py b/lib/dl_auth_api_lib/dl_auth_api_lib/oauth/yandex.py index 85d0d237a..c94334406 100644 --- a/lib/dl_auth_api_lib/dl_auth_api_lib/oauth/yandex.py +++ b/lib/dl_auth_api_lib/dl_auth_api_lib/oauth/yandex.py @@ -1,13 +1,32 @@ from base64 import b64encode -from typing import Any +from typing import ( + Any, + Optional, +) import urllib.parse import aiohttp import attr +import pydantic from typing_extensions import Self from dl_auth_api_lib.oauth.base import BaseOAuth -from dl_auth_api_lib.settings import YandexOAuthClient +from dl_auth_api_lib.settings import BaseOAuthClient + + +_AUTH_URL: str = "https://oauth.yandex.ru/authorize?" +_TOKEN_URL: str = "https://oauth.yandex.ru/token" + + +class YandexOAuthClient(BaseOAuthClient): + auth_type: str = "yandex" + + client_id: str + client_secret: str + redirect_uri: str + scope: Optional[str] = pydantic.Field(default=None) + auth_url: str = pydantic.Field(default=_AUTH_URL) + token_url: str = pydantic.Field(default=_TOKEN_URL) @attr.s @@ -16,9 +35,8 @@ class YandexOAuth(BaseOAuth): client_secret: str = attr.ib() redirect_uri: str = attr.ib() scope: str | None = attr.ib(default=None) - - _AUTH_URL: str = "https://oauth.yandex.ru/authorize?" - _TOKEN_URL: str = "https://oauth.yandex.ru/token" + auth_url: str = attr.ib(default=_AUTH_URL) + token_url: str = attr.ib(default=_TOKEN_URL) def get_auth_uri(self) -> str: params = { @@ -27,7 +45,7 @@ def get_auth_uri(self) -> str: "response_type": "code", "scope": self.scope, } - uri = self._AUTH_URL + urllib.parse.urlencode({k: v for k, v in params.items() if v is not None}) + uri = self.auth_url + urllib.parse.urlencode({k: v for k, v in params.items() if v is not None}) return uri async def get_auth_token(self, code: str) -> dict[str, Any]: @@ -38,7 +56,7 @@ async def get_auth_token(self, code: str) -> dict[str, Any]: "grant_type": "authorization_code", "code": code, } - async with session.post(self._TOKEN_URL, data=token_data) as resp: + async with session.post(self.token_url, data=token_data) as resp: token_response = await resp.json() return token_response @@ -49,6 +67,8 @@ def from_settings(cls, settings: YandexOAuthClient) -> Self: client_secret=settings.client_secret, redirect_uri=settings.redirect_uri, scope=settings.scope, + auth_url=settings.auth_url, + token_url=settings.token_url, ) def _get_session_headers(self) -> dict[str, str]: diff --git a/lib/dl_auth_api_lib/dl_auth_api_lib/settings.py b/lib/dl_auth_api_lib/dl_auth_api_lib/settings.py index 9d19c56b7..71ddc6cb3 100644 --- a/lib/dl_auth_api_lib/dl_auth_api_lib/settings.py +++ b/lib/dl_auth_api_lib/dl_auth_api_lib/settings.py @@ -2,7 +2,6 @@ from typing import ( Annotated, Any, - Optional, Type, ) @@ -29,13 +28,14 @@ def factory(cls, data: Any) -> "BaseOAuthClient": return config_class.model_validate(data) -class YandexOAuthClient(BaseOAuthClient): - auth_type: str = "yandex" +_REGISTRY: dict[str, type[BaseOAuthClient]] = {} - client_id: str - client_secret: str - redirect_uri: str - scope: Optional[str] = pydantic.Field(default=None) + +def register_auth_client( + name: str, + auth_type: type[BaseOAuthClient], +) -> None: + _REGISTRY[name] = auth_type class AuthAPISettings(pydantic_settings.BaseSettings): @@ -63,8 +63,3 @@ def settings_customise_sources( ), init_settings, ) - - -_REGISTRY = { - "yandex": YandexOAuthClient, -} diff --git a/lib/dl_auth_api_lib/dl_auth_api_lib_tests/conftest.py b/lib/dl_auth_api_lib/dl_auth_api_lib_tests/conftest.py index def6cf345..581ecd41b 100644 --- a/lib/dl_auth_api_lib/dl_auth_api_lib_tests/conftest.py +++ b/lib/dl_auth_api_lib/dl_auth_api_lib_tests/conftest.py @@ -12,10 +12,8 @@ ) from dl_api_commons.client.common import DLCommonAPIClient from dl_auth_api_lib.app import OAuthApiAppFactory -from dl_auth_api_lib.settings import ( - AuthAPISettings, - YandexOAuthClient, -) +from dl_auth_api_lib.oauth.yandex import YandexOAuthClient +from dl_auth_api_lib.settings import AuthAPISettings from dl_testing.utils import get_default_aiohttp_session @@ -66,6 +64,15 @@ def oauth_app_settings(monkeypatch, config_path): client_id="app_metrica", redirect_uri="localhost", ), + custom_conn=dict( + auth_type="yandex", + conn_type="custom_conn", + client_id="custom_conn", + client_secret="pass321", + redirect_uri="localhost", + auth_url="https://oauth.yandex.com/authorize?", + token_url="https://oauth.yandex.com/token", + ), ) ) yield settings diff --git a/lib/dl_auth_api_lib/dl_auth_api_lib_tests/unit/test_yandex.py b/lib/dl_auth_api_lib/dl_auth_api_lib_tests/unit/test_yandex.py index d88f8e10e..b9f1c7a88 100644 --- a/lib/dl_auth_api_lib/dl_auth_api_lib_tests/unit/test_yandex.py +++ b/lib/dl_auth_api_lib/dl_auth_api_lib_tests/unit/test_yandex.py @@ -15,6 +15,10 @@ "app_metrica", "https://oauth.yandex.ru/authorize?client_id=app_metrica&redirect_uri=localhost&response_type=code", ), + ( + "custom_conn", + "https://oauth.yandex.com/authorize?client_id=custom_conn&redirect_uri=localhost&response_type=code", + ), ], ) async def test_yandex_uri(oauth_app_client, conn_type, resp_uri):