diff --git a/fastapi_users/authentication/__init__.py b/fastapi_users/authentication/__init__.py index f80827a9..d2aa3529 100644 --- a/fastapi_users/authentication/__init__.py +++ b/fastapi_users/authentication/__init__.py @@ -43,7 +43,7 @@ class Authenticator: def __init__( self, backends: Sequence[BaseAuthentication], - get_user_manager: UserManagerDependency[models.UD], + get_user_manager: UserManagerDependency[models.UC, models.UD], ): self.backends = backends self.get_user_manager = get_user_manager @@ -108,7 +108,7 @@ class Authenticator: async def _authenticate( self, *args, - user_manager: UserManager[models.UD], + user_manager: UserManager[models.UC, models.UD], optional: bool = False, active: bool = False, verified: bool = False, diff --git a/fastapi_users/authentication/base.py b/fastapi_users/authentication/base.py index 2d780ee1..a8b9fe4a 100644 --- a/fastapi_users/authentication/base.py +++ b/fastapi_users/authentication/base.py @@ -9,7 +9,7 @@ from fastapi_users.manager import UserManager T = TypeVar("T") -class BaseAuthentication(Generic[T]): +class BaseAuthentication(Generic[T, models.UC, models.UD]): """ Base authentication backend. @@ -28,7 +28,7 @@ class BaseAuthentication(Generic[T]): self.logout = logout async def __call__( - self, credentials: Optional[T], user_manager: UserManager[models.UD] + self, credentials: Optional[T], user_manager: UserManager[models.UC, models.UD] ) -> Optional[models.UD]: raise NotImplementedError() diff --git a/fastapi_users/authentication/cookie.py b/fastapi_users/authentication/cookie.py index e2174dca..baf8fb81 100644 --- a/fastapi_users/authentication/cookie.py +++ b/fastapi_users/authentication/cookie.py @@ -1,4 +1,4 @@ -from typing import Any, List, Optional +from typing import Any, Generic, List, Optional import jwt from fastapi import Response @@ -11,7 +11,9 @@ from fastapi_users.jwt import SecretType, decode_jwt, generate_jwt from fastapi_users.manager import UserManager, UserNotExists -class CookieAuthentication(BaseAuthentication[str]): +class CookieAuthentication( + Generic[models.UC, models.UD], BaseAuthentication[str, models.UC, models.UD] +): """ Authentication backend using a cookie. @@ -67,7 +69,7 @@ class CookieAuthentication(BaseAuthentication[str]): async def __call__( self, credentials: Optional[str], - user_manager: UserManager[models.UD], + user_manager: UserManager[models.UC, models.UD], ) -> Optional[models.UD]: if credentials is None: return None diff --git a/fastapi_users/authentication/jwt.py b/fastapi_users/authentication/jwt.py index 7315431e..c0020a84 100644 --- a/fastapi_users/authentication/jwt.py +++ b/fastapi_users/authentication/jwt.py @@ -1,4 +1,4 @@ -from typing import Any, List, Optional +from typing import Any, Generic, List, Optional import jwt from fastapi import Response @@ -11,7 +11,9 @@ from fastapi_users.jwt import SecretType, decode_jwt, generate_jwt from fastapi_users.manager import UserManager, UserNotExists -class JWTAuthentication(BaseAuthentication[str]): +class JWTAuthentication( + Generic[models.UC, models.UD], BaseAuthentication[str, models.UC, models.UD] +): """ Authentication backend using a JWT in a Bearer header. @@ -44,7 +46,7 @@ class JWTAuthentication(BaseAuthentication[str]): async def __call__( self, credentials: Optional[str], - user_manager: UserManager[models.UD], + user_manager: UserManager[models.UC, models.UD], ) -> Optional[models.UD]: if credentials is None: return None diff --git a/fastapi_users/fastapi_users.py b/fastapi_users/fastapi_users.py index a13fd913..44384a4d 100644 --- a/fastapi_users/fastapi_users.py +++ b/fastapi_users/fastapi_users.py @@ -34,8 +34,6 @@ class FastAPIUsers(Generic[models.U, models.UC, models.UU, models.UD]): :param user_create_model: Pydantic model for creating a user. :param user_update_model: Pydantic model for updating a user. :param user_db_model: Pydantic model of a DB representation of a user. - :param validate_password: Optional function to validate the password - at user registration, user update or password reset. :attribute get_user_manager: Dependency callable getter to inject the user manager class instance. @@ -58,12 +56,11 @@ class FastAPIUsers(Generic[models.U, models.UC, models.UU, models.UD]): user_create_model: Type[models.UC], user_update_model: Type[models.UU], user_db_model: Type[models.UD], - validate_password: Optional[ValidatePasswordProtocol] = None, ): def get_user_manager( user_db: BaseUserDatabase[models.UD] = Depends(get_db), ): - return UserManager(user_db_model, user_db, validate_password) + return UserManager(user_db_model, user_db) self.authenticator = Authenticator(auth_backends, get_user_manager) @@ -73,8 +70,6 @@ class FastAPIUsers(Generic[models.U, models.UC, models.UU, models.UD]): self._user_update_model = user_update_model self._user_db_model = user_db_model - self.validate_password = validate_password - self.get_user_manager = get_user_manager self.current_user = self.authenticator.current_user diff --git a/fastapi_users/manager.py b/fastapi_users/manager.py index 257beda4..32088c83 100644 --- a/fastapi_users/manager.py +++ b/fastapi_users/manager.py @@ -42,21 +42,18 @@ class ValidatePasswordProtocol(Protocol): # pragma: no cover pass -class UserManager(Generic[models.UD]): +class UserManager(Generic[models.UC, models.UD]): user_db_model: Type[models.UD] user_db: BaseUserDatabase[models.UD] - validate_password: Optional[ValidatePasswordProtocol] def __init__( self, user_db_model: Type[models.UD], user_db: BaseUserDatabase[models.UD], - validate_password: Optional[ValidatePasswordProtocol] = None, ): self.user_db_model = user_db_model self.user_db = user_db - self.validate_password = validate_password async def get(self, id: UUID4) -> models.UD: user = await self.user_db.get(id) @@ -83,8 +80,7 @@ class UserManager(Generic[models.UD]): return user async def create(self, user: models.UC, safe: bool = False) -> models.UD: - if self.validate_password: - await self.validate_password(user.password, user) + await self.validate_password(user.password, user) existing_user = await self.user_db.get_by_email(user.email) if existing_user is not None: @@ -116,6 +112,11 @@ class UserManager(Generic[models.UD]): async def delete(self, user: models.UD) -> None: await self.user_db.delete(user) + async def validate_password( + self, password: str, user: Union[models.UC, models.UD] + ) -> None: + return + async def authenticate( self, credentials: OAuth2PasswordRequestForm ) -> Optional[models.UD]: @@ -154,8 +155,7 @@ class UserManager(Generic[models.UD]): user.email = update_dict[field] elif field == "password": password = update_dict[field] - if self.validate_password: - await self.validate_password(password, user) + await self.validate_password(password, user) hashed_password = get_password_hash(password) user.hashed_password = hashed_password else: @@ -164,4 +164,4 @@ class UserManager(Generic[models.UD]): return updated_user -UserManagerDependency = Callable[..., UserManager[models.UD]] +UserManagerDependency = Callable[..., UserManager[models.UC, models.UD]] diff --git a/fastapi_users/router/auth.py b/fastapi_users/router/auth.py index 321856b1..be3e28c2 100644 --- a/fastapi_users/router/auth.py +++ b/fastapi_users/router/auth.py @@ -9,7 +9,7 @@ from fastapi_users.router.common import ErrorCode def get_auth_router( backend: BaseAuthentication, - get_user_manager: UserManagerDependency[models.UD], + get_user_manager: UserManagerDependency[models.UC, models.UD], authenticator: Authenticator, requires_verification: bool = False, ) -> APIRouter: @@ -23,7 +23,7 @@ def get_auth_router( async def login( response: Response, credentials: OAuth2PasswordRequestForm = Depends(), - user_manager: UserManager[models.UD] = Depends(get_user_manager), + user_manager: UserManager[models.UC, models.UD] = Depends(get_user_manager), ): user = await user_manager.authenticate(credentials) diff --git a/fastapi_users/router/oauth.py b/fastapi_users/router/oauth.py index 0b7f66f7..a9746ef3 100644 --- a/fastapi_users/router/oauth.py +++ b/fastapi_users/router/oauth.py @@ -24,7 +24,7 @@ def generate_state_token( def get_oauth_router( oauth_client: BaseOAuth2, - get_user_manager: UserManagerDependency[models.UD], + get_user_manager: UserManagerDependency[models.UC, models.UD], user_db_model: Type[models.UD], authenticator: Authenticator, state_secret: SecretType, @@ -83,7 +83,7 @@ def get_oauth_router( request: Request, response: Response, access_token_state=Depends(oauth2_authorize_callback), - user_manager: UserManager[models.UD] = Depends(get_user_manager), + user_manager: UserManager[models.UC, models.UD] = Depends(get_user_manager), ): token, state = access_token_state account_id, account_email = await oauth_client.get_id_email( diff --git a/fastapi_users/router/register.py b/fastapi_users/router/register.py index 51ecc5a2..fd1db326 100644 --- a/fastapi_users/router/register.py +++ b/fastapi_users/router/register.py @@ -13,7 +13,7 @@ from fastapi_users.router.common import ErrorCode, run_handler def get_register_router( - get_user_manager: UserManagerDependency[models.UD], + get_user_manager: UserManagerDependency[models.UC, models.UD], user_model: Type[models.U], user_create_model: Type[models.UC], after_register: Optional[Callable[[models.UD, Request], None]] = None, @@ -27,7 +27,7 @@ def get_register_router( async def register( request: Request, user: user_create_model, # type: ignore - user_manager: UserManager[models.UD] = Depends(get_user_manager), + user_manager: UserManager[models.UC, models.UD] = Depends(get_user_manager), ): try: created_user = await user_manager.create(user, safe=True) diff --git a/fastapi_users/router/reset.py b/fastapi_users/router/reset.py index a148e3cc..609d312f 100644 --- a/fastapi_users/router/reset.py +++ b/fastapi_users/router/reset.py @@ -11,7 +11,6 @@ from fastapi_users.manager import ( UserManager, UserManagerDependency, UserNotExists, - ValidatePasswordProtocol, ) from fastapi_users.password import get_password_hash from fastapi_users.router.common import ErrorCode, run_handler @@ -20,12 +19,11 @@ RESET_PASSWORD_TOKEN_AUDIENCE = "fastapi-users:reset" def get_reset_password_router( - get_user_manager: UserManagerDependency[models.UD], + get_user_manager: UserManagerDependency[models.UC, models.UD], reset_password_token_secret: SecretType, reset_password_token_lifetime_seconds: int = 3600, after_forgot_password: Optional[Callable[[models.UD, str, Request], None]] = None, after_reset_password: Optional[Callable[[models.UD, Request], None]] = None, - validate_password: Optional[ValidatePasswordProtocol] = None, ) -> APIRouter: """Generate a router with the reset password routes.""" router = APIRouter() @@ -34,7 +32,7 @@ def get_reset_password_router( async def forgot_password( request: Request, email: EmailStr = Body(..., embed=True), - user_manager: UserManager[models.UD] = Depends(get_user_manager), + user_manager: UserManager[models.UC, models.UD] = Depends(get_user_manager), ): try: user = await user_manager.get_by_email(email) @@ -58,7 +56,7 @@ def get_reset_password_router( request: Request, token: str = Body(...), password: str = Body(...), - user_manager: UserManager[models.UD] = Depends(get_user_manager), + user_manager: UserManager[models.UC, models.UD] = Depends(get_user_manager), ): try: data = decode_jwt( @@ -93,17 +91,16 @@ def get_reset_password_router( detail=ErrorCode.RESET_PASSWORD_BAD_TOKEN, ) - if validate_password: - try: - await validate_password(password, user) - except InvalidPasswordException as e: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail={ - "code": ErrorCode.RESET_PASSWORD_INVALID_PASSWORD, - "reason": e.reason, - }, - ) + try: + await user_manager.validate_password(password, user) + except InvalidPasswordException as e: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail={ + "code": ErrorCode.RESET_PASSWORD_INVALID_PASSWORD, + "reason": e.reason, + }, + ) user.hashed_password = get_password_hash(password) await user_manager.user_db.update(user) diff --git a/fastapi_users/router/users.py b/fastapi_users/router/users.py index 827d7681..2d62b75a 100644 --- a/fastapi_users/router/users.py +++ b/fastapi_users/router/users.py @@ -16,7 +16,7 @@ from fastapi_users.router.common import ErrorCode, run_handler def get_users_router( - get_user_manager: UserManagerDependency[models.UD], + get_user_manager: UserManagerDependency[models.UC, models.UD], user_model: Type[models.U], user_update_model: Type[models.UU], user_db_model: Type[models.UD], @@ -35,7 +35,8 @@ def get_users_router( ) async def get_user_or_404( - id: UUID4, user_manager: UserManager[models.UD] = Depends(get_user_manager) + id: UUID4, + user_manager: UserManager[models.UC, models.UD] = Depends(get_user_manager), ) -> models.UD: try: return await user_manager.get(id) @@ -57,7 +58,7 @@ def get_users_router( request: Request, user_update: user_update_model, # type: ignore user: user_db_model = Depends(get_current_active_user), # type: ignore - user_manager: UserManager[models.UD] = Depends(get_user_manager), + user_manager: UserManager[models.UC, models.UD] = Depends(get_user_manager), ): try: updated_user = await user_manager.update(user_update, user, safe=True) @@ -100,7 +101,7 @@ def get_users_router( user_update: user_update_model, # type: ignore request: Request, user=Depends(get_user_or_404), - user_manager: UserManager[models.UD] = Depends(get_user_manager), + user_manager: UserManager[models.UC, models.UD] = Depends(get_user_manager), ): try: updated_user = await user_manager.update(user_update, user, safe=False) @@ -134,7 +135,7 @@ def get_users_router( ) async def delete_user( user=Depends(get_user_or_404), - user_manager: UserManager[models.UD] = Depends(get_user_manager), + user_manager: UserManager[models.UC, models.UD] = Depends(get_user_manager), ): await user_manager.delete(user) return None diff --git a/fastapi_users/router/verify.py b/fastapi_users/router/verify.py index 0827177a..3f7839e5 100644 --- a/fastapi_users/router/verify.py +++ b/fastapi_users/router/verify.py @@ -18,7 +18,7 @@ VERIFY_USER_TOKEN_AUDIENCE = "fastapi-users:verify" def get_verify_router( - get_user_manager: UserManagerDependency[models.UD], + get_user_manager: UserManagerDependency[models.UC, models.UD], user_model: Type[models.U], verification_token_secret: SecretType, verification_token_lifetime_seconds: int = 3600, @@ -33,7 +33,7 @@ def get_verify_router( async def request_verify_token( request: Request, email: EmailStr = Body(..., embed=True), - user_manager: UserManager[models.UD] = Depends(get_user_manager), + user_manager: UserManager[models.UC, models.UD] = Depends(get_user_manager), ): try: user = await user_manager.get_by_email(email) @@ -60,7 +60,7 @@ def get_verify_router( async def verify( request: Request, token: str = Body(..., embed=True), - user_manager: UserManager[models.UD] = Depends(get_user_manager), + user_manager: UserManager[models.UC, models.UD] = Depends(get_user_manager), ): try: data = decode_jwt( diff --git a/tests/conftest.py b/tests/conftest.py index 17f33859..b9f87ed2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,6 @@ import asyncio -from typing import AsyncGenerator, List, Optional +from typing import AsyncGenerator, List, Optional, Union -import asynctest import httpx import pytest from asgi_lifespan import LifespanManager @@ -15,13 +14,10 @@ from fastapi_users import models from fastapi_users.authentication import Authenticator, BaseAuthentication from fastapi_users.db import BaseUserDatabase from fastapi_users.jwt import SecretType -from fastapi_users.manager import ( - InvalidPasswordException, - UserManager, - UserNotExists, - ValidatePasswordProtocol, -) -from fastapi_users.models import BaseOAuthAccount, BaseOAuthAccountMixin, BaseUserDB +from fastapi_users.manager import InvalidPasswordException +from fastapi_users.manager import UserManager as BaseUserManager +from fastapi_users.manager import UserNotExists +from fastapi_users.models import BaseOAuthAccount, BaseOAuthAccountMixin from fastapi_users.password import get_password_hash guinevere_password_hash = get_password_hash("guinevere") @@ -55,6 +51,16 @@ class UserDBOAuth(UserOAuth, UserDB): pass +class UserManager(BaseUserManager[UserCreate, UserDB]): + async def validate_password( + self, password: str, user: Union[UserCreate, UserDB] + ) -> None: + if len(password) < 3: + raise InvalidPasswordException( + reason="Password should be at least 3 characters" + ) + + @pytest.fixture(scope="session") def event_loop(): """Force the pytest-asyncio loop to be the main one.""" @@ -218,17 +224,6 @@ def oauth_account5() -> BaseOAuthAccount: ) -@pytest.fixture -def validate_password() -> ValidatePasswordProtocol: - async def _validate_password(password: str, user: models.UD) -> None: - if len(password) < 3: - raise InvalidPasswordException( - reason="Password should be at least 3 characters" - ) - - return asynctest.CoroutineMock(wraps=_validate_password) - - @pytest.fixture def mock_user_db( user, verified_user, inactive_user, superuser, verified_superuser @@ -356,32 +351,32 @@ def get_mock_user_db_oauth(mock_user_db_oauth): @pytest.fixture -def user_manager(mock_user_db, validate_password): - return UserManager(UserDB, mock_user_db, validate_password) +def user_manager(mock_user_db): + return UserManager(UserDB, mock_user_db) @pytest.fixture -def get_user_manager(get_mock_user_db, validate_password): +def get_user_manager(get_mock_user_db): def _get_user_manager(user_db=Depends(get_mock_user_db)): - return UserManager(UserDB, user_db, validate_password) + return UserManager(UserDB, user_db) return _get_user_manager @pytest.fixture -def get_user_manager_oauth(get_mock_user_db_oauth, validate_password): +def get_user_manager_oauth(get_mock_user_db_oauth): def _get_user_manager_oauth(user_db=Depends(get_mock_user_db_oauth)): - return UserManager(UserDBOAuth, user_db, validate_password) + return UserManager(UserDBOAuth, user_db) return _get_user_manager_oauth -class MockAuthentication(BaseAuthentication[str]): +class MockAuthentication(BaseAuthentication[str, UserCreate, UserDB]): def __init__(self, name: str = "mock"): super().__init__(name, logout=True) self.scheme = OAuth2PasswordBearer("/login", auto_error=False) - async def __call__(self, credentials: Optional[str], user_manager: UserManager): + async def __call__(self, credentials: Optional[str], user_manager: BaseUserManager): if credentials is not None: try: token_uuid = UUID4(credentials) @@ -392,10 +387,10 @@ class MockAuthentication(BaseAuthentication[str]): return None return None - async def get_login_response(self, user: BaseUserDB, response: Response): + async def get_login_response(self, user: UserDB, response: Response): return {"token": user.id} - async def get_logout_response(self, user: BaseUserDB, response: Response): + async def get_logout_response(self, user: UserDB, response: Response): return None diff --git a/tests/test_authentication.py b/tests/test_authentication.py index a075f17e..6de2323b 100644 --- a/tests/test_authentication.py +++ b/tests/test_authentication.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Generic, Optional import pytest from fastapi import Request, status @@ -14,25 +14,33 @@ class MockSecurityScheme(SecurityBase): return "mock" -class BackendNone(BaseAuthentication[str]): +class BackendNone( + Generic[models.UC, models.UD], BaseAuthentication[str, models.UC, models.UD] +): def __init__(self, name="none"): super().__init__(name, logout=False) self.scheme = MockSecurityScheme() async def __call__( - self, credentials: Optional[str], user_manager: UserManager[models.UD] + self, + credentials: Optional[str], + user_manager: UserManager[models.UC, models.UD], ) -> Optional[models.UD]: return None -class BackendUser(BaseAuthentication[str]): +class BackendUser( + Generic[models.UC, models.UD], BaseAuthentication[str, models.UC, models.UD] +): def __init__(self, user: models.UD, name="user"): super().__init__(name, logout=False) self.scheme = MockSecurityScheme() self.user = user async def __call__( - self, credentials: Optional[str], user_manager: UserManager[models.UD] + self, + credentials: Optional[str], + user_manager: UserManager[models.UC, models.UD], ) -> Optional[models.UD]: return self.user diff --git a/tests/test_fastapi_users.py b/tests/test_fastapi_users.py index d97b6cbb..0edebf50 100644 --- a/tests/test_fastapi_users.py +++ b/tests/test_fastapi_users.py @@ -16,7 +16,6 @@ async def test_app_client( mock_authentication, oauth_client, get_test_client, - validate_password, ) -> AsyncGenerator[httpx.AsyncClient, None]: fastapi_users = FastAPIUsers[User, UserCreate, UserUpdate, UserDB]( get_mock_user_db, @@ -25,7 +24,6 @@ async def test_app_client( UserCreate, UserUpdate, UserDB, - validate_password, ) app = FastAPI() diff --git a/tests/test_manager.py b/tests/test_manager.py index 307dee84..dca5bc11 100644 --- a/tests/test_manager.py +++ b/tests/test_manager.py @@ -9,7 +9,7 @@ from tests.conftest import UserCreate, UserDB @pytest.fixture -def user_manager(mock_user_db) -> UserManager[UserDB]: +def user_manager(mock_user_db) -> UserManager[UserCreate, UserDB]: return UserManager(UserDB, mock_user_db) @@ -28,20 +28,24 @@ class TestCreateUser: @pytest.mark.parametrize( "email", ["king.arthur@camelot.bt", "King.Arthur@camelot.bt"] ) - async def test_existing_user(self, email: str, user_manager: UserManager[UserDB]): + async def test_existing_user( + self, email: str, user_manager: UserManager[UserCreate, UserDB] + ): user = UserCreate(email=email, password="guinevere") with pytest.raises(UserAlreadyExists): await user_manager.create(user) @pytest.mark.parametrize("email", ["lancelot@camelot.bt", "Lancelot@camelot.bt"]) - async def test_regular_user(self, email: str, user_manager: UserManager[UserDB]): + async def test_regular_user( + self, email: str, user_manager: UserManager[UserCreate, UserDB] + ): user = UserCreate(email=email, password="guinevere") created_user = await user_manager.create(user) assert type(created_user) == UserDB @pytest.mark.parametrize("safe,result", [(True, False), (False, True)]) async def test_superuser( - self, user_manager: UserManager[UserDB], safe: bool, result: bool + self, user_manager: UserManager[UserCreate, UserDB], safe: bool, result: bool ): user = UserCreate( email="lancelot@camelot.b", password="guinevere", is_superuser=True @@ -52,7 +56,7 @@ class TestCreateUser: @pytest.mark.parametrize("safe,result", [(True, True), (False, False)]) async def test_is_active( - self, user_manager: UserManager[UserDB], safe: bool, result: bool + self, user_manager: UserManager[UserCreate, UserDB], safe: bool, result: bool ): user = UserCreate( email="lancelot@camelot.b", password="guinevere", is_active=False @@ -65,13 +69,13 @@ class TestCreateUser: @pytest.mark.asyncio class TestVerifyUser: async def test_already_verified_user( - self, user_manager: UserManager[UserDB], verified_user: UserDB + self, user_manager: UserManager[UserCreate, UserDB], verified_user: UserDB ): with pytest.raises(UserAlreadyVerified): await user_manager.verify(verified_user) async def test_non_verified_user( - self, user_manager: UserManager[UserDB], user: UserDB + self, user_manager: UserManager[UserCreate, UserDB], user: UserDB ): user = await user_manager.verify(user) assert user.is_verified @@ -85,7 +89,7 @@ class TestAuthenticate: create_oauth2_password_request_form: Callable[ [str, str], OAuth2PasswordRequestForm ], - user_manager: UserManager[UserDB], + user_manager: UserManager[UserCreate, UserDB], ): form = create_oauth2_password_request_form("lancelot@camelot.bt", "guinevere") user = await user_manager.authenticate(form) @@ -97,7 +101,7 @@ class TestAuthenticate: create_oauth2_password_request_form: Callable[ [str, str], OAuth2PasswordRequestForm ], - user_manager: UserManager[UserDB], + user_manager: UserManager[UserCreate, UserDB], ): form = create_oauth2_password_request_form("king.arthur@camelot.bt", "percival") user = await user_manager.authenticate(form) @@ -109,7 +113,7 @@ class TestAuthenticate: create_oauth2_password_request_form: Callable[ [str, str], OAuth2PasswordRequestForm ], - user_manager: UserManager[UserDB], + user_manager: UserManager[UserCreate, UserDB], ): form = create_oauth2_password_request_form( "king.arthur@camelot.bt", "guinevere" @@ -125,7 +129,7 @@ class TestAuthenticate: create_oauth2_password_request_form: Callable[ [str, str], OAuth2PasswordRequestForm ], - user_manager: UserManager[UserDB], + user_manager: UserManager[UserCreate, UserDB], ): verify_and_update_password_patch = mocker.patch( "fastapi_users.password.verify_and_update_password" diff --git a/tests/test_router_register.py b/tests/test_router_register.py index bffec6ad..13a6d38e 100644 --- a/tests/test_router_register.py +++ b/tests/test_router_register.py @@ -75,7 +75,7 @@ class TestRegister: assert after_register.called is False async def test_invalid_password( - self, test_app_client: httpx.AsyncClient, after_register, validate_password + self, test_app_client: httpx.AsyncClient, after_register ): json = {"email": "king.arthur@camelot.bt", "password": "g"} response = await test_app_client.post("/register", json=json) @@ -85,9 +85,6 @@ class TestRegister: "code": ErrorCode.REGISTER_INVALID_PASSWORD, "reason": "Password should be at least 3 characters", } - validate_password.assert_called_with( - "g", UserCreate(email="king.arthur@camelot.bt", password="g") - ) assert after_register.called is False @pytest.mark.parametrize( diff --git a/tests/test_router_reset.py b/tests/test_router_reset.py index d5648150..0bf198cf 100644 --- a/tests/test_router_reset.py +++ b/tests/test_router_reset.py @@ -58,7 +58,6 @@ async def test_app_client( after_forgot_password, after_reset_password, get_test_client, - validate_password, ) -> AsyncGenerator[httpx.AsyncClient, None]: reset_router = get_reset_password_router( get_user_manager, @@ -66,7 +65,6 @@ async def test_app_client( LIFETIME, after_forgot_password, after_reset_password, - validate_password, ) app = FastAPI() @@ -255,7 +253,6 @@ class TestResetPassword: forgot_password_token, user: UserDB, after_reset_password, - validate_password, ): mocker.spy(mock_user_db, "update") @@ -270,7 +267,6 @@ class TestResetPassword: "code": ErrorCode.RESET_PASSWORD_INVALID_PASSWORD, "reason": "Password should be at least 3 characters", } - validate_password.assert_called_with("h", user) assert mock_user_db.update.called is False assert after_reset_password.called is False diff --git a/tests/test_router_users.py b/tests/test_router_users.py index 97bb4755..d9ff6178 100644 --- a/tests/test_router_users.py +++ b/tests/test_router_users.py @@ -25,7 +25,7 @@ def after_update(request): @pytest.fixture -def app_factory(get_user_manager, mock_authentication, after_update, validate_password): +def app_factory(get_user_manager, mock_authentication, after_update): def _app_factory(requires_verification: bool) -> FastAPI: mock_authentication_bis = MockAuthentication(name="mock-bis") authenticator = Authenticator( @@ -168,7 +168,6 @@ class TestUpdateMe: test_app_client: Tuple[httpx.AsyncClient, bool], user: UserDB, after_update, - validate_password, ): client, requires_verification = test_app_client response = await client.patch( @@ -186,7 +185,6 @@ class TestUpdateMe: "code": ErrorCode.UPDATE_USER_INVALID_PASSWORD, "reason": "Password should be at least 3 characters", } - validate_password.assert_called_with("m", user) assert after_update.called is False async def test_empty_body( @@ -751,7 +749,6 @@ class TestUpdateUser: user: UserDB, verified_superuser: UserDB, after_update, - validate_password, ): client, _ = test_app_client response = await client.patch( @@ -765,7 +762,6 @@ class TestUpdateUser: "code": ErrorCode.UPDATE_USER_INVALID_PASSWORD, "reason": "Password should be at least 3 characters", } - validate_password.assert_called_with("m", user) assert after_update.called is False async def test_valid_body_verified_superuser(