Skip to content

Commit f7c52d8

Browse files
committed
refactors with no deletes integrated into backend
1 parent a76587d commit f7c52d8

Some content is hidden

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

44 files changed

+482
-419
lines changed

.DS_Store

6 KB
Binary file not shown.
Binary file not shown.
Binary file not shown.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__="0.1.0"
1+
__version__ = "0.1.0"

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

Lines changed: 40 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
from typing import Any, Union, Dict
1+
from typing import Any, Union
22

33
from fastapi import APIRouter, Body, Depends, HTTPException
44
from fastapi.security import OAuth2PasswordRequestForm
5-
from sqlalchemy.orm import Session
5+
from motor.core import AgnosticDatabase
66

77
from app import crud, models, schemas
88
from app.api import deps
@@ -24,7 +24,7 @@
2424
- The user ID or password was incorrect.
2525
- The account does not exist.
2626
- The account is locked or disabled.
27-
- Code should go through the same process, no matter what, allowing the application to return in approximately
27+
- Code should go through the same process, no matter what, allowing the application to return in approximately
2828
the same response time.
2929
- In the words of George Orwell, break these rules sooner than do something truly barbaric.
3030
@@ -33,15 +33,15 @@
3333

3434

3535
@router.post("/magic/{email}", response_model=schemas.WebToken)
36-
def login_with_magic_link(*, db: Session = Depends(deps.get_db), email: str) -> Any:
36+
async def login_with_magic_link(*, db: AgnosticDatabase = Depends(deps.get_db), email: str) -> Any:
3737
"""
3838
First step of a 'magic link' login. Check if the user exists and generate a magic link. Generates two short-duration
3939
jwt tokens, one for validation, one for email. Creates user if not exist.
4040
"""
41-
user = crud.user.get_by_email(db, email=email)
41+
user = await crud.user.get_by_email(db, email=email)
4242
if not user:
4343
user_in = schemas.UserCreate(**{"email": email})
44-
user = crud.user.create(db, obj_in=user_in)
44+
user = await crud.user.create(db, obj_in=user_in)
4545
if not crud.user.is_active(user):
4646
# Still permits a timed-attack, but does create ambiguity.
4747
raise HTTPException(status_code=400, detail="A link to activate your account has been emailed.")
@@ -53,9 +53,9 @@ def login_with_magic_link(*, db: Session = Depends(deps.get_db), email: str) ->
5353

5454

5555
@router.post("/claim", response_model=schemas.Token)
56-
def validate_magic_link(
56+
async def validate_magic_link(
5757
*,
58-
db: Session = Depends(deps.get_db),
58+
db: AgnosticDatabase = Depends(deps.get_db),
5959
obj_in: schemas.WebToken,
6060
magic_in: bool = Depends(deps.get_magic_token),
6161
) -> Any:
@@ -64,7 +64,7 @@ def validate_magic_link(
6464
"""
6565
claim_in = deps.get_magic_token(token=obj_in.claim)
6666
# Get the user
67-
user = crud.user.get(db, id=magic_in.sub)
67+
user = await crud.user.get(db, id=magic_in.sub)
6868
# Test the claims
6969
if (
7070
(claim_in.sub == magic_in.sub)
@@ -75,15 +75,15 @@ def validate_magic_link(
7575
raise HTTPException(status_code=400, detail="Login failed; invalid claim.")
7676
# Validate that the email is the user's
7777
if not user.email_validated:
78-
crud.user.validate_email(db=db, db_obj=user)
78+
await crud.user.validate_email(db=db, db_obj=user)
7979
# Check if totp active
8080
refresh_token = None
8181
force_totp = True
8282
if not user.totp_secret:
8383
# No TOTP, so this concludes the login validation
8484
force_totp = False
8585
refresh_token = security.create_refresh_token(subject=user.id)
86-
crud.token.create(db=db, obj_in=refresh_token, user_obj=user)
86+
await crud.token.create(db=db, obj_in=refresh_token, user_obj=user)
8787
return {
8888
"access_token": security.create_access_token(subject=user.id, force_totp=force_totp),
8989
"refresh_token": refresh_token,
@@ -92,11 +92,13 @@ def validate_magic_link(
9292

9393

9494
@router.post("/oauth", response_model=schemas.Token)
95-
def login_with_oauth2(db: Session = Depends(deps.get_db), form_data: OAuth2PasswordRequestForm = Depends()) -> Any:
95+
async def login_with_oauth2(
96+
db: AgnosticDatabase = Depends(deps.get_db), form_data: OAuth2PasswordRequestForm = Depends()
97+
) -> Any:
9698
"""
9799
First step with OAuth2 compatible token login, get an access token for future requests.
98100
"""
99-
user = crud.user.authenticate(db, email=form_data.username, password=form_data.password)
101+
user = await crud.user.authenticate(db, email=form_data.username, password=form_data.password)
100102
if not form_data.password or not user or not crud.user.is_active(user):
101103
raise HTTPException(status_code=400, detail="Login failed; incorrect email or password")
102104
# Check if totp active
@@ -106,7 +108,7 @@ def login_with_oauth2(db: Session = Depends(deps.get_db), form_data: OAuth2Passw
106108
# No TOTP, so this concludes the login validation
107109
force_totp = False
108110
refresh_token = security.create_refresh_token(subject=user.id)
109-
crud.token.create(db=db, obj_in=refresh_token, user_obj=user)
111+
await crud.token.create(db=db, obj_in=refresh_token, user_obj=user)
110112
return {
111113
"access_token": security.create_access_token(subject=user.id, force_totp=force_totp),
112114
"refresh_token": refresh_token,
@@ -115,9 +117,9 @@ def login_with_oauth2(db: Session = Depends(deps.get_db), form_data: OAuth2Passw
115117

116118

117119
@router.post("/totp", response_model=schemas.Token)
118-
def login_with_totp(
120+
async def login_with_totp(
119121
*,
120-
db: Session = Depends(deps.get_db),
122+
db: AgnosticDatabase = Depends(deps.get_db),
121123
totp_data: schemas.WebToken,
122124
current_user: models.User = Depends(deps.get_totp_user),
123125
) -> Any:
@@ -130,9 +132,9 @@ def login_with_totp(
130132
if not new_counter:
131133
raise HTTPException(status_code=400, detail="Login failed; unable to verify TOTP.")
132134
# Save the new counter to prevent reuse
133-
current_user = crud.user.update_totp_counter(db=db, db_obj=current_user, new_counter=new_counter)
135+
current_user = await crud.user.update_totp_counter(db=db, db_obj=current_user, new_counter=new_counter)
134136
refresh_token = security.create_refresh_token(subject=current_user.id)
135-
crud.token.create(db=db, obj_in=refresh_token, user_obj=current_user)
137+
await crud.token.create(db=db, obj_in=refresh_token, user_obj=current_user)
136138
return {
137139
"access_token": security.create_access_token(subject=current_user.id),
138140
"refresh_token": refresh_token,
@@ -141,17 +143,17 @@ def login_with_totp(
141143

142144

143145
@router.put("/totp", response_model=schemas.Msg)
144-
def enable_totp_authentication(
146+
async def enable_totp_authentication(
145147
*,
146-
db: Session = Depends(deps.get_db),
148+
db: AgnosticDatabase = Depends(deps.get_db),
147149
data_in: schemas.EnableTOTP,
148150
current_user: models.User = Depends(deps.get_current_active_user),
149151
) -> Any:
150152
"""
151153
For validation of token before enabling TOTP.
152154
"""
153155
if current_user.hashed_password:
154-
user = crud.user.authenticate(db, email=current_user.email, password=data_in.password)
156+
user = await crud.user.authenticate(db, email=current_user.email, password=data_in.password)
155157
if not data_in.password or not user:
156158
raise HTTPException(status_code=400, detail="Unable to authenticate or activate TOTP.")
157159
totp_in = security.create_new_totp(label=current_user.email, uri=data_in.uri)
@@ -161,39 +163,39 @@ def enable_totp_authentication(
161163
if not new_counter:
162164
raise HTTPException(status_code=400, detail="Unable to authenticate or activate TOTP.")
163165
# Enable TOTP and save the new counter to prevent reuse
164-
current_user = crud.user.activate_totp(db=db, db_obj=current_user, totp_in=totp_in)
165-
current_user = crud.user.update_totp_counter(db=db, db_obj=current_user, new_counter=new_counter)
166+
current_user = await crud.user.activate_totp(db=db, db_obj=current_user, totp_in=totp_in)
167+
current_user = await crud.user.update_totp_counter(db=db, db_obj=current_user, new_counter=new_counter)
166168
return {"msg": "TOTP enabled. Do not lose your recovery code."}
167169

168170

169171
@router.delete("/totp", response_model=schemas.Msg)
170-
def disable_totp_authentication(
172+
async def disable_totp_authentication(
171173
*,
172-
db: Session = Depends(deps.get_db),
174+
db: AgnosticDatabase = Depends(deps.get_db),
173175
data_in: schemas.UserUpdate,
174176
current_user: models.User = Depends(deps.get_current_active_user),
175177
) -> Any:
176178
"""
177179
Disable TOTP.
178180
"""
179181
if current_user.hashed_password:
180-
user = crud.user.authenticate(db, email=current_user.email, password=data_in.original)
182+
user = await crud.user.authenticate(db, email=current_user.email, password=data_in.original)
181183
if not data_in.original or not user:
182184
raise HTTPException(status_code=400, detail="Unable to authenticate or deactivate TOTP.")
183-
crud.user.deactivate_totp(db=db, db_obj=current_user)
185+
await crud.user.deactivate_totp(db=db, db_obj=current_user)
184186
return {"msg": "TOTP disabled."}
185187

186188

187189
@router.post("/refresh", response_model=schemas.Token)
188-
def refresh_token(
189-
db: Session = Depends(deps.get_db),
190+
async def refresh_token(
191+
db: AgnosticDatabase = Depends(deps.get_db),
190192
current_user: models.User = Depends(deps.get_refresh_user),
191193
) -> Any:
192194
"""
193195
Refresh tokens for future requests
194196
"""
195197
refresh_token = security.create_refresh_token(subject=current_user.id)
196-
crud.token.create(db=db, obj_in=refresh_token, user_obj=current_user)
198+
await crud.token.create(db=db, obj_in=refresh_token, user_obj=current_user)
197199
return {
198200
"access_token": security.create_access_token(subject=current_user.id),
199201
"refresh_token": refresh_token,
@@ -202,8 +204,8 @@ def refresh_token(
202204

203205

204206
@router.post("/revoke", response_model=schemas.Msg)
205-
def revoke_token(
206-
db: Session = Depends(deps.get_db),
207+
async def revoke_token(
208+
db: AgnosticDatabase = Depends(deps.get_db),
207209
current_user: models.User = Depends(deps.get_refresh_user),
208210
) -> Any:
209211
"""
@@ -213,11 +215,11 @@ def revoke_token(
213215

214216

215217
@router.post("/recover/{email}", response_model=Union[schemas.WebToken, schemas.Msg])
216-
def recover_password(email: str, db: Session = Depends(deps.get_db)) -> Any:
218+
async def recover_password(email: str, db: AgnosticDatabase = Depends(deps.get_db)) -> Any:
217219
"""
218220
Password Recovery
219221
"""
220-
user = crud.user.get_by_email(db, email=email)
222+
user = await crud.user.get_by_email(db, email=email)
221223
if user and crud.user.is_active(user):
222224
tokens = security.create_magic_tokens(subject=user.id)
223225
if settings.EMAILS_ENABLED:
@@ -227,9 +229,9 @@ def recover_password(email: str, db: Session = Depends(deps.get_db)) -> Any:
227229

228230

229231
@router.post("/reset", response_model=schemas.Msg)
230-
def reset_password(
232+
async def reset_password(
231233
*,
232-
db: Session = Depends(deps.get_db),
234+
db: AgnosticDatabase = Depends(deps.get_db),
233235
new_password: str = Body(...),
234236
claim: str = Body(...),
235237
magic_in: bool = Depends(deps.get_magic_token),
@@ -239,7 +241,7 @@ def reset_password(
239241
"""
240242
claim_in = deps.get_magic_token(token=claim)
241243
# Get the user
242-
user = crud.user.get(db, id=magic_in.sub)
244+
user = await crud.user.get(db, id=magic_in.sub)
243245
# Test the claims
244246
if (
245247
(claim_in.sub == magic_in.sub)
@@ -251,6 +253,5 @@ def reset_password(
251253
# Update the password
252254
hashed_password = security.get_password_hash(new_password)
253255
user.hashed_password = hashed_password
254-
db.add(user)
255-
db.commit()
256+
await user.save()
256257
return {"msg": "Password updated successfully."}

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717

1818
@router.post("/{path:path}")
1919
async def proxy_post_request(
20-
*, path: AnyHttpUrl, request: Request, current_user: models.User = Depends(deps.get_current_active_user),
20+
*,
21+
path: AnyHttpUrl,
22+
request: Request,
23+
current_user: models.User = Depends(deps.get_current_active_user),
2124
) -> Any:
2225
# https://www.starlette.io/requests/
2326
# https://www.python-httpx.org/quickstart/
@@ -39,7 +42,10 @@ async def proxy_post_request(
3942

4043
@router.get("/{path:path}")
4144
async def proxy_get_request(
42-
*, path: AnyHttpUrl, request: Request, current_user: models.User = Depends(deps.get_current_active_user),
45+
*,
46+
path: AnyHttpUrl,
47+
request: Request,
48+
current_user: models.User = Depends(deps.get_current_active_user),
4349
) -> Any:
4450
try:
4551
headers = {
@@ -52,4 +58,3 @@ async def proxy_get_request(
5258
return response
5359
except Exception as e:
5460
raise HTTPException(status_code=403, detail=str(e))
55-

0 commit comments

Comments
 (0)