Further cleanup

This commit is contained in:
Mawoka
2025-11-01 15:57:10 +01:00
parent abd2155469
commit 02c917a28f
16 changed files with 215 additions and 336 deletions
+18 -73
View File
@@ -10,22 +10,18 @@ from datetime import datetime
import ormar
import pydantic
from email_validator import validate_email, EmailNotValidError
from fastapi import APIRouter, Response, HTTPException, Request, Depends, status
from fastapi import APIRouter, Response, HTTPException, Request, Depends
from fastapi.background import BackgroundTasks
from fastapi.responses import JSONResponse, RedirectResponse, PlainTextResponse
from fastapi.security import OAuth2PasswordRequestForm
from jose import jwt, JWTError
from classquiz import oauth
from classquiz.helpers.avatar import gzipped_user_avatar
import base64
from classquiz.oauth.authenticate_user import rememberme_check, log_user_in
from classquiz.auth import (
get_password_hash,
verify_password,
authenticate_user,
get_current_user,
)
from classquiz.cache import clear_cache_for_account
@@ -33,7 +29,7 @@ from classquiz.config import redis, settings, meilisearch
import uuid
import bleach
from pydantic import BaseModel
from classquiz.db.models import User, UserSession, UpdatePassword, Token, Quiz, ApiKey
from classquiz.db.models import User, UserSession, UpdatePassword, Quiz, ApiKey
from classquiz.emails import send_register_email, send_forgotten_password_email
from classquiz.routers.users import webauthn, twofa
@@ -66,7 +62,12 @@ router.include_router(oauth.router, tags=["users", "oauth"], prefix="/oauth")
async def create_user(user: RouteUser, background_task: BackgroundTasks) -> User | JSONResponse:
if settings.registration_disabled:
raise HTTPException(status_code=423)
user = User(**user.model_dump(), id=uuid.uuid4(), avatar=gzipped_user_avatar(), created_at=datetime.now())
user: User = User(
**user.model_dump(),
id=uuid.uuid4(),
avatar=gzipped_user_avatar(),
created_at=datetime.now(),
)
try:
validate_email(user.email)
except EmailNotValidError as e:
@@ -91,31 +92,6 @@ async def create_user(user: RouteUser, background_task: BackgroundTasks) -> User
return user
@router.post("/token/cookie", response_model=Token, deprecated=True)
async def login_for_cookie_access_token(
request: Request,
response: Response,
form_data: OAuth2PasswordRequestForm = Depends(),
):
user = await authenticate_user(form_data.username, form_data.password)
user = await User.objects.select_related("fidocredentialss").get(id=user.id)
if not user or user.totp_secret is not None or user or len(user.fidocredentialss) != 0:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
)
return await log_user_in(response=response, request=request, user=user)
@router.get("/token/rememberme")
async def rememberme_token(request: Request, response: Response):
rememberme_token_lol = request.cookies.get("rememberme_token")
if rememberme_token_lol is None:
raise HTTPException(status_code=400, detail="No rememberme cookie")
return await rememberme_check(rememberme_token=rememberme_token_lol, response=response)
@router.get("/logout")
async def logout(request: Request, response: Response):
remember_token = request.cookies.get("rememberme_token")
@@ -229,24 +205,26 @@ async def reset_password_with_token(reset_password: ResetPassword, response: Res
return {"message": "Password updated successfully"}
@router.get("/sessions/list", response_model=list[UserSession], response_model_exclude={"user", "session_key", "quizs"})
@router.get(
"/sessions/list",
response_model=list[UserSession],
response_model_exclude={"user", "session_key", "quizs"},
)
async def list_sessions(user: User = Depends(get_current_user)):
sessions = await UserSession.objects.filter(user=user).all()
return [session.model_dump() for session in sessions]
@router.delete("/sessions/{session_id}")
async def delete_session(session_id: str, user: User = Depends(get_current_user)):
try:
session_id = uuid.UUID(session_id)
except ValueError:
raise HTTPException(status_code=400, detail="Invalid session id")
async def delete_session(session_id: uuid.UUID, user: User = Depends(get_current_user)):
await UserSession.objects.filter(user=user, id=session_id).delete()
return {"message": "Session deleted"}
@router.get(
"/session", response_model=UserSession, response_model_exclude={"user": ..., "session_key": ..., "quizs": ...}
"/session",
response_model=UserSession,
response_model_exclude={"user": ..., "session_key": ..., "quizs": ...},
)
async def get_session(request: Request, user: User = Depends(get_current_user)):
try:
@@ -300,43 +278,10 @@ class InternalAuthData(BaseModel):
jwt: str | None = None
@router.post("/auth/internal")
async def internal_auth(data: InternalAuthData, resp: Response):
try:
data.jwt = data.jwt.replace("Bearer ", "")
except AttributeError:
pass
if data.jwt is not None:
try:
payload = jwt.decode(data.jwt, settings.secret_key, algorithms=["HS256"])
email: str = payload.get("sub")
if email is None:
resp.status_code = 401
return resp
except JWTError:
resp.status_code = 401
return resp
else:
return await rememberme_check(data.rememberme, resp)
class GetEmailFromJWT(BaseModel):
jwt: str
@router.post("/auth/internal/email")
async def get_email_from_jwt(data: GetEmailFromJWT):
try:
data.jwt = data.jwt.replace("Bearer ", "")
except AttributeError:
pass
try:
payload = jwt.decode(data.jwt, settings.secret_key, algorithms=["HS256"])
return payload.get("sub")
except JWTError:
raise HTTPException(status_code=401)
@router.post("/api_keys", response_model=ApiKey, response_model_include={"key"})
async def generate_api_key(user: User = Depends(get_current_user)):
key = ApiKey(key=os.urandom(24).hex(), user=user)
@@ -351,7 +296,7 @@ async def list_api_keys(user: User = Depends(get_current_user)):
@router.delete("/api_keys")
async def delete_api_key(api_key: str, user: User = Depends(get_current_user)):
async def delete_api_key(api_key: str, _: User = Depends(get_current_user)):
key = await ApiKey.objects.get_or_none(key=api_key)
if key is None:
raise HTTPException(status_code=404, detail="Key not found")