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:
+21
-10
@@ -3,6 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import UTC
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Response, status
|
||||
@@ -16,13 +17,26 @@ from app.models.saved_view import SavedView
|
||||
|
||||
router = APIRouter(prefix="/api/v1/saved-views", tags=["saved-views"])
|
||||
|
||||
VALID_ENTITY_TYPES = {"contacts", "mail", "calendar", "dms"}
|
||||
VALID_ENTITY_TYPES = None # Dynamic — validated against ENTITY_MODELS at runtime
|
||||
|
||||
|
||||
def _validate_entity_type(entity_type: str) -> None:
|
||||
"""Validate entity_type against ENTITY_MODELS. Raises HTTPException if invalid."""
|
||||
from app.services.entity_permission_service import ENTITY_MODELS
|
||||
if entity_type not in ENTITY_MODELS:
|
||||
from fastapi import HTTPException
|
||||
valid = sorted(ENTITY_MODELS.keys())
|
||||
raise HTTPException(400, detail={
|
||||
"detail": f"Invalid entity_type: {entity_type}",
|
||||
"code": "invalid_entity_type",
|
||||
"valid_types": valid,
|
||||
})
|
||||
|
||||
|
||||
class SavedViewCreate(BaseModel):
|
||||
"""Schema for creating a saved view."""
|
||||
name: str = Field(..., min_length=1, max_length=100)
|
||||
entity_type: str = Field(..., pattern="^(contacts|mail|calendar|dms)$")
|
||||
entity_type: str = Field(..., min_length=1, max_length=50)
|
||||
view_config: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
@@ -44,16 +58,15 @@ def _view_to_dict(v: SavedView) -> dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
@router.get("", dependencies=[Depends(require_permission("contacts:read"))])
|
||||
@router.get("")
|
||||
async def list_saved_views(
|
||||
entity_type: str | None = Query(None, pattern="^(contacts|mail|calendar|dms)$"),
|
||||
entity_type: str | None = Query(None),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""List saved views for the current user, optionally filtered by entity_type."""
|
||||
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)
|
||||
|
||||
try:
|
||||
query = select(SavedView).where(
|
||||
@@ -79,9 +92,9 @@ async def create_saved_view(
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Create a new saved view for the current user."""
|
||||
_validate_entity_type(body.entity_type)
|
||||
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)
|
||||
|
||||
try:
|
||||
# Check uniqueness within user+entity
|
||||
@@ -121,7 +134,6 @@ async def update_saved_view(
|
||||
"""Update a saved view."""
|
||||
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)
|
||||
|
||||
try:
|
||||
try:
|
||||
@@ -160,7 +172,6 @@ async def delete_saved_view(
|
||||
"""Delete a saved view (soft-delete)."""
|
||||
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)
|
||||
|
||||
try:
|
||||
try:
|
||||
@@ -180,8 +191,8 @@ async def delete_saved_view(
|
||||
if saved is None:
|
||||
raise HTTPException(404, detail={"detail": "Saved view not found", "code": "not_found"})
|
||||
|
||||
from datetime import datetime, timezone
|
||||
saved.deleted_at = datetime.now(timezone.utc)
|
||||
from datetime import datetime
|
||||
saved.deleted_at = datetime.now(UTC)
|
||||
await db.flush()
|
||||
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
||||
except PermissionError as e:
|
||||
|
||||
Reference in New Issue
Block a user