🔒 Fixed some security issues. Please upgrade!
This commit is contained in:
@@ -14,7 +14,6 @@ from fastapi import APIRouter, Response, HTTPException, Request, Depends, status
|
||||
from fastapi.background import BackgroundTasks
|
||||
from fastapi.responses import JSONResponse, RedirectResponse, PlainTextResponse
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
import html
|
||||
|
||||
from jose import jwt, JWTError
|
||||
|
||||
@@ -78,7 +77,7 @@ async def create_user(user: RouteUser, background_task: BackgroundTasks) -> User
|
||||
raise HTTPException(status_code=409, detail="User already exists")
|
||||
|
||||
user.password = get_password_hash(user.password)
|
||||
user.username = html.unescape(bleach.clean(user.username, tags=[], strip=True))
|
||||
user.username = bleach.clean(user.username, tags=[], strip=True)
|
||||
if len(user.username) == 32:
|
||||
return JSONResponse({"details": "Username mustn't be 32 characters long"}, 400)
|
||||
await user.save()
|
||||
|
||||
@@ -7,10 +7,10 @@ import os
|
||||
import urllib.parse
|
||||
|
||||
import pyotp
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from pydantic import BaseModel
|
||||
|
||||
from classquiz.auth import get_current_user
|
||||
from classquiz.auth import get_current_user, verify_password
|
||||
from classquiz.db.models import User
|
||||
|
||||
router = APIRouter()
|
||||
@@ -20,10 +20,16 @@ class GetBackupCodeResponse(BaseModel):
|
||||
code: str
|
||||
|
||||
|
||||
@router.get("/backup_code", response_model=GetBackupCodeResponse)
|
||||
async def get_backup_code(user: User = Depends(get_current_user)):
|
||||
class RequirePasswordForAction(BaseModel):
|
||||
password: str
|
||||
|
||||
|
||||
@router.post("/backup_code", response_model=GetBackupCodeResponse)
|
||||
async def get_backup_code(data: RequirePasswordForAction, user: User = Depends(get_current_user)):
|
||||
backup_code = os.urandom(32).hex()
|
||||
user = await User.objects.get(id=user.id)
|
||||
if not verify_password(data.password, user.password):
|
||||
raise HTTPException(status_code=401, detail="Invalid")
|
||||
user.backup_code = backup_code
|
||||
await user.update()
|
||||
return GetBackupCodeResponse(code=backup_code)
|
||||
@@ -31,11 +37,14 @@ async def get_backup_code(user: User = Depends(get_current_user)):
|
||||
|
||||
class SetRequirePassword(BaseModel):
|
||||
require_password: bool
|
||||
password: str
|
||||
|
||||
|
||||
@router.post("/require_password", response_model=SetRequirePassword)
|
||||
async def set_require_password(data: SetRequirePassword, user: User = Depends(get_current_user)):
|
||||
user = await User.objects.get(id=user.id)
|
||||
if not verify_password(data.password, user.password):
|
||||
raise HTTPException(status_code=401, detail="Invalid")
|
||||
user.require_password = data.require_password
|
||||
await user.update()
|
||||
return data
|
||||
@@ -47,8 +56,10 @@ class SetTotpUpResponse(BaseModel):
|
||||
|
||||
|
||||
@router.post("/totp", response_model=SetTotpUpResponse)
|
||||
async def set_totp_up(user: User = Depends(get_current_user)):
|
||||
async def set_totp_up(data: RequirePasswordForAction, user: User = Depends(get_current_user)):
|
||||
user = await User.objects.get(id=user.id)
|
||||
if not verify_password(data.password, user.password):
|
||||
raise HTTPException(status_code=401, detail="Invalid")
|
||||
user.totp_secret = pyotp.random_base32()
|
||||
url = pyotp.totp.TOTP(user.totp_secret).provisioning_uri(
|
||||
name=urllib.parse.quote(user.username), issuer_name="ClassQuiz"
|
||||
@@ -71,7 +82,9 @@ async def get_totp_status(user: User = Depends(get_current_user)):
|
||||
|
||||
|
||||
@router.delete("/totp")
|
||||
async def disable_totp(user: User = Depends(get_current_user)):
|
||||
async def disable_totp(data: RequirePasswordForAction, user: User = Depends(get_current_user)):
|
||||
user = await User.objects.get(id=user.id)
|
||||
if not verify_password(data.password, user.password):
|
||||
raise HTTPException(status_code=401, detail="Invalid")
|
||||
user.totp_secret = None
|
||||
await user.update()
|
||||
|
||||
@@ -8,7 +8,7 @@ from fastapi import APIRouter, Depends, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from webauthn.helpers.cose import COSEAlgorithmIdentifier
|
||||
|
||||
from classquiz.auth import get_current_user
|
||||
from classquiz.auth import get_current_user, verify_password
|
||||
from classquiz.db.models import User, FidoCredentials
|
||||
from classquiz.config import redis, settings
|
||||
|
||||
@@ -28,9 +28,15 @@ settings = settings()
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/add_key", response_model=PublicKeyCredentialCreationOptions)
|
||||
async def request_add_key_data(user: User = Depends(get_current_user)):
|
||||
class RequirePasswordForAction(BaseModel):
|
||||
password: str
|
||||
|
||||
|
||||
@router.post("/add_key_init", response_model=PublicKeyCredentialCreationOptions)
|
||||
async def request_add_key_data(data: RequirePasswordForAction, user: User = Depends(get_current_user)):
|
||||
user = await User.objects.select_related("fidocredentialss").get(id=user.id)
|
||||
if not verify_password(data.password, user.password):
|
||||
raise HTTPException(status_code=401, detail="Invalid")
|
||||
options = generate_registration_options(
|
||||
rp_id=urllib.parse.urlparse(settings.root_address).hostname,
|
||||
rp_name="ClassQuiz",
|
||||
@@ -86,7 +92,9 @@ async def list_security_keys(user: User = Depends(get_current_user)):
|
||||
|
||||
|
||||
@router.delete("/key/{key_id}")
|
||||
async def delete_security_key(key_id: int, user: User = Depends(get_current_user)):
|
||||
async def delete_security_key(data: RequirePasswordForAction, key_id: int, user: User = Depends(get_current_user)):
|
||||
if not verify_password(data.password, user.password):
|
||||
raise HTTPException(status_code=401, detail="Invalid")
|
||||
key = await FidoCredentials.objects.get_or_none(pk=key_id, user=user.id)
|
||||
if key is None:
|
||||
raise HTTPException(status_code=404, detail="Key not found")
|
||||
|
||||
Reference in New Issue
Block a user