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:21:34 +00:00
SECRET_KEY = ' change-me-in-production '
ALGORITHM = ' HS256 '
ACCESS_TOKEN_EXPIRE_MINUTES = 120
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
token_type : str = ' bearer '
class TokenData ( BaseModel ) :
username : Optional [ str ] = None
class User ( BaseModel ) :
username : str
disabled : bool = False
role : str = ' viewer '
class UserInDB ( User ) :
hashed_password : str
fake_users_db = {
' admin ' : {
' username ' : ' admin ' ,
' hashed_password ' : ' $2b$12$j2rrsvzYhC9lQ4w6WJ1wPeY8CKEMMvmFo0xSg6u40qCMgfHdCqkfG ' ,
' disabled ' : False ,
' role ' : ' admin ' ,
} ,
' viewer ' : {
' username ' : ' viewer ' ,
' hashed_password ' : ' $2b$12$DA7Nn4MVSr1m3Q0P6x1Qe.i6yd0qJ7Yx1C2VYLRNvKcJsteVEh9W6 ' ,
' disabled ' : False ,
' role ' : ' viewer ' ,
} ,
}
def verify_password ( plain_password : str , hashed_password : str ) - > bool :
return pwd_context . verify ( plain_password , hashed_password )
2026-08-04 20:21:34 +00:00
def get_user ( username : str ) - > Optional [ UserInDB ] :
user = fake_users_db . get ( username )
return UserInDB ( * * user ) if user else None
2026-08-04 20:25:33 +00:00
2026-08-04 20:21:34 +00:00
def authenticate_user ( username : str , password : str ) - > Optional [ UserInDB ] :
user = get_user ( username )
return user if user and verify_password ( password , user . hashed_password ) else None
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 ) )
to_encode . update ( { ' exp ' : expire } )
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 :
credentials_exception = HTTPException ( status_code = status . HTTP_401_UNAUTHORIZED , detail = ' Could not validate credentials ' , headers = { ' WWW-Authenticate ' : ' Bearer ' } )
try :
payload = jwt . decode ( token , SECRET_KEY , algorithms = [ ALGORITHM ] )
username : str = payload . get ( ' sub ' )
2026-08-04 20:25:33 +00:00
if username is None :
raise credentials_exception
2026-08-04 20:21:34 +00:00
except JWTError :
raise credentials_exception
user = get_user ( username )
2026-08-04 20:25:33 +00:00
if user is None :
raise credentials_exception
2026-08-04 20:21:34 +00:00
return User ( username = user . username , disabled = user . disabled , role = user . role )
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 :
raise HTTPException ( status_code = 400 , detail = ' Inactive user ' )
2026-08-04 20:21:34 +00:00
return current_user