fix(audit): P0-P3 audit fixes — 838 ruff errors → 0, 30 F821 bugs fixed, 118 files changed
- P0: hooks.py 3-tuple fix, trigger_dispatcher Contract, contacts/plugin unregister_actions_by_owner - P0: 5 test files — check_permission mocks removed, hardcoded DB credential → env var - P1: attachment_service DmsFile via Contract helper, restore_registry/history_hooks dedup - P1: mail/plugin restore unregister, mcp_client datetime.now(UTC), saved_views/filters patterns - P1: ProtectedRoute fail-closed, 13 test assertion fixes (bcrypt, DB-URLs, SECRET_KEYs) - P2: deprecated notifications → post_system_message (3 files), forgejo Base, report_generator lazy import - P2: webhooks permissions, deps.py/roles.py plugin perms removed, import_export default - P2: address/tags/entity_links patterns removed, worker.py Contract-Umgehungen fixed - P2: 28 frontend TODOs (hardcoded constants, deprecated notification API) - P3: dead code, duplicates, deprecated imports, private attr, __import__ inline - P3: 8 frontend TODOs (LucideIcons, inline styles, XSS, i18n) - ruff: 838 → 0 (612 auto-fix + 246 manual + 27 F821 regression fix) - F821: 30 → 0 (AutomationDefinition, DmsFile, user_id, Path, Any, String) - Contract-Umgehungen: 2 neue gefunden (worker.py:169, worker.py:280) und gefixt
This commit is contained in:
+17
-17
@@ -9,7 +9,7 @@ from __future__ import annotations
|
||||
import uuid
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Header, status
|
||||
from fastapi import APIRouter, Depends, Header, HTTPException, status
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
@@ -79,20 +79,20 @@ async def workspace_context(
|
||||
x_workspace_id: str | None = Header(None, alias="X-Workspace-ID"),
|
||||
):
|
||||
"""Get workspace context for the current user (modules, widgets).
|
||||
|
||||
|
||||
Uses X-Workspace-ID header for tab-local workspace selection.
|
||||
Falls back to user's default workspace if no header.
|
||||
"""
|
||||
tenant_id = uuid.UUID(current_user["tenant_id"])
|
||||
user_id = uuid.UUID(current_user["user_id"])
|
||||
is_admin = current_user.get("is_system_admin", False)
|
||||
|
||||
|
||||
workspace_id = None
|
||||
if x_workspace_id:
|
||||
try:
|
||||
workspace_id = uuid.UUID(x_workspace_id)
|
||||
except ValueError:
|
||||
raise HTTPException(400, detail={"detail": "Invalid X-Workspace-ID", "code": "invalid_id"})
|
||||
raise HTTPException(400, detail={"detail": "Invalid X-Workspace-ID", "code": "invalid_id"}) from None
|
||||
else:
|
||||
# Find user's default workspace
|
||||
my = await workspace_service.get_my_workspaces(db, tenant_id, user_id)
|
||||
@@ -102,10 +102,10 @@ async def workspace_context(
|
||||
break
|
||||
if workspace_id is None and my["items"]:
|
||||
workspace_id = uuid.UUID(my["items"][0]["id"])
|
||||
|
||||
|
||||
if workspace_id is None:
|
||||
return {"workspace_id": None, "modules": [], "widgets": []}
|
||||
|
||||
|
||||
ctx = await workspace_service.get_workspace_context(db, tenant_id, user_id, workspace_id)
|
||||
if ctx is None and not is_admin:
|
||||
# User not assigned — return empty context
|
||||
@@ -151,7 +151,7 @@ async def get_workspace(
|
||||
try:
|
||||
wid = uuid.UUID(workspace_id)
|
||||
except ValueError:
|
||||
raise HTTPException(400, detail={"detail": "Invalid workspace_id", "code": "invalid_id"})
|
||||
raise HTTPException(400, detail={"detail": "Invalid workspace_id", "code": "invalid_id"}) from None
|
||||
result = await workspace_service.get_workspace(db, tenant_id, wid)
|
||||
if result is None:
|
||||
raise HTTPException(404, detail={"detail": "Workspace not found", "code": "not_found"})
|
||||
@@ -170,7 +170,7 @@ async def update_workspace(
|
||||
try:
|
||||
wid = uuid.UUID(workspace_id)
|
||||
except ValueError:
|
||||
raise HTTPException(400, detail={"detail": "Invalid workspace_id", "code": "invalid_id"})
|
||||
raise HTTPException(400, detail={"detail": "Invalid workspace_id", "code": "invalid_id"}) from None
|
||||
result = await workspace_service.update_workspace(
|
||||
db, tenant_id, wid, body.name, body.icon, body.description, body.is_default, body.is_active
|
||||
)
|
||||
@@ -190,7 +190,7 @@ async def delete_workspace(
|
||||
try:
|
||||
wid = uuid.UUID(workspace_id)
|
||||
except ValueError:
|
||||
raise HTTPException(400, detail={"detail": "Invalid workspace_id", "code": "invalid_id"})
|
||||
raise HTTPException(400, detail={"detail": "Invalid workspace_id", "code": "invalid_id"}) from None
|
||||
deleted = await workspace_service.delete_workspace(db, tenant_id, wid)
|
||||
if not deleted:
|
||||
raise HTTPException(404, detail={"detail": "Workspace not found", "code": "not_found"})
|
||||
@@ -208,7 +208,7 @@ async def set_modules(
|
||||
try:
|
||||
wid = uuid.UUID(workspace_id)
|
||||
except ValueError:
|
||||
raise HTTPException(400, detail={"detail": "Invalid workspace_id", "code": "invalid_id"})
|
||||
raise HTTPException(400, detail={"detail": "Invalid workspace_id", "code": "invalid_id"}) from None
|
||||
modules = [{"module_key": m.module_key, "is_visible": m.is_visible, "menu_order": m.menu_order, "config": m.config} for m in body.modules]
|
||||
return await workspace_service.set_workspace_modules(db, tenant_id, wid, modules)
|
||||
|
||||
@@ -227,7 +227,7 @@ async def assign_user(
|
||||
wid = uuid.UUID(workspace_id)
|
||||
target_uid = uuid.UUID(body.user_id)
|
||||
except ValueError:
|
||||
raise HTTPException(400, detail={"detail": "Invalid ID", "code": "invalid_id"})
|
||||
raise HTTPException(400, detail={"detail": "Invalid ID", "code": "invalid_id"}) from None
|
||||
# Cross-tenant validation: target user must belong to same tenant
|
||||
if not await workspace_service.verify_user_same_tenant(db, tenant_id, target_uid):
|
||||
raise HTTPException(403, detail={"detail": "Cannot assign user from different tenant", "code": "cross_tenant"})
|
||||
@@ -247,7 +247,7 @@ async def remove_user(
|
||||
wid = uuid.UUID(workspace_id)
|
||||
uid = uuid.UUID(user_id)
|
||||
except ValueError:
|
||||
raise HTTPException(400, detail={"detail": "Invalid ID", "code": "invalid_id"})
|
||||
raise HTTPException(400, detail={"detail": "Invalid ID", "code": "invalid_id"}) from None
|
||||
removed = await workspace_service.remove_user(db, tenant_id, wid, uid)
|
||||
if not removed:
|
||||
raise HTTPException(404, detail={"detail": "User not assigned to this workspace", "code": "not_found"})
|
||||
@@ -283,7 +283,7 @@ async def list_widgets(
|
||||
try:
|
||||
wid = uuid.UUID(workspace_id)
|
||||
except ValueError:
|
||||
raise HTTPException(400, detail={"detail": "Invalid workspace_id", "code": "invalid_id"})
|
||||
raise HTTPException(400, detail={"detail": "Invalid workspace_id", "code": "invalid_id"}) from None
|
||||
widgets = await workspace_service.get_widgets(db, tenant_id, wid)
|
||||
return {"items": widgets, "total": len(widgets)}
|
||||
|
||||
@@ -300,7 +300,7 @@ async def create_widget(
|
||||
try:
|
||||
wid = uuid.UUID(workspace_id)
|
||||
except ValueError:
|
||||
raise HTTPException(400, detail={"detail": "Invalid workspace_id", "code": "invalid_id"})
|
||||
raise HTTPException(400, detail={"detail": "Invalid workspace_id", "code": "invalid_id"}) from None
|
||||
return await workspace_service.create_widget(
|
||||
db, tenant_id, wid, body.widget_key,
|
||||
body.position_x, body.position_y, body.width, body.height, body.config,
|
||||
@@ -321,7 +321,7 @@ async def update_widget(
|
||||
ws_id = uuid.UUID(workspace_id)
|
||||
wid = uuid.UUID(widget_id)
|
||||
except ValueError:
|
||||
raise HTTPException(400, detail={"detail": "Invalid ID", "code": "invalid_id"})
|
||||
raise HTTPException(400, detail={"detail": "Invalid ID", "code": "invalid_id"}) from None
|
||||
result = await workspace_service.update_widget(
|
||||
db, tenant_id, ws_id, wid,
|
||||
body.position_x, body.position_y, body.width, body.height, body.config,
|
||||
@@ -344,7 +344,7 @@ async def delete_widget(
|
||||
ws_id = uuid.UUID(workspace_id)
|
||||
wid = uuid.UUID(widget_id)
|
||||
except ValueError:
|
||||
raise HTTPException(400, detail={"detail": "Invalid ID", "code": "invalid_id"})
|
||||
raise HTTPException(400, detail={"detail": "Invalid ID", "code": "invalid_id"}) from None
|
||||
deleted = await workspace_service.delete_widget(db, tenant_id, ws_id, wid)
|
||||
if not deleted:
|
||||
raise HTTPException(404, detail={"detail": "Widget not found", "code": "not_found"})
|
||||
@@ -364,7 +364,7 @@ async def set_default_workspace(
|
||||
try:
|
||||
wid = uuid.UUID(workspace_id)
|
||||
except ValueError:
|
||||
raise HTTPException(400, detail={"detail": "Invalid workspace_id", "code": "invalid_id"})
|
||||
raise HTTPException(400, detail={"detail": "Invalid workspace_id", "code": "invalid_id"}) from None
|
||||
# Verify user is assigned to this workspace
|
||||
ctx = await workspace_service.get_workspace_context(db, tenant_id, user_id, wid)
|
||||
if ctx is None and not current_user.get("is_system_admin"):
|
||||
|
||||
Reference in New Issue
Block a user