mirror of
https://github.com/fastapi-users/fastapi-users.git
synced 2025-08-14 18:58:10 +08:00
Reorganize base modules
This commit is contained in:
@ -1,36 +1,2 @@
|
|||||||
from typing import Callable, Type
|
from fastapi_users.fastapi_users import FastAPIUsers # noqa: F401
|
||||||
|
from fastapi_users.models import BaseUser # noqa: F401
|
||||||
from fastapi import APIRouter
|
|
||||||
|
|
||||||
from fastapi_users.authentication import BaseAuthentication
|
|
||||||
from fastapi_users.db import BaseUserDatabase
|
|
||||||
from fastapi_users.models import BaseUser, BaseUserDB
|
|
||||||
from fastapi_users.router import get_user_router
|
|
||||||
|
|
||||||
|
|
||||||
class FastAPIUsers:
|
|
||||||
"""
|
|
||||||
Main object that ties together the component for users authentication.
|
|
||||||
|
|
||||||
:param db: Database adapter instance.
|
|
||||||
:param auth: Authentication logic instance.
|
|
||||||
:param user_model: Pydantic model of a user.
|
|
||||||
|
|
||||||
:attribute router: FastAPI router exposing authentication routes.
|
|
||||||
:attribute get_current_user: Dependency callable to inject authenticated user.
|
|
||||||
"""
|
|
||||||
|
|
||||||
db: BaseUserDatabase
|
|
||||||
auth: BaseAuthentication
|
|
||||||
router: APIRouter
|
|
||||||
get_current_user: Callable[..., BaseUserDB]
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self, db: BaseUserDatabase, auth: BaseAuthentication, user_model: Type[BaseUser]
|
|
||||||
):
|
|
||||||
self.db = db
|
|
||||||
self.auth = auth
|
|
||||||
self.router = get_user_router(self.db, user_model, self.auth)
|
|
||||||
|
|
||||||
get_current_user = self.auth.get_authentication_method(self.db)
|
|
||||||
self.get_current_user = get_current_user # type: ignore
|
|
||||||
|
@ -1,18 +1,2 @@
|
|||||||
from typing import Callable
|
from fastapi_users.authentication.base import BaseAuthentication # noqa: F401
|
||||||
|
from fastapi_users.authentication.jwt import JWTAuthentication # noqa: F401
|
||||||
from starlette.responses import Response
|
|
||||||
|
|
||||||
from fastapi_users.db import BaseUserDatabase
|
|
||||||
from fastapi_users.models import BaseUserDB
|
|
||||||
|
|
||||||
|
|
||||||
class BaseAuthentication:
|
|
||||||
"""Base adapter for generating and decoding authentication tokens."""
|
|
||||||
|
|
||||||
async def get_login_response(self, user: BaseUserDB, response: Response):
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def get_authentication_method(
|
|
||||||
self, user_db: BaseUserDatabase
|
|
||||||
) -> Callable[..., BaseUserDB]:
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
18
fastapi_users/authentication/base.py
Normal file
18
fastapi_users/authentication/base.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
from starlette.responses import Response
|
||||||
|
|
||||||
|
from fastapi_users.db import BaseUserDatabase
|
||||||
|
from fastapi_users.models import BaseUserDB
|
||||||
|
|
||||||
|
|
||||||
|
class BaseAuthentication:
|
||||||
|
"""Base adapter for generating and decoding authentication tokens."""
|
||||||
|
|
||||||
|
async def get_login_response(self, user: BaseUserDB, response: Response):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def get_authentication_method(
|
||||||
|
self, user_db: BaseUserDatabase
|
||||||
|
) -> Callable[..., BaseUserDB]:
|
||||||
|
raise NotImplementedError()
|
@ -6,8 +6,8 @@ from fastapi.security import OAuth2PasswordBearer
|
|||||||
from starlette import status
|
from starlette import status
|
||||||
from starlette.responses import Response
|
from starlette.responses import Response
|
||||||
|
|
||||||
from fastapi_users.authentication import BaseAuthentication
|
from fastapi_users.authentication.base import BaseAuthentication
|
||||||
from fastapi_users.db import BaseUserDatabase
|
from fastapi_users.db.base import BaseUserDatabase
|
||||||
from fastapi_users.models import BaseUserDB
|
from fastapi_users.models import BaseUserDB
|
||||||
|
|
||||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/login")
|
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/login")
|
||||||
|
@ -1,59 +1,5 @@
|
|||||||
from typing import List, Optional
|
from fastapi_users.db.base import BaseUserDatabase # noqa: F401
|
||||||
|
from fastapi_users.db.sqlalchemy import ( # noqa: F401
|
||||||
from fastapi.security import OAuth2PasswordRequestForm
|
SQLAlchemyBaseUserTable,
|
||||||
|
SQLAlchemyUserDatabase,
|
||||||
from fastapi_users.models import BaseUserDB
|
)
|
||||||
from fastapi_users.password import get_password_hash, verify_and_update_password
|
|
||||||
|
|
||||||
|
|
||||||
class BaseUserDatabase:
|
|
||||||
"""Base adapter for retrieving, creating and updating users from a database."""
|
|
||||||
|
|
||||||
async def list(self) -> List[BaseUserDB]:
|
|
||||||
"""List all users."""
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
async def get(self, id: str) -> Optional[BaseUserDB]:
|
|
||||||
"""Get a single user by id."""
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
async def get_by_email(self, email: str) -> Optional[BaseUserDB]:
|
|
||||||
"""Get a single user by email."""
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
async def create(self, user: BaseUserDB) -> BaseUserDB:
|
|
||||||
"""Create a user."""
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
async def update(self, user: BaseUserDB) -> BaseUserDB:
|
|
||||||
"""Update a user."""
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
async def authenticate(
|
|
||||||
self, credentials: OAuth2PasswordRequestForm
|
|
||||||
) -> Optional[BaseUserDB]:
|
|
||||||
"""
|
|
||||||
Authenticate and return a user following an email and a password.
|
|
||||||
|
|
||||||
Will automatically upgrade password hash if necessary.
|
|
||||||
"""
|
|
||||||
user = await self.get_by_email(credentials.username)
|
|
||||||
|
|
||||||
# Always run the hasher to mitigate timing attack
|
|
||||||
# Inspired from Django: https://code.djangoproject.com/ticket/20760
|
|
||||||
get_password_hash(credentials.password)
|
|
||||||
|
|
||||||
if user is None:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
verified, updated_password_hash = verify_and_update_password(
|
|
||||||
credentials.password, user.hashed_password
|
|
||||||
)
|
|
||||||
if not verified:
|
|
||||||
return None
|
|
||||||
# Update password hash to a more robust one if needed
|
|
||||||
if updated_password_hash is not None:
|
|
||||||
user.hashed_password = updated_password_hash
|
|
||||||
await self.update(user)
|
|
||||||
|
|
||||||
return user
|
|
||||||
|
59
fastapi_users/db/base.py
Normal file
59
fastapi_users/db/base.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
from fastapi.security import OAuth2PasswordRequestForm
|
||||||
|
|
||||||
|
from fastapi_users.models import BaseUserDB
|
||||||
|
from fastapi_users.password import get_password_hash, verify_and_update_password
|
||||||
|
|
||||||
|
|
||||||
|
class BaseUserDatabase:
|
||||||
|
"""Base adapter for retrieving, creating and updating users from a database."""
|
||||||
|
|
||||||
|
async def list(self) -> List[BaseUserDB]:
|
||||||
|
"""List all users."""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
async def get(self, id: str) -> Optional[BaseUserDB]:
|
||||||
|
"""Get a single user by id."""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
async def get_by_email(self, email: str) -> Optional[BaseUserDB]:
|
||||||
|
"""Get a single user by email."""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
async def create(self, user: BaseUserDB) -> BaseUserDB:
|
||||||
|
"""Create a user."""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
async def update(self, user: BaseUserDB) -> BaseUserDB:
|
||||||
|
"""Update a user."""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
async def authenticate(
|
||||||
|
self, credentials: OAuth2PasswordRequestForm
|
||||||
|
) -> Optional[BaseUserDB]:
|
||||||
|
"""
|
||||||
|
Authenticate and return a user following an email and a password.
|
||||||
|
|
||||||
|
Will automatically upgrade password hash if necessary.
|
||||||
|
"""
|
||||||
|
user = await self.get_by_email(credentials.username)
|
||||||
|
|
||||||
|
# Always run the hasher to mitigate timing attack
|
||||||
|
# Inspired from Django: https://code.djangoproject.com/ticket/20760
|
||||||
|
get_password_hash(credentials.password)
|
||||||
|
|
||||||
|
if user is None:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
verified, updated_password_hash = verify_and_update_password(
|
||||||
|
credentials.password, user.hashed_password
|
||||||
|
)
|
||||||
|
if not verified:
|
||||||
|
return None
|
||||||
|
# Update password hash to a more robust one if needed
|
||||||
|
if updated_password_hash is not None:
|
||||||
|
user.hashed_password = updated_password_hash
|
||||||
|
await self.update(user)
|
||||||
|
|
||||||
|
return user
|
@ -3,11 +3,11 @@ from typing import List, cast
|
|||||||
from databases import Database
|
from databases import Database
|
||||||
from sqlalchemy import Boolean, Column, String, Table
|
from sqlalchemy import Boolean, Column, String, Table
|
||||||
|
|
||||||
from fastapi_users.db import BaseUserDatabase
|
from fastapi_users.db.base import BaseUserDatabase
|
||||||
from fastapi_users.models import BaseUserDB
|
from fastapi_users.models import BaseUserDB
|
||||||
|
|
||||||
|
|
||||||
class BaseUserTable:
|
class SQLAlchemyBaseUserTable:
|
||||||
"""Base SQLAlchemy users table definition."""
|
"""Base SQLAlchemy users table definition."""
|
||||||
|
|
||||||
__tablename__ = "user"
|
__tablename__ = "user"
|
||||||
|
38
fastapi_users/fastapi_users.py
Normal file
38
fastapi_users/fastapi_users.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
"""Ready-to-use and customizable users management for FastAPI."""
|
||||||
|
|
||||||
|
from typing import Callable, Type
|
||||||
|
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from fastapi_users.authentication import BaseAuthentication
|
||||||
|
from fastapi_users.db import BaseUserDatabase
|
||||||
|
from fastapi_users.models import BaseUser, BaseUserDB
|
||||||
|
from fastapi_users.router import get_user_router
|
||||||
|
|
||||||
|
|
||||||
|
class FastAPIUsers:
|
||||||
|
"""
|
||||||
|
Main object that ties together the component for users authentication.
|
||||||
|
|
||||||
|
:param db: Database adapter instance.
|
||||||
|
:param auth: Authentication logic instance.
|
||||||
|
:param user_model: Pydantic model of a user.
|
||||||
|
|
||||||
|
:attribute router: FastAPI router exposing authentication routes.
|
||||||
|
:attribute get_current_user: Dependency callable to inject authenticated user.
|
||||||
|
"""
|
||||||
|
|
||||||
|
db: BaseUserDatabase
|
||||||
|
auth: BaseAuthentication
|
||||||
|
router: APIRouter
|
||||||
|
get_current_user: Callable[..., BaseUserDB]
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, db: BaseUserDatabase, auth: BaseAuthentication, user_model: Type[BaseUser]
|
||||||
|
):
|
||||||
|
self.db = db
|
||||||
|
self.auth = auth
|
||||||
|
self.router = get_user_router(self.db, user_model, self.auth)
|
||||||
|
|
||||||
|
get_current_user = self.auth.get_authentication_method(self.db)
|
||||||
|
self.get_current_user = get_current_user # type: ignore
|
@ -6,14 +6,14 @@ import sqlalchemy
|
|||||||
from databases import Database
|
from databases import Database
|
||||||
from sqlalchemy.ext.declarative import DeclarativeMeta, declarative_base
|
from sqlalchemy.ext.declarative import DeclarativeMeta, declarative_base
|
||||||
|
|
||||||
from fastapi_users.db.sqlalchemy import BaseUserTable, SQLAlchemyUserDatabase
|
from fastapi_users.db.sqlalchemy import SQLAlchemyBaseUserTable, SQLAlchemyUserDatabase
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
async def sqlalchemy_user_db() -> AsyncGenerator[SQLAlchemyUserDatabase, None]:
|
async def sqlalchemy_user_db() -> AsyncGenerator[SQLAlchemyUserDatabase, None]:
|
||||||
Base: DeclarativeMeta = declarative_base()
|
Base: DeclarativeMeta = declarative_base()
|
||||||
|
|
||||||
class User(BaseUserTable, Base):
|
class User(SQLAlchemyBaseUserTable, Base):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
DATABASE_URL = "sqlite:///./test.db"
|
DATABASE_URL = "sqlite:///./test.db"
|
||||||
|
Reference in New Issue
Block a user