from fastapi import Depends, HTTPException, status, Security from fastapi.security import OAuth2AuthorizationCodeBearer, SecurityScopes from datetime import datetime, timedelta from passlib.context import CryptContext from jose import jwt, JWTError from models.other import TokenData from models.request import User from database.neo4j import db import os from security.secrets import get_secrets secrets = get_secrets() # password context crypt_context = CryptContext( schemes=["bcrypt"], deprecated="auto") oauth2_scheme = OAuth2AuthorizationCodeBearer( authorizationUrl='foundation/oauth2/auth', tokenUrl='foundation/oauth2/token', scopes={ 'test': 'Full access, but only test data.', 'user': 'Normal user access.', 'admin': 'Full access to all.' }) credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", headers={"WWW-Authenticate": "Bearer"}, ) def create_access_token(data: dict, expires_delta: timedelta | None = None): payload = data.copy() if expires_delta: expire = datetime.utcnow() + expires_delta else: expire = datetime.utcnow() + timedelta(minutes=15) payload.update({"expires": str(expire)}) encoded_jwt = jwt.encode(payload, secrets['security_secret_key'], algorithm=os.environ['SECURITY_ALGORITHM']) return encoded_jwt def verify_password(plain_password, hashed_password): return crypt_context.verify(plain_password, hashed_password) # see if given password matches password of username def authenticate_user(username_to_authenticate: str, plain_password: str) -> dict or False: # maybe put an exception around this code below user = db.get_user(username_to_authenticate) if not user: return False if verify_password(plain_password, user.hashed_password): return user return False def get_password_hash(plain_password): return crypt_context.hash(plain_password) # get current user from token async def get_current_user(security_scopes: SecurityScopes, token: str = Depends(oauth2_scheme)): print("\n\n\nGets current user.") if security_scopes.scopes: authenticate_value = f'Bearer scope="{security_scopes.scope_str}"' else: authenticate_value = f"Bearer" print(authenticate_value) try: print('Token: ', token) payload = jwt.decode(token, secrets['security_secret_key'], algorithms=[os.environ['SECURITY_ALGORITHM']]) username_from_token: str = payload.get("username") print('Token username: ', username_from_token) if username_from_token is None: raise credentials_exception token_scopes = payload.get("scopes", []) print('Token scopes: ', token_scopes) token_data = TokenData(scopes=token_scopes, username=username_from_token) except JWTError: print('JWTError') raise credentials_exception user = db.get_user(username=token_data.username) if user is None: raise credentials_exception for scope in security_scopes.scopes: if scope not in token_data.scopes: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Not enough permissions", headers={"WWW-Authenticate": authenticate_value}, ) return user async def get_current_active_user(current_user: User = Security(get_current_user, scopes=["test"])): if current_user.disabled: raise HTTPException(status_code=400, detail="Inactive user") return current_user