Done for Login and notification system

This commit is contained in:
2024-05-30 14:35:48 +00:00
parent d5c967d2e5
commit 9400113a57
52 changed files with 2204 additions and 616 deletions

View File

@ -0,0 +1 @@
from .message_code import *

View File

@ -1,20 +1,76 @@
from fastapi import Depends, HTTPException, Request
from sqlalchemy.orm import Session
from fastapi import Depends, HTTPException, Request, status
from fastapi.security import OAuth2PasswordBearer
from fuware.core.config import get_app_settings
from fuware.db.db_setup import generate_session
from fuware.core import MessageCode
import jwt
from fuware.services.user.user_service import UserService
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/token")
oauth2_scheme_soft_fail = OAuth2PasswordBearer(tokenUrl="/api/auth/token", auto_error=False)
ALGORITHM = "HS256"
settings = get_app_settings()
async def get_auth_user(request: Request, db: Session = Depends(generate_session)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
async def is_logged_in(token: str = Depends(oauth2_scheme_soft_fail)) -> bool:
try:
payload = jwt.decode(token, settings.SECRET, algorithms=[ALGORITHM])
user_id: str = payload.get("sub")
exp: int = payload.get("exp")
if exp is not None:
try:
user_service = UserService()
user = user_service.get_by_id(user_id)
if not user:
raise credentials_exception
if user.is_lock is True:
raise HTTPException(status_code=status.HTTP_423_LOCKED, detail=MessageCode.ACCOUNT_LOCK)
except Exception:
return credentials_exception
return user
except Exception:
raise credentials_exception
async def get_current_user(request: Request, token: str | None = Depends(oauth2_scheme_soft_fail)):
"""verify that user has a valid session"""
session_id = request.cookies.get(settings.COOKIE_KEY)
if not session_id:
raise HTTPException(status_code=401, detail="Unauthorized")
# decrypt_user = decryptString(session_id).split(',')
# db_user = get_user_by_username(db, decrypt_user[0])
# if not db_user:
# raise HTTPException(status_code=403)
# if not verify_password(decrypt_user[1], db_user.password):
# raise HTTPException(status_code=401, detail="Your username or password input is wrong!")
return True
if token is None and settings.COOKIE_KEY in request.cookies:
# Try extract from cookie
token = request.cookies.get(settings.COOKIE_KEY, "")
else:
token = token or ""
try:
payload = jwt.decode(token, settings.SECRET, algorithms=[ALGORITHM])
user_id: str = payload.get("sub")
exp: int = payload.get("exp")
if user_id is None or exp is None:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="credentials have expired",
)
user_service = UserService()
user = user_service.get_by_id(user_id)
if not user:
raise credentials_exception
if user.is_lock is True:
raise HTTPException(status_code=status.HTTP_423_LOCKED, detail=MessageCode.ACCOUNT_LOCK)
return user
except jwt.ExpiredSignatureError:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="credentials have expired",
)
except Exception:
raise credentials_exception

View File

@ -1,8 +1,6 @@
class MessageCode:
class MessageCode():
CREATED_USER: str = 'CREATED_USER'
WRONG_INPUT: str = 'LOGIN_WRONG'
ACCOUNT_LOCK: str = 'USER_LOCK'
def message_code():
return MessageCode()
REFRESH_TOKEN_EXPIRED: str = 'REFRESH_TOKEN_EXPIRED'

View File

@ -1 +1 @@
from .hasher import get_hasher
from .security import *

View File

@ -0,0 +1,46 @@
import secrets
from datetime import datetime, timedelta, timezone
from pathlib import Path
import jwt
from fuware.core.config import get_app_settings
from fuware.core import root_logger
from fuware.core.security.hasher import get_hasher
ALGORITHM = "HS256"
logger = root_logger.get_logger("security")
settings = get_app_settings()
def create_access_token(data: dict, expires_delta: timedelta | None = None) -> str:
settings = get_app_settings()
to_encode = data.copy()
expires_delta = expires_delta or timedelta(minutes=settings.EXP_TOKEN)
expire = datetime.now(timezone.utc) + expires_delta
to_encode["exp"] = expire
return jwt.encode(to_encode, settings.SECRET, algorithm=ALGORITHM)
def create_refresh_token(data: dict) -> str:
return create_access_token(data, expires_delta=timedelta(days=settings.EXP_REFRESH))
def create_file_token(file_path: Path) -> str:
token_data = {"file": str(file_path)}
return create_access_token(token_data, expires_delta=timedelta(minutes=30))
def hash_password(password: str) -> str:
"""Takes in a raw password and hashes it. Used prior to saving a new password to the database."""
return get_hasher().hash(password)
def url_safe_token() -> str:
"""Generates a cryptographic token without embedded data. Used for password reset tokens and invitation tokens"""
return secrets.token_urlsafe(24)
def verify_token(exp: int):
expried = datetime.fromtimestamp(exp / 1e3)
return expried < datetime.now(timezone.utc)

View File

@ -7,7 +7,7 @@ def determine_secrets(production: bool) -> str:
if not production:
return "shh-secret-test-key"
return "oWNhXlfo666JlMHk6UHYxeNB6z_CA2MisDDZJe4N0yc="
return "1d00e664fb3b07aff5a191755ea72f9c4bc85a3f36868308d0b2c417aed3419e"
def determine_cookie(production: bool) -> str:
if not production:
@ -31,6 +31,10 @@ class AppSettings(BaseSettings):
SECRET: str
COOKIE_KEY: str
EXP_TOKEN: int = 30
"""in minutes, default is 30 minutes"""
EXP_REFRESH: int = 7
"""in days, default is 7 days"""
LOG_CONFIG_OVERRIDE: Path | None = None
"""path to custom logging configuration file"""