Add pyproject.toml for consolidated project configuration and replace Black, isort, and Flake8 with Ruff

- Introduced `pyproject.toml` to centralize backend tool configurations (e.g., Ruff, mypy, coverage, pytest).
- Replaced Black, isort, and Flake8 with Ruff for linting, formatting, and import sorting.
- Updated `requirements.txt` to include Ruff and remove replaced tools.
- Added `Makefile` to streamline development workflows with commands for linting, formatting, type-checking, testing, and cleanup.
This commit is contained in:
2025-11-10 11:55:15 +01:00
parent a5c671c133
commit c589b565f0
86 changed files with 4572 additions and 3956 deletions

View File

@@ -5,18 +5,21 @@ This module provides utilities for creating and verifying signed tokens,
useful for operations like file uploads, password resets, or any other
time-limited, single-use operations.
"""
import base64
import hashlib
import hmac
import json
import secrets
import time
from typing import Dict, Any, Optional
from typing import Any
from app.core.config import settings
def create_upload_token(file_path: str, content_type: str, expires_in: int = 300) -> str:
def create_upload_token(
file_path: str, content_type: str, expires_in: int = 300
) -> str:
"""
Create a signed token for secure file uploads.
@@ -40,34 +43,29 @@ def create_upload_token(file_path: str, content_type: str, expires_in: int = 300
"path": file_path,
"content_type": content_type,
"exp": int(time.time()) + expires_in,
"nonce": secrets.token_hex(8) # Add randomness to prevent token reuse
"nonce": secrets.token_hex(8), # Add randomness to prevent token reuse
}
# Convert to JSON and encode
payload_bytes = json.dumps(payload).encode('utf-8')
payload_bytes = json.dumps(payload).encode("utf-8")
# Create a signature using HMAC-SHA256 for security
# This prevents length extension attacks that plain SHA-256 is vulnerable to
signature = hmac.new(
settings.SECRET_KEY.encode('utf-8'),
payload_bytes,
hashlib.sha256
settings.SECRET_KEY.encode("utf-8"), payload_bytes, hashlib.sha256
).hexdigest()
# Combine payload and signature
token_data = {
"payload": payload,
"signature": signature
}
token_data = {"payload": payload, "signature": signature}
# Encode the final token
token_json = json.dumps(token_data)
token = base64.urlsafe_b64encode(token_json.encode('utf-8')).decode('utf-8')
token = base64.urlsafe_b64encode(token_json.encode("utf-8")).decode("utf-8")
return token
def verify_upload_token(token: str) -> Optional[Dict[str, Any]]:
def verify_upload_token(token: str) -> dict[str, Any] | None:
"""
Verify an upload token and return the payload if valid.
@@ -88,7 +86,7 @@ def verify_upload_token(token: str) -> Optional[Dict[str, Any]]:
"""
try:
# Decode the token
token_json = base64.urlsafe_b64decode(token.encode('utf-8')).decode('utf-8')
token_json = base64.urlsafe_b64decode(token.encode("utf-8")).decode("utf-8")
token_data = json.loads(token_json)
# Extract payload and signature
@@ -96,11 +94,9 @@ def verify_upload_token(token: str) -> Optional[Dict[str, Any]]:
signature = token_data["signature"]
# Verify signature using HMAC and constant-time comparison
payload_bytes = json.dumps(payload).encode('utf-8')
payload_bytes = json.dumps(payload).encode("utf-8")
expected_signature = hmac.new(
settings.SECRET_KEY.encode('utf-8'),
payload_bytes,
hashlib.sha256
settings.SECRET_KEY.encode("utf-8"), payload_bytes, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected_signature):
@@ -136,34 +132,29 @@ def create_password_reset_token(email: str, expires_in: int = 3600) -> str:
"email": email,
"exp": int(time.time()) + expires_in,
"nonce": secrets.token_hex(16), # Extra randomness
"purpose": "password_reset"
"purpose": "password_reset",
}
# Convert to JSON and encode
payload_bytes = json.dumps(payload).encode('utf-8')
payload_bytes = json.dumps(payload).encode("utf-8")
# Create a signature using HMAC-SHA256 for security
# This prevents length extension attacks that plain SHA-256 is vulnerable to
signature = hmac.new(
settings.SECRET_KEY.encode('utf-8'),
payload_bytes,
hashlib.sha256
settings.SECRET_KEY.encode("utf-8"), payload_bytes, hashlib.sha256
).hexdigest()
# Combine payload and signature
token_data = {
"payload": payload,
"signature": signature
}
token_data = {"payload": payload, "signature": signature}
# Encode the final token
token_json = json.dumps(token_data)
token = base64.urlsafe_b64encode(token_json.encode('utf-8')).decode('utf-8')
token = base64.urlsafe_b64encode(token_json.encode("utf-8")).decode("utf-8")
return token
def verify_password_reset_token(token: str) -> Optional[str]:
def verify_password_reset_token(token: str) -> str | None:
"""
Verify a password reset token and return the email if valid.
@@ -182,7 +173,7 @@ def verify_password_reset_token(token: str) -> Optional[str]:
"""
try:
# Decode the token
token_json = base64.urlsafe_b64decode(token.encode('utf-8')).decode('utf-8')
token_json = base64.urlsafe_b64decode(token.encode("utf-8")).decode("utf-8")
token_data = json.loads(token_json)
# Extract payload and signature
@@ -194,11 +185,9 @@ def verify_password_reset_token(token: str) -> Optional[str]:
return None
# Verify signature using HMAC and constant-time comparison
payload_bytes = json.dumps(payload).encode('utf-8')
payload_bytes = json.dumps(payload).encode("utf-8")
expected_signature = hmac.new(
settings.SECRET_KEY.encode('utf-8'),
payload_bytes,
hashlib.sha256
settings.SECRET_KEY.encode("utf-8"), payload_bytes, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected_signature):
@@ -234,34 +223,29 @@ def create_email_verification_token(email: str, expires_in: int = 86400) -> str:
"email": email,
"exp": int(time.time()) + expires_in,
"nonce": secrets.token_hex(16),
"purpose": "email_verification"
"purpose": "email_verification",
}
# Convert to JSON and encode
payload_bytes = json.dumps(payload).encode('utf-8')
payload_bytes = json.dumps(payload).encode("utf-8")
# Create a signature using HMAC-SHA256 for security
# This prevents length extension attacks that plain SHA-256 is vulnerable to
signature = hmac.new(
settings.SECRET_KEY.encode('utf-8'),
payload_bytes,
hashlib.sha256
settings.SECRET_KEY.encode("utf-8"), payload_bytes, hashlib.sha256
).hexdigest()
# Combine payload and signature
token_data = {
"payload": payload,
"signature": signature
}
token_data = {"payload": payload, "signature": signature}
# Encode the final token
token_json = json.dumps(token_data)
token = base64.urlsafe_b64encode(token_json.encode('utf-8')).decode('utf-8')
token = base64.urlsafe_b64encode(token_json.encode("utf-8")).decode("utf-8")
return token
def verify_email_verification_token(token: str) -> Optional[str]:
def verify_email_verification_token(token: str) -> str | None:
"""
Verify an email verification token and return the email if valid.
@@ -280,7 +264,7 @@ def verify_email_verification_token(token: str) -> Optional[str]:
"""
try:
# Decode the token
token_json = base64.urlsafe_b64decode(token.encode('utf-8')).decode('utf-8')
token_json = base64.urlsafe_b64decode(token.encode("utf-8")).decode("utf-8")
token_data = json.loads(token_json)
# Extract payload and signature
@@ -292,11 +276,9 @@ def verify_email_verification_token(token: str) -> Optional[str]:
return None
# Verify signature using HMAC and constant-time comparison
payload_bytes = json.dumps(payload).encode('utf-8')
payload_bytes = json.dumps(payload).encode("utf-8")
expected_signature = hmac.new(
settings.SECRET_KEY.encode('utf-8'),
payload_bytes,
hashlib.sha256
settings.SECRET_KEY.encode("utf-8"), payload_bytes, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected_signature):