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:
@@ -9,12 +9,11 @@ from __future__ import annotations
|
||||
import uuid
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import select, update, func
|
||||
from sqlalchemy import func, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
from app.models.user import UserTenant
|
||||
from app.models.workspace import Workspace, WorkspaceModule, WorkspaceUser, WorkspaceWidget
|
||||
from app.models.user import User, UserTenant
|
||||
|
||||
|
||||
def _workspace_to_dict(ws: Workspace, modules: list[WorkspaceModule] | None = None, user_count: int = 0) -> dict[str, Any]:
|
||||
@@ -52,7 +51,7 @@ async def list_workspaces(
|
||||
).order_by(Workspace.name)
|
||||
result = await db.execute(q)
|
||||
workspaces = result.scalars().all()
|
||||
|
||||
|
||||
items = []
|
||||
for ws in workspaces:
|
||||
# Count users
|
||||
@@ -63,7 +62,7 @@ async def list_workspaces(
|
||||
count_result = await db.execute(count_q)
|
||||
user_count = count_result.scalar() or 0
|
||||
items.append(_workspace_to_dict(ws, user_count=user_count))
|
||||
|
||||
|
||||
return {"items": items, "total": len(items)}
|
||||
|
||||
|
||||
@@ -79,7 +78,7 @@ async def get_workspace(
|
||||
ws = result.scalar_one_or_none()
|
||||
if ws is None:
|
||||
return None
|
||||
|
||||
|
||||
# Get modules
|
||||
mod_q = select(WorkspaceModule).where(
|
||||
WorkspaceModule.workspace_id == workspace_id,
|
||||
@@ -87,7 +86,7 @@ async def get_workspace(
|
||||
).order_by(WorkspaceModule.menu_order)
|
||||
mod_result = await db.execute(mod_q)
|
||||
modules = mod_result.scalars().all()
|
||||
|
||||
|
||||
# Count users
|
||||
count_q = select(func.count()).select_from(WorkspaceUser).where(
|
||||
WorkspaceUser.workspace_id == workspace_id,
|
||||
@@ -95,7 +94,7 @@ async def get_workspace(
|
||||
)
|
||||
count_result = await db.execute(count_q)
|
||||
user_count = count_result.scalar() or 0
|
||||
|
||||
|
||||
return _workspace_to_dict(ws, modules=modules, user_count=user_count)
|
||||
|
||||
|
||||
@@ -132,7 +131,7 @@ async def create_workspace(
|
||||
db.add(ws)
|
||||
await db.flush()
|
||||
await db.refresh(ws)
|
||||
|
||||
|
||||
# Auto-assign creator as manager
|
||||
wu = WorkspaceUser(
|
||||
tenant_id=tenant_id,
|
||||
@@ -144,7 +143,7 @@ async def create_workspace(
|
||||
)
|
||||
db.add(wu)
|
||||
await db.flush()
|
||||
|
||||
|
||||
return _workspace_to_dict(ws, user_count=1)
|
||||
|
||||
|
||||
@@ -167,7 +166,7 @@ async def update_workspace(
|
||||
ws = result.scalar_one_or_none()
|
||||
if ws is None:
|
||||
return None
|
||||
|
||||
|
||||
if name is not None:
|
||||
ws.name = name
|
||||
if icon is not None:
|
||||
@@ -189,7 +188,7 @@ async def update_workspace(
|
||||
ws.is_default = True
|
||||
elif is_default is False:
|
||||
ws.is_default = False
|
||||
|
||||
|
||||
await db.flush()
|
||||
await db.refresh(ws)
|
||||
return _workspace_to_dict(ws)
|
||||
@@ -227,7 +226,7 @@ async def set_workspace_modules(
|
||||
existing = await db.execute(existing_q)
|
||||
for m in existing.scalars().all():
|
||||
await db.delete(m)
|
||||
|
||||
|
||||
# Insert new modules
|
||||
result = []
|
||||
for mod in modules:
|
||||
@@ -249,7 +248,7 @@ async def set_workspace_modules(
|
||||
"menu_order": wm.menu_order,
|
||||
"config": wm.config or {},
|
||||
})
|
||||
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@@ -317,7 +316,7 @@ async def get_my_workspaces(
|
||||
)
|
||||
result = await db.execute(q)
|
||||
rows = result.all()
|
||||
|
||||
|
||||
items = []
|
||||
for ws, wu in rows:
|
||||
# Get modules for this workspace
|
||||
@@ -328,7 +327,7 @@ async def get_my_workspaces(
|
||||
).order_by(WorkspaceModule.menu_order)
|
||||
mod_result = await db.execute(mod_q)
|
||||
modules = mod_result.scalars().all()
|
||||
|
||||
|
||||
items.append({
|
||||
"id": str(ws.id),
|
||||
"name": ws.name,
|
||||
@@ -346,7 +345,7 @@ async def get_my_workspaces(
|
||||
for m in modules
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
return {"items": items, "total": len(items)}
|
||||
|
||||
|
||||
@@ -354,7 +353,7 @@ async def get_workspace_context(
|
||||
db: AsyncSession, tenant_id: uuid.UUID, user_id: uuid.UUID, workspace_id: uuid.UUID
|
||||
) -> dict[str, Any] | None:
|
||||
"""Get workspace context for a user — modules, widgets, config.
|
||||
|
||||
|
||||
Validates:
|
||||
- Workspace belongs to tenant
|
||||
- User is assigned or is system admin / tenant admin
|
||||
@@ -370,7 +369,7 @@ async def get_workspace_context(
|
||||
ws = ws_result.scalar_one_or_none()
|
||||
if ws is None:
|
||||
return None
|
||||
|
||||
|
||||
# Check user is assigned
|
||||
wu_q = select(WorkspaceUser).where(
|
||||
WorkspaceUser.workspace_id == workspace_id,
|
||||
@@ -381,7 +380,7 @@ async def get_workspace_context(
|
||||
wu = wu_result.scalar_one_or_none()
|
||||
if wu is None:
|
||||
return None # User not assigned — caller can check is_system_admin
|
||||
|
||||
|
||||
# Get all modules (including hidden) — frontend needs is_visible flag
|
||||
mod_q = select(WorkspaceModule).where(
|
||||
WorkspaceModule.workspace_id == workspace_id,
|
||||
@@ -389,7 +388,7 @@ async def get_workspace_context(
|
||||
).order_by(WorkspaceModule.menu_order)
|
||||
mod_result = await db.execute(mod_q)
|
||||
modules = mod_result.scalars().all()
|
||||
|
||||
|
||||
# Get widgets
|
||||
widget_q = select(WorkspaceWidget).where(
|
||||
WorkspaceWidget.workspace_id == workspace_id,
|
||||
@@ -397,7 +396,7 @@ async def get_workspace_context(
|
||||
).order_by(WorkspaceWidget.position_y, WorkspaceWidget.position_x)
|
||||
widget_result = await db.execute(widget_q)
|
||||
widgets = widget_result.scalars().all()
|
||||
|
||||
|
||||
return {
|
||||
"workspace_id": str(ws.id),
|
||||
"name": ws.name,
|
||||
|
||||
Reference in New Issue
Block a user