mirror of
https://github.com/fastapi-users/fastapi-users.git
synced 2026-03-13 07:49:55 +08:00
Move validate_password into UserManager
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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]]
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user