fix(lint): resolve all ruff lint errors
Add ruff config to exclude alembic E402, SQLAlchemy F821, and pre-existing E501 line-length issues. Auto-fix 69 unused imports and 2 f-strings without placeholders. Manually fix 8 unused variables. Apply ruff format to 127 files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -49,6 +49,7 @@ def require_role(*allowed_roles: str) -> Callable:
|
||||
Returns:
|
||||
FastAPI dependency that raises 403 if the role is insufficient
|
||||
"""
|
||||
|
||||
async def dependency(
|
||||
current_user: CurrentUser = Depends(get_current_user),
|
||||
) -> CurrentUser:
|
||||
@@ -56,7 +57,7 @@ def require_role(*allowed_roles: str) -> Callable:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail=f"Access denied. Required roles: {', '.join(allowed_roles)}. "
|
||||
f"Your role: {current_user.role}",
|
||||
f"Your role: {current_user.role}",
|
||||
)
|
||||
return current_user
|
||||
|
||||
@@ -82,7 +83,7 @@ def require_min_role(min_role: str) -> Callable:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail=f"Access denied. Minimum required role: {min_role}. "
|
||||
f"Your role: {current_user.role}",
|
||||
f"Your role: {current_user.role}",
|
||||
)
|
||||
return current_user
|
||||
|
||||
@@ -96,6 +97,7 @@ def require_write_access() -> Callable:
|
||||
Viewers are NOT allowed on POST/PUT/PATCH/DELETE endpoints.
|
||||
Call this on any mutating endpoint to deny viewers.
|
||||
"""
|
||||
|
||||
async def dependency(
|
||||
request: Request,
|
||||
current_user: CurrentUser = Depends(get_current_user),
|
||||
@@ -105,7 +107,7 @@ def require_write_access() -> Callable:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Viewers have read-only access. "
|
||||
"Contact your administrator to request elevated permissions.",
|
||||
"Contact your administrator to request elevated permissions.",
|
||||
)
|
||||
return current_user
|
||||
|
||||
@@ -127,6 +129,7 @@ def require_scope(scope: str) -> DependsClass:
|
||||
Raises:
|
||||
HTTPException 403 if the API key is missing the required scope.
|
||||
"""
|
||||
|
||||
async def _check_scope(
|
||||
current_user: CurrentUser = Depends(get_current_user),
|
||||
) -> CurrentUser:
|
||||
@@ -143,6 +146,7 @@ def require_scope(scope: str) -> DependsClass:
|
||||
|
||||
# Pre-built convenience dependencies
|
||||
|
||||
|
||||
async def require_super_admin(
|
||||
current_user: CurrentUser = Depends(get_current_user),
|
||||
) -> CurrentUser:
|
||||
|
||||
@@ -20,32 +20,36 @@ from starlette.requests import Request
|
||||
from starlette.responses import Response
|
||||
|
||||
# Production CSP: strict -- no inline scripts allowed
|
||||
_CSP_PRODUCTION = "; ".join([
|
||||
"default-src 'self'",
|
||||
"script-src 'self'",
|
||||
"style-src 'self' 'unsafe-inline'",
|
||||
"img-src 'self' data: blob:",
|
||||
"font-src 'self'",
|
||||
"connect-src 'self' wss: ws:",
|
||||
"worker-src 'self'",
|
||||
"frame-ancestors 'none'",
|
||||
"base-uri 'self'",
|
||||
"form-action 'self'",
|
||||
])
|
||||
_CSP_PRODUCTION = "; ".join(
|
||||
[
|
||||
"default-src 'self'",
|
||||
"script-src 'self'",
|
||||
"style-src 'self' 'unsafe-inline'",
|
||||
"img-src 'self' data: blob:",
|
||||
"font-src 'self'",
|
||||
"connect-src 'self' wss: ws:",
|
||||
"worker-src 'self'",
|
||||
"frame-ancestors 'none'",
|
||||
"base-uri 'self'",
|
||||
"form-action 'self'",
|
||||
]
|
||||
)
|
||||
|
||||
# Development CSP: relaxed for Vite HMR (hot module replacement)
|
||||
_CSP_DEV = "; ".join([
|
||||
"default-src 'self'",
|
||||
"script-src 'self' 'unsafe-inline' 'unsafe-eval'",
|
||||
"style-src 'self' 'unsafe-inline'",
|
||||
"img-src 'self' data: blob:",
|
||||
"font-src 'self'",
|
||||
"connect-src 'self' http://localhost:* ws://localhost:* wss:",
|
||||
"worker-src 'self' blob:",
|
||||
"frame-ancestors 'none'",
|
||||
"base-uri 'self'",
|
||||
"form-action 'self'",
|
||||
])
|
||||
_CSP_DEV = "; ".join(
|
||||
[
|
||||
"default-src 'self'",
|
||||
"script-src 'self' 'unsafe-inline' 'unsafe-eval'",
|
||||
"style-src 'self' 'unsafe-inline'",
|
||||
"img-src 'self' data: blob:",
|
||||
"font-src 'self'",
|
||||
"connect-src 'self' http://localhost:* ws://localhost:* wss:",
|
||||
"worker-src 'self' blob:",
|
||||
"frame-ancestors 'none'",
|
||||
"base-uri 'self'",
|
||||
"form-action 'self'",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class SecurityHeadersMiddleware(BaseHTTPMiddleware):
|
||||
@@ -72,8 +76,6 @@ class SecurityHeadersMiddleware(BaseHTTPMiddleware):
|
||||
|
||||
# HSTS only in production (plain HTTP in dev would be blocked)
|
||||
if self.is_production:
|
||||
response.headers["Strict-Transport-Security"] = (
|
||||
"max-age=31536000; includeSubDomains"
|
||||
)
|
||||
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
|
||||
|
||||
return response
|
||||
|
||||
@@ -178,7 +178,6 @@ async def get_current_user_ws(
|
||||
Raises:
|
||||
WebSocketException 1008: If no token is provided or the token is invalid.
|
||||
"""
|
||||
from starlette.websockets import WebSocket, WebSocketState
|
||||
from fastapi import WebSocketException
|
||||
|
||||
# 1. Try cookie
|
||||
|
||||
Reference in New Issue
Block a user