Skip to content

Commit 0ed3ae8

Browse files
committed
added template_fastapi_users, changes in project structure
1 parent 77b3e9c commit 0ed3ae8

File tree

25 files changed

+471
-769
lines changed

25 files changed

+471
-769
lines changed

{{cookiecutter.project_name}}/template_fastapi_users/alembic/versions/2021_11_09_1736_init__cefce371682e.py

Lines changed: 0 additions & 38 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""initial_fastapi_users_model
2+
3+
Revision ID: 57b460a916d4
4+
Revises:
5+
Create Date: 2022-01-09 19:52:37.810574
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
from fastapi_users_db_sqlalchemy import guid
11+
12+
# revision identifiers, used by Alembic.
13+
revision = "57b460a916d4"
14+
down_revision = None
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade():
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
op.create_table(
22+
"user",
23+
sa.Column("id", guid.GUID(), nullable=False),
24+
sa.Column("email", sa.String(length=320), nullable=False),
25+
sa.Column("hashed_password", sa.String(length=72), nullable=False),
26+
sa.Column("is_active", sa.Boolean(), nullable=False),
27+
sa.Column("is_superuser", sa.Boolean(), nullable=False),
28+
sa.Column("is_verified", sa.Boolean(), nullable=False),
29+
sa.PrimaryKeyConstraint("id"),
30+
)
31+
op.create_index(op.f("ix_user_email"), "user", ["email"], unique=True)
32+
# ### end Alembic commands ###
33+
34+
35+
def downgrade():
36+
# ### commands auto generated by Alembic - please adjust! ###
37+
op.drop_index(op.f("ix_user_email"), table_name="user")
38+
op.drop_table("user")
39+
# ### end Alembic commands ###
Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,31 @@
1+
"""
2+
Users and auth routers 'for free' from FastAPI Users.
3+
https://fastapi-users.github.io/fastapi-users/configuration/routers/
4+
5+
You can include more of them + oauth login endpoints.
6+
7+
fastapi_users in defined in deps, because it also
8+
includes useful dependencies.
9+
"""
10+
111
from fastapi import APIRouter
212

3-
from app.api.endpoints import auth, users
13+
from app.api.deps import fastapi_users
14+
from app.core import security
415

516
api_router = APIRouter()
6-
api_router.include_router(auth.router, prefix="/auth", tags=["auth"])
7-
api_router.include_router(users.router, prefix="/users", tags=["users"])
17+
api_router.include_router(
18+
fastapi_users.get_auth_router(security.AUTH_BACKEND),
19+
prefix="/auth/jwt",
20+
tags=["auth"],
21+
)
22+
api_router.include_router(
23+
fastapi_users.get_register_router(),
24+
prefix="/auth",
25+
tags=["auth"],
26+
)
27+
api_router.include_router(
28+
fastapi_users.get_users_router(),
29+
prefix="/users",
30+
tags=["users"],
31+
)
Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
from typing import AsyncGenerator, Optional
1+
from typing import AsyncGenerator
22

3-
from fastapi import Depends, HTTPException, status
3+
from fastapi import Depends
44
from fastapi.security import OAuth2PasswordBearer
5-
from jose import jwt
6-
from pydantic import ValidationError
7-
from sqlalchemy import select
5+
from fastapi_users.fastapi_users import FastAPIUsers
6+
from fastapi_users_db_sqlalchemy import SQLAlchemyUserDatabase
87
from sqlalchemy.ext.asyncio import AsyncSession
98

109
from app import schemas
11-
from app.core import security, config
12-
from app.models import User
10+
from app.core import security
11+
from app.models import UserTable
1312
from app.session import async_session
1413

1514
reusable_oauth2 = OAuth2PasswordBearer(tokenUrl="auth/access-token")
@@ -20,24 +19,24 @@ async def get_session() -> AsyncGenerator[AsyncSession, None]:
2019
yield session
2120

2221

23-
async def get_current_user(
24-
session: AsyncSession = Depends(get_session), token: str = Depends(reusable_oauth2)
25-
) -> User:
22+
async def get_user_db(session: AsyncSession = Depends(get_session)):
23+
yield SQLAlchemyUserDatabase(schemas.UserDB, session, UserTable)
2624

27-
try:
28-
payload = jwt.decode(
29-
token, config.settings.SECRET_KEY, algorithms=[security.ALGORITHM]
30-
)
31-
token_data = schemas.TokenPayload(**payload)
32-
except (jwt.JWTError, ValidationError):
33-
raise HTTPException(
34-
status_code=status.HTTP_403_FORBIDDEN,
35-
detail="Could not validate credentials",
36-
)
3725

38-
result = await session.execute(select(User).where(User.id == token_data.sub))
39-
user: Optional[User] = result.scalars().first()
26+
async def get_user_manager(user_db: SQLAlchemyUserDatabase = Depends(get_user_db)):
27+
yield security.UserManager(user_db)
4028

41-
if not user:
42-
raise HTTPException(status_code=404, detail="User not found")
43-
return user
29+
30+
fastapi_users = FastAPIUsers(
31+
get_user_manager, # type: ignore
32+
[security.AUTH_BACKEND],
33+
schemas.User,
34+
schemas.UserCreate,
35+
schemas.UserUpdate,
36+
schemas.UserDB,
37+
)
38+
39+
40+
get_current_user = fastapi_users.current_user()
41+
get_current_active_user = fastapi_users.current_user(active=True)
42+
get_current_superuser = fastapi_users.current_user(active=True, superuser=True)

{{cookiecutter.project_name}}/template_fastapi_users/app/api/endpoints/auth.py

Lines changed: 0 additions & 91 deletions
This file was deleted.

{{cookiecutter.project_name}}/template_fastapi_users/app/api/endpoints/users.py

Lines changed: 0 additions & 41 deletions
This file was deleted.

{{cookiecutter.project_name}}/template_fastapi_users/app/core/config.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,29 +20,27 @@
2020
"""
2121

2222
from pathlib import Path
23-
from typing import Union, Literal
23+
from typing import Literal, Union
2424

2525
import toml
2626
from pydantic import AnyHttpUrl, AnyUrl, BaseSettings, EmailStr, validator
2727

28-
2928
PROJECT_DIR = Path(__file__).parent.parent.parent
30-
pyproject_content = toml.load(f"{PROJECT_DIR}/pyproject.toml")["tool"]["poetry"]
29+
PYPROJECT_CONTENT = toml.load(f"{PROJECT_DIR}/pyproject.toml")["tool"]["poetry"]
3130

3231

3332
class Settings(BaseSettings):
3433
# CORE SETTINGS
3534
SECRET_KEY: str
3635
ENVIRONMENT: Literal["DEV", "PYTEST", "STAGE", "PRODUCTION"]
3736
ACCESS_TOKEN_EXPIRE_MINUTES: int
38-
SECURITY_BCRYPT_DEFAULT_ROUNDS: int = 12
3937
REFRESH_TOKEN_EXPIRE_MINUTES: int
4038
BACKEND_CORS_ORIGINS: Union[str, list[AnyHttpUrl]]
4139

4240
# PROJECT NAME, VERSION AND DESCRIPTION
43-
PROJECT_NAME: str = pyproject_content["name"]
44-
VERSION: str = pyproject_content["version"]
45-
DESCRIPTION: str = pyproject_content["description"]
41+
PROJECT_NAME: str = PYPROJECT_CONTENT["name"]
42+
VERSION: str = PYPROJECT_CONTENT["version"]
43+
DESCRIPTION: str = PYPROJECT_CONTENT["description"]
4644

4745
# POSTGRESQL DEFAULT DATABASE
4846
DEFAULT_DATABASE_HOSTNAME: str

0 commit comments

Comments
 (0)