From 15e7fd51edf26883885f035760308321de752dcf Mon Sep 17 00:00:00 2001 From: Grigoriev Semyon <33061489+grigoriev-semyon@users.noreply.github.com> Date: Wed, 10 May 2023 02:26:02 +0300 Subject: [PATCH] Fixes (#29) --- Makefile | 12 ++++++++++ auth_lib/aiomethods.py | 42 +++++++++++++++-------------------- auth_lib/fastapi.py | 36 +++++++++++++----------------- auth_lib/methods.py | 24 +++++++++----------- auth_lib/testing/__init__.py | 5 ++++- auth_lib/testing/testutils.py | 6 ++--- requirements.dev.txt | 1 + setup.py | 2 +- 8 files changed, 66 insertions(+), 62 deletions(-) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..788bca3 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +configure: venv + source ./venv/bin/activate && pip install -r requirements.dev.txt + +venv: + python3.11 -m venv venv + +format: + autoflake -r --in-place --remove-all-unused-imports ./auth_lib + isort ./auth_lib + black ./auth_lib + + diff --git a/auth_lib/aiomethods.py b/auth_lib/aiomethods.py index f80286c..26994b5 100644 --- a/auth_lib/aiomethods.py +++ b/auth_lib/aiomethods.py @@ -1,8 +1,9 @@ from typing import Any +from urllib.parse import urljoin import aiohttp -from .exceptions import AuthFailed, IncorrectData, NotFound, SessionExpired +from .exceptions import AuthFailed, SessionExpired # See docs on https://api.test.profcomff.com/?urls.primaryName=auth @@ -23,30 +24,23 @@ async def email_login(self, email: str, password: str) -> dict[str, Any]: case 401: raise AuthFailed(response=await response.json()) - async def check_token(self, token: str) -> dict[str, Any]: + async def check_token(self, token: str) -> dict[str, Any] | None: headers = {"Authorization": token} - async with aiohttp.ClientSession() as session: - response = await session.get( - url=f"{self.url}/me", - headers=headers, - params={ - "info": [ - "groups", - "indirect_groups", - "session_scopes", - "user_scopes", - ] - }, - ) - match response.status: - case 200: - return await response.json() - case 400: - raise IncorrectData(response=await response.json()) - case 404: - raise NotFound(response=await response.json()) - case 403: - raise SessionExpired(response=await response.json()) + async with aiohttp.request( + "GET", + urljoin(self.url, "me"), + headers={"Authorization": token}, + params={ + "info": [ + "indirect_groups", + "session_scopes", + ] + }, + ) as r: + user_session = await r.json() + if r.ok: + return user_session + return None async def logout(self, token: str) -> bool: headers = {"Authorization": token} diff --git a/auth_lib/fastapi.py b/auth_lib/fastapi.py index e79d210..386db29 100644 --- a/auth_lib/fastapi.py +++ b/auth_lib/fastapi.py @@ -1,13 +1,15 @@ -from urllib.parse import urljoin +from typing import Any from warnings import warn -import aiohttp from fastapi.exceptions import HTTPException from fastapi.openapi.models import APIKey, APIKeyIn from fastapi.security.base import SecurityBase from pydantic import BaseSettings from starlette.requests import Request from starlette.status import HTTP_403_FORBIDDEN +from starlette.websockets import WebSocket + +from auth_lib.aiomethods import AsyncAuthLib class UnionAuthSettings(BaseSettings): @@ -57,29 +59,23 @@ def _except(self): status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" ) else: - return {} + return None - async def __call__( - self, - request: Request, - ) -> dict[str, str] | None: - token = request.headers.get("Authorization") + async def _get_session(self, token: str | None) -> dict[str, Any] | None: if not token and self.allow_none: return None if not token: return self._except() - async with aiohttp.request( - "GET", - urljoin(self.auth_url, "me"), - headers={"Authorization": token}, - params={ - "info": ["groups", "indirect_groups", "session_scopes", "user_scopes"] - }, - ) as r: - status_code = r.status - user_session = await r.json() - if status_code != 200: - self._except() + return await AsyncAuthLib(url=self.auth_url).check_token(token) + + async def __call__( + self, + request: Request | WebSocket, + ) -> dict[str, Any] | None: + token = request.headers.get("Authorization") + user_session = await self._get_session(token) + if user_session is None: + return self._except() session_scopes = set( [scope["name"].lower() for scope in user_session["session_scopes"]] ) diff --git a/auth_lib/methods.py b/auth_lib/methods.py index 72c4681..f9b9c02 100644 --- a/auth_lib/methods.py +++ b/auth_lib/methods.py @@ -1,8 +1,9 @@ from typing import Any +from urllib.parse import urljoin import requests -from .exceptions import AuthFailed, IncorrectData, NotFound, SessionExpired +from .exceptions import AuthFailed, SessionExpired # See docs on https://api.test.profcomff.com/?urls.primaryName=auth @@ -22,24 +23,21 @@ def email_login(self, email: str, password: str) -> dict[str, Any]: case 401: raise AuthFailed(response=response.json()["body"]) - def check_token(self, token: str) -> dict[str, Any]: + def check_token(self, token: str) -> dict[str, Any] | None: headers = {"Authorization": token} response = requests.get( - url=f"{self.url}/me", + url=urljoin(self.url, "me"), headers=headers, params={ - "info": ["groups", "indirect_groups", "session_scopes", "user_scopes"] + "info": [ + "indirect_groups", + "session_scopes", + ] }, ) - match response.status_code: - case 200: - return response.json() - case 400: - raise IncorrectData(response=response.json()["body"]) - case 404: - raise NotFound(response=response.json()["body"]) - case 403: - raise SessionExpired(response=response.json()["body"]) + if response.ok: + return response.json() + return None def logout(self, token: str) -> bool: headers = {"Authorization": token} diff --git a/auth_lib/testing/__init__.py b/auth_lib/testing/__init__.py index 565e213..c1e0b8a 100644 --- a/auth_lib/testing/__init__.py +++ b/auth_lib/testing/__init__.py @@ -1,5 +1,8 @@ try: from auth_lib.testing.testutils import auth_mock, pytest_configure -except ImportError: +except ImportError as e: print("You have to install testing requirements") print("pip install 'auth-lib-profcomff[testing]'") + raise e + +__all__ = ["auth_mock", "pytest_configure"] diff --git a/auth_lib/testing/testutils.py b/auth_lib/testing/testutils.py index 6ed1ae5..78b7b8a 100644 --- a/auth_lib/testing/testutils.py +++ b/auth_lib/testing/testutils.py @@ -1,4 +1,4 @@ -from unittest.mock import MagicMock, patch +from unittest.mock import AsyncMock, patch import pytest @@ -25,8 +25,8 @@ def auth_mock(request): "user_scopes": session_scopes, } patcher = patch( - "auth_lib.fastapi.UnionAuth.__call__", - new=MagicMock(return_value=_return_val), + "auth_lib.fastapi.UnionAuth._get_session", + new=AsyncMock(return_value=_return_val), ) patcher.start() yield diff --git a/requirements.dev.txt b/requirements.dev.txt index 8dd1e2d..3cb9e29 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -2,5 +2,6 @@ fastapi starlette requests aiohttp +autoflake setuptools pydantic diff --git a/setup.py b/setup.py index 11d08b3..8e14964 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name="auth_lib_profcomff", - version="2023.04.23", + version="2023.05.10", author="Semyon Grigoriev", long_description=readme, long_description_content_type="text/markdown",