Done for Login and notification system
This commit is contained in:
@ -0,0 +1 @@
|
||||
from .message_code import *
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
|
@ -1 +1 @@
|
||||
from .hasher import get_hasher
|
||||
from .security import *
|
||||
|
46
fuware/core/security/security.py
Normal file
46
fuware/core/security/security.py
Normal 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)
|
@ -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"""
|
||||
|
Reference in New Issue
Block a user