Move validate_password into UserManager

This commit is contained in:
François Voron
2021-09-14 14:01:57 +02:00
parent fdc8e54253
commit 480a6bc4df
19 changed files with 107 additions and 116 deletions

View File

@@ -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,

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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]]

View File

@@ -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)

View File

@@ -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(

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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(

View File

@@ -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

View File

@@ -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

View File

@@ -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()

View File

@@ -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"

View File

@@ -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(

View File

@@ -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

View File

@@ -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(