11import logging
2- from datetime import datetime , timedelta , timezone
3- from typing import Any , AsyncGenerator , Callable , Dict , Optional , cast
2+ from collections .abc import AsyncGenerator , Callable
3+ from datetime import UTC , datetime
4+ from typing import Any , Dict , Optional , cast
45
56from fastapi import APIRouter , Cookie , Depends , Request , Response
67from fastapi .responses import RedirectResponse
@@ -40,10 +41,10 @@ class AdminSite:
4041 Args:
4142 database_config: Database configuration for admin interface
4243 templates_directory: Path to template files
43- models: Dictionary of registered models and their configurations
44- admin_authentication: Authentication handler instance
45- mount_path: URL path prefix for admin interface (e.g. "/admin")
46- theme: UI theme name ("dark-theme" or "light-theme")
44+ models: Dictionary of registered models
45+ admin_authentication: Authentication handler
46+ mount_path: URL prefix for admin routes
47+ theme: Active UI theme
4748 secure_cookies: Enable secure cookie flags
4849 event_integration: Optional event logging integration
4950
@@ -54,7 +55,6 @@ class AdminSite:
5455 models: Dictionary of registered models
5556 admin_user_service: Service for user management
5657 admin_authentication: Authentication handler
57- token_service: JWT token service
5858 mount_path: URL prefix for admin routes
5959 theme: Active UI theme
6060 event_integration: Event logging handler
@@ -108,27 +108,28 @@ def __init__(
108108 secure_cookies : bool ,
109109 event_integration : Optional [Any ] = None ,
110110 ) -> None :
111- self .db_config = database_config
112- self .router = APIRouter ()
113- self .templates = Jinja2Templates (directory = templates_directory )
114- self .models = models
115- self .admin_user_service = AdminUserService (db_config = database_config )
116- self .admin_authentication = admin_authentication
111+ self .db_config : DatabaseConfig = database_config
112+ self .router : APIRouter = APIRouter ()
113+ self .templates : Jinja2Templates = Jinja2Templates (directory = templates_directory )
114+ self .models : Dict [str , Any ] = models
115+ self .admin_user_service : AdminUserService = AdminUserService (
116+ db_config = database_config
117+ )
118+ self .admin_authentication : AdminAuthentication = admin_authentication
117119 self .admin_user_service = admin_authentication .user_service
118- self .token_service = admin_authentication .token_service
119120
120- self .mount_path = mount_path
121- self .theme = theme
122- self .event_integration = event_integration
121+ self .mount_path : str = mount_path
122+ self .theme : str = theme
123+ self .event_integration : Optional [ Any ] = event_integration
123124
124- self .session_manager = SessionManager (
125+ self .session_manager : SessionManager = SessionManager (
125126 self .db_config ,
126127 max_sessions_per_user = 5 ,
127128 session_timeout_minutes = 30 ,
128129 cleanup_interval_minutes = 15 ,
129130 )
130131
131- self .secure_cookies = secure_cookies
132+ self .secure_cookies : bool = secure_cookies
132133
133134 def setup_routes (self ) -> None :
134135 """
@@ -205,7 +206,7 @@ def login_page(self) -> EndpointCallable:
205206
206207 Notes:
207208 - Validates credentials and creates user session on success
208- - Sets secure cookies with tokens
209+ - Sets secure cookies with session ID
209210 - Logs login attempts if event tracking enabled
210211 """
211212
@@ -237,13 +238,7 @@ async def login_page_inner(
237238 )
238239
239240 request .state .user = user
240- logger .info ("User authenticated successfully, creating token" )
241- access_token_expires = timedelta (
242- minutes = self .token_service .ACCESS_TOKEN_EXPIRE_MINUTES
243- )
244- access_token = await self .token_service .create_access_token (
245- data = {"sub" : user ["username" ]}, expires_delta = access_token_expires
246- )
241+ logger .info ("User authenticated successfully, creating session" )
247242
248243 try :
249244 logger .info ("Creating user session..." )
@@ -253,7 +248,7 @@ async def login_page_inner(
253248 metadata = {
254249 "login_type" : "password" ,
255250 "username" : user ["username" ],
256- "creation_time" : datetime .now (timezone . utc ).isoformat (),
251+ "creation_time" : datetime .now (UTC ).isoformat (),
257252 },
258253 )
259254
@@ -267,24 +262,16 @@ async def login_page_inner(
267262 url = f"/{ self .mount_path } /" , status_code = 303
268263 )
269264
270- max_age_int = int (access_token_expires .total_seconds ())
271-
272- response .set_cookie (
273- key = "access_token" ,
274- value = f"Bearer { access_token } " ,
275- httponly = True ,
276- secure = self .secure_cookies ,
277- max_age = max_age_int ,
278- path = f"/{ self .mount_path } " ,
279- samesite = "lax" ,
265+ session_timeout_seconds = int (
266+ self .session_manager .session_timeout .total_seconds ()
280267 )
281268
282269 response .set_cookie (
283270 key = "session_id" ,
284271 value = session .session_id ,
285272 httponly = True ,
286273 secure = self .secure_cookies ,
287- max_age = max_age_int ,
274+ max_age = session_timeout_seconds ,
288275 path = f"/{ self .mount_path } " ,
289276 samesite = "lax" ,
290277 )
@@ -341,31 +328,9 @@ async def logout_endpoint_inner(
341328 request : Request ,
342329 response : Response ,
343330 db : AsyncSession = Depends (self .db_config .get_admin_db ),
344- access_token : Optional [str ] = Cookie (None ),
345331 session_id : Optional [str ] = Cookie (None ),
346332 event_integration : Optional [Any ] = Depends (lambda : self .event_integration ),
347333 ) -> RouteResponse :
348- if access_token :
349- token = (
350- access_token .replace ("Bearer " , "" )
351- if access_token .startswith ("Bearer " )
352- else access_token
353- )
354- token_data = await self .token_service .verify_token (token , db )
355- if token_data :
356- if "@" in token_data .username_or_email :
357- user = await self .db_config .crud_users .get (
358- db = db , email = token_data .username_or_email
359- )
360- else :
361- user = await self .db_config .crud_users .get (
362- db = db , username = token_data .username_or_email
363- )
364- if user :
365- request .state .user = user
366-
367- await self .token_service .blacklist_token (token , db )
368-
369334 if session_id :
370335 await self .session_manager .terminate_session (
371336 db = db , session_id = session_id
@@ -375,7 +340,6 @@ async def logout_endpoint_inner(
375340 url = f"/{ self .mount_path } /login" , status_code = 303
376341 )
377342
378- response .delete_cookie (key = "access_token" , path = f"/{ self .mount_path } " )
379343 response .delete_cookie (key = "session_id" , path = f"/{ self .mount_path } " )
380344
381345 return response
@@ -401,27 +365,18 @@ async def admin_login_page_inner(
401365 db : AsyncSession = Depends (self .db_config .get_admin_db ),
402366 ) -> RouteResponse :
403367 try :
404- access_token = request .cookies .get ("access_token" )
405368 session_id = request .cookies .get ("session_id" )
406369
407- if access_token and session_id :
408- token = (
409- access_token .split (" " )[1 ]
410- if access_token .startswith ("Bearer " )
411- else access_token
370+ if session_id :
371+ is_valid_session = await self .session_manager .validate_session (
372+ db = db , session_id = session_id
412373 )
413- token_data = await self .token_service .verify_token (token , db )
414374
415- if token_data :
416- is_valid_session = await self . session_manager . validate_session (
417- db = db , session_id = session_id
375+ if is_valid_session :
376+ return RedirectResponse (
377+ url = f"/ { self . mount_path } /" , status_code = 303
418378 )
419379
420- if is_valid_session :
421- return RedirectResponse (
422- url = f"/{ self .mount_path } /" , status_code = 303
423- )
424-
425380 except Exception :
426381 pass
427382
0 commit comments