1 |
2fe004b1
|
Matej Zeman
|
from fastapi import Depends, APIRouter, Form
|
2 |
|
|
from fastapi import Request
|
3 |
5dc6d077
|
Matej Zeman
|
from fastapi.responses import HTMLResponse, RedirectResponse
|
4 |
2fe004b1
|
Matej Zeman
|
from fastapi.templating import Jinja2Templates
|
5 |
|
|
from fastapi_jwt_auth import AuthJWT
|
6 |
cbd239c6
|
Matej Zeman
|
from sqlalchemy.orm import Session
|
7 |
|
|
from sql_app import crud
|
8 |
|
|
from passlib.context import CryptContext
|
9 |
2fe004b1
|
Matej Zeman
|
from pydantic import BaseModel
|
10 |
cbd239c6
|
Matej Zeman
|
from ..database import SessionLocal, engine
|
11 |
2fe004b1
|
Matej Zeman
|
|
12 |
|
|
# Path to html templates used in this file
|
13 |
|
|
templates = Jinja2Templates(directory="templates/auth")
|
14 |
|
|
|
15 |
cbd239c6
|
Matej Zeman
|
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
16 |
|
|
|
17 |
2fe004b1
|
Matej Zeman
|
# prefix used for all endpoints in this file
|
18 |
|
|
auth = APIRouter(prefix="")
|
19 |
|
|
|
20 |
|
|
|
21 |
cbd239c6
|
Matej Zeman
|
# Dependency
|
22 |
|
|
def get_db():
|
23 |
|
|
db = SessionLocal()
|
24 |
|
|
try:
|
25 |
|
|
yield db
|
26 |
|
|
finally:
|
27 |
|
|
db.close()
|
28 |
|
|
|
29 |
2fe004b1
|
Matej Zeman
|
|
30 |
|
|
class Settings(BaseModel):
|
31 |
|
|
authjwt_secret_key: str = "secret"
|
32 |
|
|
# Configure application to store and get JWT from cookies
|
33 |
|
|
authjwt_token_location: set = {"cookies"}
|
34 |
|
|
# Disable CSRF Protection for this example. default is True
|
35 |
|
|
authjwt_cookie_csrf_protect: bool = False
|
36 |
|
|
|
37 |
|
|
|
38 |
|
|
@AuthJWT.load_config
|
39 |
|
|
def get_config():
|
40 |
|
|
return Settings()
|
41 |
|
|
|
42 |
cbd239c6
|
Matej Zeman
|
|
43 |
7fe7be79
|
zemanm98@students.zcu.cz
|
# admin username and password
|
44 |
2fe004b1
|
Matej Zeman
|
fake_users_db = {
|
45 |
|
|
"admin": {
|
46 |
|
|
"username": "admin",
|
47 |
|
|
"password": "admin"
|
48 |
|
|
}
|
49 |
|
|
}
|
50 |
|
|
|
51 |
|
|
|
52 |
cbd239c6
|
Matej Zeman
|
def verify_password(plain_password, hashed_password):
|
53 |
0fcb708f
|
Matej Zeman
|
"""
|
54 |
|
|
Verifies plain text password with hashed password
|
55 |
|
|
"""
|
56 |
cbd239c6
|
Matej Zeman
|
return pwd_context.verify(plain_password, hashed_password)
|
57 |
|
|
|
58 |
|
|
|
59 |
|
|
def get_hash_password(password):
|
60 |
0fcb708f
|
Matej Zeman
|
"""
|
61 |
|
|
Returns hashed password
|
62 |
|
|
"""
|
63 |
cbd239c6
|
Matej Zeman
|
return pwd_context.hash(password)
|
64 |
|
|
|
65 |
|
|
|
66 |
|
|
def auth_user(db, username: str, password: str):
|
67 |
0fcb708f
|
Matej Zeman
|
"""
|
68 |
|
|
Determines if given password belongs to user with given username
|
69 |
|
|
"""
|
70 |
cbd239c6
|
Matej Zeman
|
user = crud.find_user(db, username)
|
71 |
|
|
if not user:
|
72 |
|
|
return None
|
73 |
|
|
if not verify_password(password, user.password):
|
74 |
|
|
return None
|
75 |
|
|
return user
|
76 |
|
|
|
77 |
|
|
|
78 |
|
|
@auth.get("/signup", response_class=HTMLResponse)
|
79 |
|
|
async def signup_get(request: Request):
|
80 |
|
|
"""
|
81 |
|
|
return html template for signup
|
82 |
|
|
"""
|
83 |
|
|
return templates.TemplateResponse("signup.html", {"request": request})
|
84 |
|
|
|
85 |
|
|
|
86 |
|
|
@auth.post("/signup", response_class=HTMLResponse)
|
87 |
|
|
async def signup(username: str = Form(...), password: str = Form(...), db: Session = Depends(get_db)):
|
88 |
|
|
"""
|
89 |
|
|
Endpoint called form signup template. Creates new user with role guest that can be changed by admin user
|
90 |
|
|
"""
|
91 |
|
|
users = crud.get_users(db, 0, 100)
|
92 |
|
|
users_names = []
|
93 |
|
|
for u in users:
|
94 |
|
|
users_names.append(u.username)
|
95 |
|
|
if username not in users_names:
|
96 |
|
|
new_user = crud.create_user(db, username, get_hash_password(password), "guest")
|
97 |
|
|
if new_user is None:
|
98 |
|
|
print("something went wrong")
|
99 |
|
|
return """
|
100 |
|
|
<html>
|
101 |
|
|
<head>
|
102 |
|
|
<title>Signup</title>
|
103 |
|
|
</head>
|
104 |
|
|
<body>
|
105 |
2552e614
|
Matej Zeman
|
<h1>New user created. You can go back to previous page.</h1>
|
106 |
cbd239c6
|
Matej Zeman
|
<form action="/logs-web" method="get">
|
107 |
2552e614
|
Matej Zeman
|
<input type="submit" value="Home Page" />
|
108 |
cbd239c6
|
Matej Zeman
|
</form>
|
109 |
|
|
</body>
|
110 |
|
|
</html>
|
111 |
|
|
"""
|
112 |
|
|
else:
|
113 |
|
|
return """
|
114 |
|
|
<html>
|
115 |
|
|
<head>
|
116 |
|
|
<title>Signup</title>
|
117 |
|
|
</head>
|
118 |
|
|
<body>
|
119 |
2552e614
|
Matej Zeman
|
<h1>Username taken. Try to choose different username.</h1>
|
120 |
cbd239c6
|
Matej Zeman
|
<form action="/logs-web" method="get">
|
121 |
2552e614
|
Matej Zeman
|
<input type="submit" value="Home Page" />
|
122 |
cbd239c6
|
Matej Zeman
|
</form>
|
123 |
|
|
</body>
|
124 |
|
|
</html>
|
125 |
|
|
"""
|
126 |
|
|
|
127 |
2fe004b1
|
Matej Zeman
|
@auth.get("/login", response_class=HTMLResponse)
|
128 |
|
|
async def login_get(request: Request):
|
129 |
7fe7be79
|
zemanm98@students.zcu.cz
|
"""
|
130 |
|
|
return html template for login
|
131 |
|
|
"""
|
132 |
2fe004b1
|
Matej Zeman
|
return templates.TemplateResponse("login.html", {"request": request})
|
133 |
|
|
|
134 |
|
|
|
135 |
|
|
@auth.post("/login", response_class=HTMLResponse)
|
136 |
cbd239c6
|
Matej Zeman
|
async def login(username: str = Form(...), password: str = Form(...), db: Session = Depends(get_db),
|
137 |
|
|
Authorize: AuthJWT = Depends()):
|
138 |
7fe7be79
|
zemanm98@students.zcu.cz
|
"""
|
139 |
|
|
Endpoint called from login template. Checks if given username and password aligns with admin
|
140 |
|
|
username and password and returns token for browser according to given username and password
|
141 |
|
|
"""
|
142 |
cbd239c6
|
Matej Zeman
|
user = auth_user(db, username, password)
|
143 |
|
|
if user != None:
|
144 |
|
|
if user.role == "admin":
|
145 |
2fe004b1
|
Matej Zeman
|
access_token = Authorize.create_access_token(subject="admin", expires_time=False)
|
146 |
|
|
refresh_token = Authorize.create_refresh_token(subject="admin", expires_time=False)
|
147 |
|
|
else:
|
148 |
cbd239c6
|
Matej Zeman
|
access_token = Authorize.create_access_token(subject="guest", expires_time=False)
|
149 |
|
|
refresh_token = Authorize.create_refresh_token(subject="guest", expires_time=False)
|
150 |
2fe004b1
|
Matej Zeman
|
else:
|
151 |
cbd239c6
|
Matej Zeman
|
usr = fake_users_db.get(username)
|
152 |
1ad8f5ba
|
Matej Zeman
|
if usr is not None and (usr["username"] == username and usr["password"] == password):
|
153 |
|
|
access_token = Authorize.create_access_token(subject="admin", expires_time=False)
|
154 |
|
|
refresh_token = Authorize.create_refresh_token(subject="admin", expires_time=False)
|
155 |
cbd239c6
|
Matej Zeman
|
else:
|
156 |
|
|
return """
|
157 |
|
|
<html>
|
158 |
|
|
<head>
|
159 |
|
|
<title>Login</title>
|
160 |
|
|
</head>
|
161 |
|
|
<body>
|
162 |
|
|
<h1>Wrong Username or Password</h1>
|
163 |
|
|
<form action="/login" method="get">
|
164 |
2552e614
|
Matej Zeman
|
<input type="submit" value="Log again" />
|
165 |
|
|
</form>
|
166 |
|
|
<form action="/login" method="get">
|
167 |
|
|
<input type="submit" value="Home Page" />
|
168 |
cbd239c6
|
Matej Zeman
|
</form>
|
169 |
|
|
</body>
|
170 |
|
|
</html>
|
171 |
|
|
"""
|
172 |
2fe004b1
|
Matej Zeman
|
|
173 |
|
|
# Set the JWT cookies in the response
|
174 |
|
|
Authorize.set_access_cookies(access_token)
|
175 |
|
|
Authorize.set_refresh_cookies(refresh_token)
|
176 |
|
|
return """
|
177 |
|
|
<html>
|
178 |
|
|
<head>
|
179 |
|
|
<title>Login</title>
|
180 |
|
|
</head>
|
181 |
|
|
<body>
|
182 |
2552e614
|
Matej Zeman
|
<h1>Now you are logged in, you can continue to previous page.</h1>
|
183 |
2fe004b1
|
Matej Zeman
|
<form action="/logs-web" method="get">
|
184 |
2552e614
|
Matej Zeman
|
<input type="submit" value="Home Page" />
|
185 |
2fe004b1
|
Matej Zeman
|
</form>
|
186 |
|
|
</body>
|
187 |
|
|
</html>
|
188 |
|
|
"""
|
189 |
|
|
|
190 |
|
|
|
191 |
|
|
@auth.post('/refresh')
|
192 |
|
|
def refresh(Authorize: AuthJWT = Depends()):
|
193 |
7fe7be79
|
zemanm98@students.zcu.cz
|
"""
|
194 |
|
|
endpoint for refreshing browser token. Not used at the moment since lifetime of given tokens are
|
195 |
|
|
unlimited.
|
196 |
|
|
"""
|
197 |
2fe004b1
|
Matej Zeman
|
Authorize.jwt_refresh_token_required()
|
198 |
|
|
current_user = Authorize.get_jwt_subject()
|
199 |
|
|
new_access_token = Authorize.create_access_token(subject=current_user)
|
200 |
|
|
# Set the JWT cookies in the response
|
201 |
|
|
Authorize.set_access_cookies(new_access_token)
|
202 |
|
|
return {"msg": "The token has been refresh"}
|
203 |
|
|
|
204 |
|
|
|
205 |
|
|
@auth.get('/logout', response_class=HTMLResponse)
|
206 |
|
|
def logout(Authorize: AuthJWT = Depends()):
|
207 |
|
|
"""
|
208 |
7fe7be79
|
zemanm98@students.zcu.cz
|
Endpoint for deleting cookie token with acces role.
|
209 |
2fe004b1
|
Matej Zeman
|
"""
|
210 |
|
|
Authorize.jwt_optional()
|
211 |
|
|
|
212 |
|
|
Authorize.unset_jwt_cookies()
|
213 |
|
|
return """
|
214 |
|
|
<html>
|
215 |
|
|
<head>
|
216 |
|
|
<title>Logout</title>
|
217 |
|
|
</head>
|
218 |
|
|
<body>
|
219 |
|
|
<h1>Logged Out</h1>
|
220 |
|
|
<form action="/logs-web" method="get">
|
221 |
|
|
<input type="submit" value="Back" />
|
222 |
|
|
</form>
|
223 |
|
|
</body>
|
224 |
|
|
</html>
|
225 |
cbd239c6
|
Matej Zeman
|
"""
|