Skip to content

Commit 57b633c

Browse files
committed
Complete backend rebuild
Main components: - Inboard 0.10.4 -> 0.37.0, including FastAPI 0.88 - SQLAlchemy 1.3 -> 1.4 - Authentication refresh token stored for long-term issuing of a new access token. NOTE: Tests have not been updated and will fail ...
1 parent 1f7027b commit 57b633c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+2665
-1417
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Get rid of .venv when copying
2+
*/.venv
3+
*/*/.venv
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
[flake8]
2-
max-line-length = 88
2+
ignore = E302, E305, E203, E501, W503
3+
select = C,E,F,W,B,B950
4+
max-line-length = 120
35
exclude = .git,__pycache__,__init__.py,.mypy_cache,.pytest_cache
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
11
.mypy_cache
22
.coverage
33
htmlcov
4+
.venv
5+
.vscode
6+
*.py[co]
7+
*.egg
8+
*.egg-info
9+
*.ipynb
10+
*.code-workspace
11+
dist
12+
eggs
13+
parts
14+
bin
15+
var
16+
sdist
17+
develop-eggs
18+
.installed.cfg
19+
pip-log.txt
20+
.coverage
21+
.tox
22+
*.mo
23+
.s3cfg
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.9.4
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"""First revision
2+
3+
Revision ID: c4f38069dc24
4+
Revises:
5+
Create Date: 2022-12-16 08:09:54.834747
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
from sqlalchemy.dialects import postgresql
11+
12+
# revision identifiers, used by Alembic.
13+
revision = 'c4f38069dc24'
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('user',
22+
sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
23+
sa.Column('full_name', sa.String(), nullable=True),
24+
sa.Column('email', sa.String(), nullable=False),
25+
sa.Column('hashed_password', sa.String(), nullable=False),
26+
sa.Column('email_validated', sa.Boolean(), nullable=True),
27+
sa.Column('is_active', sa.Boolean(), nullable=True),
28+
sa.Column('is_superuser', sa.Boolean(), nullable=True),
29+
sa.PrimaryKeyConstraint('id')
30+
)
31+
op.create_index(op.f('ix_user_email'), 'user', ['email'], unique=True)
32+
op.create_index(op.f('ix_user_full_name'), 'user', ['full_name'], unique=False)
33+
op.create_index(op.f('ix_user_id'), 'user', ['id'], unique=False)
34+
op.create_table('token',
35+
sa.Column('token', sa.String(), nullable=False),
36+
sa.Column('is_valid', sa.Boolean(), nullable=True),
37+
sa.Column('authenticates_id', postgresql.UUID(as_uuid=True), nullable=True),
38+
sa.ForeignKeyConstraint(['authenticates_id'], ['user.id'], ),
39+
sa.PrimaryKeyConstraint('token')
40+
)
41+
op.create_index(op.f('ix_token_token'), 'token', ['token'], unique=False)
42+
# ### end Alembic commands ###
43+
44+
45+
def downgrade():
46+
# ### commands auto generated by Alembic - please adjust! ###
47+
op.drop_index(op.f('ix_token_token'), table_name='token')
48+
op.drop_table('token')
49+
op.drop_index(op.f('ix_user_id'), table_name='user')
50+
op.drop_index(op.f('ix_user_full_name'), table_name='user')
51+
op.drop_index(op.f('ix_user_email'), table_name='user')
52+
op.drop_table('user')
53+
# ### end Alembic commands ###

{{cookiecutter.project_slug}}/backend/app/alembic/versions/d4867f3a4c0a_first_revision.py

Lines changed: 0 additions & 59 deletions
This file was deleted.
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
from fastapi import APIRouter
22

3-
from app.api.api_v1.endpoints import items, login, users, utils
3+
from app.api.api_v1.endpoints import (
4+
login,
5+
users,
6+
proxy,
7+
)
48

59
api_router = APIRouter()
610
api_router.include_router(login.router, tags=["login"])
711
api_router.include_router(users.router, prefix="/users", tags=["users"])
8-
api_router.include_router(utils.router, prefix="/utils", tags=["utils"])
9-
api_router.include_router(items.router, prefix="/items", tags=["items"])
12+
api_router.include_router(proxy.router, prefix="/proxy", tags=["proxy"])

{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/items.py

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

{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/login.py

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from app.core import security
1111
from app.core.config import settings
1212
from app.core.security import get_password_hash
13-
from app.utils import (
13+
from app.utilities import (
1414
generate_password_reset_token,
1515
send_reset_password_email,
1616
verify_password_reset_token,
@@ -20,34 +20,55 @@
2020

2121

2222
@router.post("/login/access-token", response_model=schemas.Token)
23-
def login_access_token(
24-
db: Session = Depends(deps.get_db), form_data: OAuth2PasswordRequestForm = Depends()
25-
) -> Any:
23+
def login_access_token(db: Session = Depends(deps.get_db), form_data: OAuth2PasswordRequestForm = Depends()) -> Any:
2624
"""
2725
OAuth2 compatible token login, get an access token for future requests
2826
"""
29-
user = crud.user.authenticate(
30-
db, email=form_data.username, password=form_data.password
31-
)
27+
user = crud.user.authenticate(db, email=form_data.username, password=form_data.password)
3228
if not user:
3329
raise HTTPException(status_code=400, detail="Incorrect email or password")
3430
elif not crud.user.is_active(user):
3531
raise HTTPException(status_code=400, detail="Inactive user")
36-
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
32+
access_token_expires = timedelta(seconds=settings.ACCESS_TOKEN_EXPIRE_SECONDS)
33+
refresh_token_expires = timedelta(seconds=settings.REFRESH_TOKEN_EXPIRE_SECONDS)
34+
refresh_token = security.create_refresh_token(user.id, expires_delta=refresh_token_expires)
35+
crud.token.create(db=db, obj_in=refresh_token, user_obj=user)
3736
return {
38-
"access_token": security.create_access_token(
39-
user.id, expires_delta=access_token_expires
40-
),
37+
"access_token": security.create_access_token(user.id, expires_delta=access_token_expires),
38+
"refresh_token": refresh_token,
4139
"token_type": "bearer",
4240
}
4341

4442

45-
@router.post("/login/test-token", response_model=schemas.User)
46-
def test_token(current_user: models.User = Depends(deps.get_current_user)) -> Any:
43+
@router.post("/login/refresh-token", response_model=schemas.Token)
44+
def refresh_token(
45+
db: Session = Depends(deps.get_db),
46+
current_user: models.User = Depends(deps.get_refresh_user),
47+
) -> Any:
4748
"""
48-
Test access token
49+
Refresh tokens for future requests
4950
"""
50-
return current_user
51+
access_token_expires = timedelta(seconds=settings.ACCESS_TOKEN_EXPIRE_SECONDS)
52+
refresh_token_expires = timedelta(seconds=settings.REFRESH_TOKEN_EXPIRE_SECONDS)
53+
refresh_token = security.create_refresh_token(current_user.id, expires_delta=refresh_token_expires)
54+
crud.token.create(db=db, obj_in=refresh_token, user_obj=current_user)
55+
access_token = security.create_access_token(current_user.id, expires_delta=access_token_expires)
56+
return {
57+
"access_token": access_token,
58+
"refresh_token": refresh_token,
59+
"token_type": "bearer",
60+
}
61+
62+
63+
@router.post("/login/revoke-token", response_model=schemas.Msg)
64+
def revoke_token(
65+
db: Session = Depends(deps.get_db),
66+
current_user: models.User = Depends(deps.get_refresh_user),
67+
) -> Any:
68+
"""
69+
Revoke a refresh token
70+
"""
71+
return {"msg": "Token revoked"}
5172

5273

5374
@router.post("/password-recovery/{email}", response_model=schemas.Msg)
@@ -56,20 +77,17 @@ def recover_password(email: str, db: Session = Depends(deps.get_db)) -> Any:
5677
Password Recovery
5778
"""
5879
user = crud.user.get_by_email(db, email=email)
59-
6080
if not user:
6181
raise HTTPException(
6282
status_code=404,
63-
detail="The user with this username does not exist in the system.",
83+
detail="This user does not exist in the system.",
6484
)
6585
password_reset_token = generate_password_reset_token(email=email)
66-
send_reset_password_email(
67-
email_to=user.email, email=email, token=password_reset_token
68-
)
69-
return {"msg": "Password recovery email sent"}
86+
send_reset_password_email(email_to=user.email, email=email, token=password_reset_token)
87+
return {"msg": "Password recovery email sent."}
7088

7189

72-
@router.post("/reset-password/", response_model=schemas.Msg)
90+
@router.post("/reset-password", response_model=schemas.Msg)
7391
def reset_password(
7492
token: str = Body(...),
7593
new_password: str = Body(...),
@@ -85,12 +103,12 @@ def reset_password(
85103
if not user:
86104
raise HTTPException(
87105
status_code=404,
88-
detail="The user with this username does not exist in the system.",
106+
detail="This user does not exist in the system.",
89107
)
90108
elif not crud.user.is_active(user):
91109
raise HTTPException(status_code=400, detail="Inactive user")
92110
hashed_password = get_password_hash(new_password)
93111
user.hashed_password = hashed_password
94112
db.add(user)
95113
db.commit()
96-
return {"msg": "Password updated successfully"}
114+
return {"msg": "Password updated successfully."}

0 commit comments

Comments
 (0)