From 6348b71b712d8e7e4c424389b7e60cf3213f6723 Mon Sep 17 00:00:00 2001 From: dudi levy <4785835+dudil@users.noreply.github.com> Date: Fri, 6 Oct 2023 14:04:52 +0300 Subject: [PATCH 1/3] Remove linting version specific --- pyproject.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index dc4baca..8535ff0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,12 +69,10 @@ test = "pytest {args:tests}" all = ["style", "typing"] [tool.black] -target-version = ["py39"] line-length = 122 skip-string-normalization = true [tool.ruff] -target-version = "py39" line-length = 122 select = [ "A", From 1f10a5617a3cab20216d60b3b79e8a864a049c63 Mon Sep 17 00:00:00 2001 From: dudi levy <4785835+dudil@users.noreply.github.com> Date: Sat, 7 Oct 2023 17:51:12 +0300 Subject: [PATCH 2/3] migration to Pydantic 2 --- fastapi_msal/clients/async_conf_client.py | 6 +++--- fastapi_msal/core/msal_client_config.py | 7 ++++--- fastapi_msal/core/session_manager.py | 4 ++-- fastapi_msal/models/base_auth_model.py | 2 +- pyproject.toml | 2 +- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/fastapi_msal/clients/async_conf_client.py b/fastapi_msal/clients/async_conf_client.py index 166ca6f..120b879 100644 --- a/fastapi_msal/clients/async_conf_client.py +++ b/fastapi_msal/clients/async_conf_client.py @@ -42,14 +42,14 @@ async def __execute_async__(func: Callable[..., T], **kwargs: Any) -> T: def decode_id_token(id_token: str) -> Optional[IDTokenClaims]: decoded: OptStrsDict = json.loads(oidc.decode_part(id_token.split(".")[1])) if decoded: - return IDTokenClaims.parse_obj(decoded) + return IDTokenClaims.model_validate(decoded) return None async def validate_id_token(self, id_token: str, nonce: OptStr = None) -> IDTokenClaims: token_claims: OptStrsDict = await self.__execute_async__( self._cca.client.decode_id_token, id_token=id_token, nonce=nonce ) - return IDTokenClaims.parse_obj(token_claims) + return IDTokenClaims.model_validate(token_claims) async def get_application_token(self, claims_challenge: OptStrsDict = None) -> AuthToken: token: StrsDict = await self.__execute_async__( @@ -117,7 +117,7 @@ async def acquire_token_silent( token = await self.__execute_async__( self._cca.acquire_token_silent, scopes=self.client_config.scopes, - account=(account.dict(exclude_none=True) if account else None), + account=(account.model_dump(exclude_none=True) if account else None), authority=authority, force_refresh=force_refresh, claims_challenge=claims_challenge, diff --git a/fastapi_msal/core/msal_client_config.py b/fastapi_msal/core/msal_client_config.py index cfd9a8f..9969b93 100644 --- a/fastapi_msal/core/msal_client_config.py +++ b/fastapi_msal/core/msal_client_config.py @@ -1,8 +1,9 @@ from enum import Enum +from typing import ClassVar -from pydantic import BaseSettings +from pydantic_settings import BaseSettings -from .utils import OptStr, StrList +from .utils import OptStr class MSALPolicies(str, Enum): @@ -23,7 +24,7 @@ class MSALClientConfig(BaseSettings): # Optional to set, see MSALPolicies for different options, default is single AAD (B2B) policy: MSALPolicies = MSALPolicies.AAD_SINGLE # Optional to set - If you are unsure don't set - it will be filled by MSAL as required - scopes: StrList = [] + scopes: ClassVar[list[str]] = [] # Not in use - for future support session_type: str = "filesystem" diff --git a/fastapi_msal/core/session_manager.py b/fastapi_msal/core/session_manager.py index 4d5d8fb..9957fbc 100644 --- a/fastapi_msal/core/session_manager.py +++ b/fastapi_msal/core/session_manager.py @@ -71,7 +71,7 @@ def save(self, model: M) -> None: if session is None: msg = "No session id, (Make sure you initialized the session by calling init_session)" raise OSError(msg) - session.update({model.__repr_name__(): model.json(exclude_none=True, by_alias=True)}) + session.update({model.__repr_name__(): model.model_dump_json(exclude_none=True, by_alias=True)}) self._write_session(session=session) def load(self, model_cls: type[M]) -> Optional[M]: @@ -79,7 +79,7 @@ def load(self, model_cls: type[M]) -> Optional[M]: if session: raw_model: OptStr = session.get(model_cls.__name__, None) if raw_model: - return model_cls.parse_raw(raw_model) + return model_cls.model_validate_json(raw_model) return None def clear(self) -> None: diff --git a/fastapi_msal/models/base_auth_model.py b/fastapi_msal/models/base_auth_model.py index 7c93d42..994eca0 100644 --- a/fastapi_msal/models/base_auth_model.py +++ b/fastapi_msal/models/base_auth_model.py @@ -12,7 +12,7 @@ class BaseAuthModel(BaseModel): @classmethod def parse_obj_debug(cls: type[AuthModel], to_parse: StrsDict) -> AuthModel: - debug_model: AuthModel = cls.parse_obj(obj=to_parse) + debug_model: AuthModel = cls.model_validate(obj=to_parse) debug_model.__setattr__("_recieved", to_parse) return debug_model diff --git a/pyproject.toml b/pyproject.toml index 8535ff0..13c849e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,8 +30,8 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", ] -dependencies = ["pydantic==1.*", "starlette", "fastapi", "msal"] dynamic = ["version"] +dependencies = ["pydantic>=2.4", "pydantic_settings>=2.0", "starlette", "fastapi", "msal"] [project.urls] Homepage = "https://github.com/dudil/fastapi_msal" From fe0f9036eaaa10d0f5019ed88d49cb627ca9bd9b Mon Sep 17 00:00:00 2001 From: dudi levy <4785835+dudil@users.noreply.github.com> Date: Sat, 7 Oct 2023 19:57:47 +0300 Subject: [PATCH 3/3] update dump and validate pydantic API --- fastapi_msal/clients/async_conf_client.py | 6 +++--- fastapi_msal/core/msal_client_config.py | 6 +++--- fastapi_msal/core/session_manager.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fastapi_msal/clients/async_conf_client.py b/fastapi_msal/clients/async_conf_client.py index 120b879..3233036 100644 --- a/fastapi_msal/clients/async_conf_client.py +++ b/fastapi_msal/clients/async_conf_client.py @@ -92,14 +92,14 @@ async def initiate_auth_flow( async def finalize_auth_flow(self, auth_code_flow: AuthCode, auth_response: AuthResponse) -> AuthToken: auth_token: StrsDict = await self.__execute_async__( self._cca.acquire_token_by_auth_code_flow, - auth_code_flow=auth_code_flow.dict(exclude_none=True), - auth_response=auth_response.dict(exclude_none=True), + auth_code_flow=auth_code_flow.model_dump(exclude_none=True), + auth_response=auth_response.model_dump(exclude_none=True), scopes=self.client_config.scopes, ) return AuthToken.parse_obj_debug(to_parse=auth_token) async def remove_account(self, account: LocalAccount) -> None: - await self.__execute_async__(self._cca.remove_account, account=account.dict(exclude_none=True)) + await self.__execute_async__(self._cca.remove_account, account=account.model_dump(exclude_none=True)) async def get_accounts(self, username: OptStr = None) -> list[LocalAccount]: accounts_objects: list[StrsDict] = await self.__execute_async__(self._cca.get_accounts, username=username) diff --git a/fastapi_msal/core/msal_client_config.py b/fastapi_msal/core/msal_client_config.py index 9969b93..fd6abbe 100644 --- a/fastapi_msal/core/msal_client_config.py +++ b/fastapi_msal/core/msal_client_config.py @@ -17,9 +17,9 @@ class MSALPolicies(str, Enum): class MSALClientConfig(BaseSettings): # The following params must be set according to the app registration data recieved from AAD # https://docs.microsoft.com/azure/active-directory/develop/quickstart-v2-register-an-app - client_id: OptStr - client_credential: OptStr - tenant: OptStr + client_id: OptStr = None + client_credential: OptStr = None + tenant: OptStr = None # Optional to set, see MSALPolicies for different options, default is single AAD (B2B) policy: MSALPolicies = MSALPolicies.AAD_SINGLE diff --git a/fastapi_msal/core/session_manager.py b/fastapi_msal/core/session_manager.py index 9957fbc..d2da66a 100644 --- a/fastapi_msal/core/session_manager.py +++ b/fastapi_msal/core/session_manager.py @@ -71,7 +71,7 @@ def save(self, model: M) -> None: if session is None: msg = "No session id, (Make sure you initialized the session by calling init_session)" raise OSError(msg) - session.update({model.__repr_name__(): model.model_dump_json(exclude_none=True, by_alias=True)}) + session.update({model.__repr_name__(): model.model_dump_json(exclude_none=True, by_alias=True)}) # type: ignore self._write_session(session=session) def load(self, model_cls: type[M]) -> Optional[M]: