Skip to content

Commit 24f1238

Browse files
Feature/login (#293) (#322)
Co-authored-by: kobyfogel <[email protected]>
1 parent 223d9eb commit 24f1238

File tree

7 files changed

+247
-70
lines changed

7 files changed

+247
-70
lines changed

app/internal/security/dependancies.py

+51-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
from fastapi import Depends, HTTPException
2+
from starlette.status import HTTP_401_UNAUTHORIZED
13
from starlette.requests import Request
24

5+
from app.database.models import User
36
from app.dependencies import get_db
47
from app.internal.security.ouath2 import (
5-
Depends, Session, check_jwt_token, get_authorization_cookie)
8+
Session, get_jwt_token, get_authorization_cookie
9+
)
10+
from app.internal.security import schema
611

712

813
async def is_logged_in(
@@ -11,7 +16,7 @@ async def is_logged_in(
1116
"""
1217
A dependency function protecting routes for only logged in user
1318
"""
14-
await check_jwt_token(db, jwt)
19+
await get_jwt_token(db, jwt)
1520
return True
1621

1722

@@ -21,5 +26,47 @@ async def is_manager(
2126
"""
2227
A dependency function protecting routes for only logged in manager
2328
"""
24-
await check_jwt_token(db, jwt, manager=True)
25-
return True
29+
jwt_payload = await get_jwt_token(db, jwt)
30+
if jwt_payload.get("is_manager"):
31+
return True
32+
raise HTTPException(
33+
status_code=HTTP_401_UNAUTHORIZED,
34+
headers=request.url.path,
35+
detail="You don't have a permition to enter this page")
36+
37+
38+
async def current_user_from_db(
39+
request: Request,
40+
db: Session = Depends(get_db),
41+
jwt: str = Depends(get_authorization_cookie),
42+
) -> User:
43+
"""
44+
Returns logged in User object.
45+
A dependency function protecting routes for only logged in user.
46+
"""
47+
jwt_payload = await get_jwt_token(db, jwt)
48+
username = jwt_payload.get("sub")
49+
user_id = jwt_payload.get("user_id")
50+
db_user = await User.get_by_username(db, username=username)
51+
if db_user and db_user.id == user_id:
52+
return db_user
53+
else:
54+
raise HTTPException(
55+
status_code=HTTP_401_UNAUTHORIZED,
56+
headers=request.url.path,
57+
detail="Your token is incorrect. Please log in again")
58+
59+
60+
async def current_user(
61+
request: Request,
62+
db: Session = Depends(get_db),
63+
jwt: str = Depends(get_authorization_cookie),
64+
) -> schema:
65+
"""
66+
Returns logged in User object.
67+
A dependency function protecting routes for only logged in user.
68+
"""
69+
jwt_payload = await get_jwt_token(db, jwt)
70+
username = jwt_payload.get("sub")
71+
user_id = jwt_payload.get("user_id")
72+
return schema.CurrentUser(user_id=user_id, username=username)

app/internal/security/ouath2.py

+7-17
Original file line numberDiff line numberDiff line change
@@ -63,28 +63,18 @@ def create_jwt_token(
6363
return jwt_token
6464

6565

66-
async def check_jwt_token(
66+
async def get_jwt_token(
6767
db: Session,
68-
token: str = Depends(oauth_schema),
69-
path: bool = None,
70-
manager: bool = False,
71-
) -> User:
68+
token: str = Depends(oauth_schema),
69+
path: Union[bool, str] = None) -> User:
7270
"""
7371
Check whether JWT token is correct.
7472
Returns jwt payloads if correct.
7573
Raises HTTPException if fails to decode.
7674
"""
7775
try:
78-
jwt_payload = jwt.decode(token, JWT_KEY, algorithms=JWT_ALGORITHM)
79-
if not manager:
80-
return True
81-
if jwt_payload.get("is_manager"):
82-
return True
83-
raise HTTPException(
84-
status_code=HTTP_401_UNAUTHORIZED,
85-
headers=path,
86-
detail="You don't have a permition to enter this page",
87-
)
76+
jwt_payload = jwt.decode(
77+
token, JWT_KEY, algorithms=JWT_ALGORITHM)
8878
except InvalidSignatureError:
8979
raise HTTPException(
9080
status_code=HTTP_401_UNAUTHORIZED,
@@ -101,8 +91,8 @@ async def check_jwt_token(
10191
raise HTTPException(
10292
status_code=HTTP_401_UNAUTHORIZED,
10393
headers=path,
104-
detail="Your token is incorrect. Please log in again",
105-
)
94+
detail="Your token is incorrect. Please log in again")
95+
return jwt_payload
10696

10797

10898
async def get_authorization_cookie(request: Request) -> str:

app/internal/security/schema.py

+11-4
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,22 @@
33
from pydantic import BaseModel
44

55

6-
class LoginUser(BaseModel):
6+
class CurrentUser(BaseModel):
77
"""
88
Validating fields types
9-
Returns a User object for signing in.
9+
Returns a user details as a class.
1010
"""
1111
user_id: Optional[int]
12-
is_manager: Optional[bool]
1312
username: str
14-
password: str
1513

1614
class Config:
1715
orm_mode = True
16+
17+
18+
class LoginUser(CurrentUser):
19+
"""
20+
Validating fields types
21+
Returns a User object for signing in.
22+
"""
23+
is_manager: Optional[bool]
24+
password: str

app/static/style.css

+11
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,22 @@ p {
9696
border: none;
9797
background-color: whitesmoke;
9898
}
99+
100+
.error-message {
101+
line-height: 0;
102+
color: red;
103+
padding-left: 12.5rem;
104+
}
105+
106+
.subtitle {
107+
font-size: 1.25rem;
108+
}
99109

100110
.error-message {
101111
line-height: 0;
102112
color: red;
103113
padding-left: 12.5rem;
114+
margin-bottom: 1em;
104115
}
105116

106117
.input-upload-file {

git

Whitespace-only changes.

tests/security_testing_routes.py

+39-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
from app.internal.security.dependancies import (is_manager, is_logged_in)
21
from fastapi import APIRouter, Depends, Request
32

3+
from app.internal.security.dependancies import (
4+
current_user, current_user_from_db,
5+
is_logged_in, is_manager, User
6+
)
7+
48

59
"""
610
These routes are for security testing.
@@ -14,20 +18,43 @@
1418
)
1519

1620

17-
@router.get('/protected')
18-
async def protected_route(
21+
@router.get('/is_logged_in')
22+
async def is_logged_in(
1923
request: Request, user: bool = Depends(is_logged_in)):
20-
# This is how to protect route for logged in user only.
21-
# Dependency will return True.
22-
# if user not looged-in, will be redirected to login route.
24+
"""This is how to protect route for logged in user only.
25+
Dependency will return True.
26+
if user not looged-in, will be redirected to login route.
27+
"""
2328
return {"user": user}
2429

2530

26-
@router.get('/manager')
27-
async def manager_route(
31+
@router.get('/is_manager')
32+
async def is_manager(
2833
request: Request, user: bool = Depends(is_manager)):
29-
# This is how to protect route for logged in manager only.
30-
# Dependency will return True.
31-
# if user not looged-in, or have no manager permission,
32-
# will be redirected to login route.
34+
"""This is how to protect route for logged in manager only.
35+
Dependency will return True.
36+
if user not looged-in, or have no manager permission,
37+
will be redirected to login route.
38+
"""
3339
return {"manager": user}
40+
41+
42+
@router.get('/current_user_from_db')
43+
async def current_user_from_db(
44+
request: Request, user: User = Depends(current_user_from_db)):
45+
"""This is how to protect route for logged in user only.
46+
Dependency will return User object.
47+
if user not looged-in, will be redirected to login route.
48+
"""
49+
return {"user": user.username}
50+
51+
52+
@router.get('/current_user')
53+
async def current_user(
54+
request: Request, user: User = Depends(current_user)):
55+
"""This is how to protect route for logged in user only.
56+
Dependency will return schema.CurrentUser object,
57+
contains user_id and username.
58+
if user not looged-in, will be redirected to login route.
59+
"""
60+
return {"user": user.username}

0 commit comments

Comments
 (0)