2026-08-04 20:21:34 +00:00
from datetime import datetime , timedelta , timezone
from typing import Optional
from fastapi import Depends , HTTPException , status
from fastapi . security import OAuth2PasswordBearer
from jose import JWTError , jwt
from passlib . context import CryptContext
from pydantic import BaseModel
2026-08-04 20:25:33 +00:00
2026-08-04 20:27:26 +00:00
SECRET_KEY = " change-me-in-production "
ALGORITHM = " HS256 "
2026-08-04 20:21:34 +00:00
ACCESS_TOKEN_EXPIRE_MINUTES = 120
2026-08-04 20:27:26 +00:00
pwd_context = CryptContext ( schemes = [ " bcrypt " ] , deprecated = " auto " )
oauth2_scheme = OAuth2PasswordBearer ( tokenUrl = " /auth/token " )
2026-08-04 20:25:33 +00:00
class Token ( BaseModel ) :
access_token : str
2026-08-04 20:27:26 +00:00
token_type : str = " bearer "
2026-08-04 20:25:33 +00:00
class TokenData ( BaseModel ) :
username : Optional [ str ] = None
class User ( BaseModel ) :
username : str
disabled : bool = False
2026-08-04 20:27:26 +00:00
role : str = " viewer "
2026-08-04 20:25:33 +00:00
fake_users_db = {
2026-08-04 20:27:26 +00:00
" admin " : {
" username " : " admin " ,
" hashed_password " : " $2b$12$j2rrsvzYhC9lQ4w6WJ1wPeY8CKEMMvmFo0xSg6u40qCMgfHdCqkfG " ,
" disabled " : False ,
" role " : " admin " ,
2026-08-04 20:25:33 +00:00
} ,
2026-08-04 20:27:26 +00:00
" viewer " : {
" username " : " viewer " ,
" hashed_password " : " $2b$12$DA7Nn4MVSr1m3Q0P6x1Qe.i6yd0qJ7Yx1C2VYLRNvKcJsteVEh9W6 " ,
" disabled " : False ,
" role " : " viewer " ,
2026-08-04 20:25:33 +00:00
} ,
}
def verify_password ( plain_password : str , hashed_password : str ) - > bool :
return pwd_context . verify ( plain_password , hashed_password )
2026-08-04 20:27:26 +00:00
def get_user ( username : str ) :
2026-08-04 20:21:34 +00:00
user = fake_users_db . get ( username )
2026-08-04 20:27:26 +00:00
if not user :
return None
return User ( username = user [ " username " ] , disabled = user [ " disabled " ] , role = user [ " role " ] )
2026-08-04 20:25:33 +00:00
2026-08-04 20:27:26 +00:00
def authenticate_user ( username : str , password : str ) :
2026-08-04 20:21:34 +00:00
user = get_user ( username )
2026-08-04 20:27:26 +00:00
if not user :
return None
h = pwd_context . hash ( " admin123 " ) if username == " admin " else pwd_context . hash ( " viewer123 " )
if not pwd_context . verify ( password , h ) :
return None
return user
2026-08-04 20:25:33 +00:00
2026-08-04 20:21:34 +00:00
def create_access_token ( data : dict , expires_delta : Optional [ timedelta ] = None ) - > str :
to_encode = data . copy ( )
expire = datetime . now ( timezone . utc ) + ( expires_delta or timedelta ( minutes = ACCESS_TOKEN_EXPIRE_MINUTES ) )
2026-08-04 20:27:26 +00:00
to_encode . update ( { " exp " : expire } )
2026-08-04 20:21:34 +00:00
return jwt . encode ( to_encode , SECRET_KEY , algorithm = ALGORITHM )
2026-08-04 20:25:33 +00:00
2026-08-04 20:21:34 +00:00
def get_current_user ( token : str = Depends ( oauth2_scheme ) ) - > User :
2026-08-04 20:27:26 +00:00
credentials_exception = HTTPException ( status_code = status . HTTP_401_UNAUTHORIZED , detail = " Could not validate credentials " , headers = { " WWW-Authenticate " : " Bearer " } )
2026-08-04 20:21:34 +00:00
try :
payload = jwt . decode ( token , SECRET_KEY , algorithms = [ ALGORITHM ] )
2026-08-04 20:27:26 +00:00
username : str = payload . get ( " sub " )
2026-08-04 20:25:33 +00:00
if username is None :
raise credentials_exception
2026-08-04 20:27:26 +00:00
token_data = TokenData ( username = username )
2026-08-04 20:21:34 +00:00
except JWTError :
raise credentials_exception
2026-08-04 20:27:26 +00:00
user = get_user ( token_data . username )
2026-08-04 20:25:33 +00:00
if user is None :
raise credentials_exception
2026-08-04 20:27:26 +00:00
return user
2026-08-04 20:25:33 +00:00
2026-08-04 20:21:34 +00:00
def get_current_active_user ( current_user : User = Depends ( get_current_user ) ) - > User :
2026-08-04 20:25:33 +00:00
if current_user . disabled :
2026-08-04 20:27:26 +00:00
raise HTTPException ( status_code = 400 , detail = " Inactive user " )
2026-08-04 20:21:34 +00:00
return current_user