Build Seth's changes C1 to C11 (Gitea issues 1, 8, 18, 23, 24, 25, 27, 31, 32, 33, 36)
- C1 (issue 1): over target follows VISUAL ticket hours, not the timer. - C2 (issue 24): a Q.C. reject always needs a reason; accept needs an NCR number after a reject without one. - C3 (issue 25): an incomplete unit ITP blocks filing. - C4 (issue 27): prints open the print dialog on the page, no new tab. - C5 (issue 31): filing is blocked while VISUAL shows a work order open; the feed sets vclosed on VISUAL packets. - C6 (issue 8): the data book leaves out the history pages. - C7 (issue 18): the supervisor approves each received short; accept and filing wait for it. - C8 (issue 23): per-department checklists, edited by the department supervisor or Admin, frozen at START. - C9 (issue 32): Q.C. and Clerical may run the stamp check. - C10 (issue 33): the PM screen and reports are for PS-ProjectMgmt and Admin only. - C11 (issue 36): the Stall screen lists the packets assigned to the welder, at the supervisor's stall. Migration 0011. Docs: PLAN (also issue 9 confirmed, issue 4 asked), POLICY, SECURITY, SCHEMA, FEED, README, CHANGELOG. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
"""Admin: the Security tab (settings, alerts, the integrity result), the access log and its CSV, who is in now, the
|
||||
stamp check, and the directory flags.
|
||||
|
||||
Every route needs the admin role and a password session. Every change is audited (`admin.*`).
|
||||
Every route needs the admin role and a password session, except the stamp check: Admin, Q.C. and Clerical on a password
|
||||
session, at /api/stamp-verify (change C9) and at the old /api/admin paths. Every change is audited (`admin.*`).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -26,12 +27,23 @@ from ..auth import secsettings
|
||||
from ..domain import security_alerts
|
||||
from ..pdf import stamps, tracecsv
|
||||
from .account import LOG_PREFIXES
|
||||
from .deps import Db, Principal, admin_principal, get_db, idle_minutes, settings_of
|
||||
from .deps import (
|
||||
Db,
|
||||
Principal,
|
||||
admin_principal,
|
||||
current_principal,
|
||||
get_db,
|
||||
idle_minutes,
|
||||
require_password_session,
|
||||
settings_of,
|
||||
)
|
||||
from .errors import ApiError
|
||||
from .jobs import run_out
|
||||
from .packets import WRITE
|
||||
|
||||
router = APIRouter(prefix="/api/admin", tags=["admin"])
|
||||
# The stamp check outside /admin (change C9): Admin, Q.C. and Clerical. The /api/admin paths stay as aliases.
|
||||
stamp_router = APIRouter(prefix="/api", tags=["stamp"])
|
||||
|
||||
CSV = "text/csv; charset=utf-8"
|
||||
PDF_MAX_BYTES = 25 * 1024 * 1024
|
||||
@@ -499,6 +511,14 @@ def check_stamp(conn: Any, key: bytes | None, sig: str | None, shown: str | None
|
||||
return out
|
||||
|
||||
|
||||
def stamp_principal(p: Principal = Depends(current_principal)) -> Principal: # noqa: B008
|
||||
"""Admin, every Q.C. role and every Clerical role (change C9, Gitea issue 32), on a password session as the Admin
|
||||
path always needed."""
|
||||
if not (p.is_admin or any(r.startswith(("qc", "clerical")) for r in p.roles)):
|
||||
raise ApiError(403, "forbidden", "The stamp check is for Admin, Q.C. and Clerical.")
|
||||
return require_password_session(p)
|
||||
|
||||
|
||||
def _seal_key(request: Request) -> bytes | None:
|
||||
try:
|
||||
return settings_of(request).seal_key()
|
||||
@@ -526,10 +546,11 @@ def _audit_verify(conn: Any, p: Principal, results: list[dict[str, Any]], source
|
||||
|
||||
|
||||
@router.post("/stamp-verify", dependencies=WRITE)
|
||||
@stamp_router.post("/stamp-verify", dependencies=WRITE)
|
||||
def stamp_verify(
|
||||
body: StampText,
|
||||
request: Request,
|
||||
p: Principal = Depends(admin_principal), # noqa: B008
|
||||
p: Principal = Depends(stamp_principal), # noqa: B008
|
||||
d: Db = Depends(get_db), # noqa: B008
|
||||
) -> dict[str, Any]:
|
||||
text = body.text
|
||||
@@ -569,9 +590,10 @@ def pdf_stamps(data: bytes) -> list[tuple[str, str]]:
|
||||
|
||||
|
||||
@router.post("/stamp-verify/pdf")
|
||||
@stamp_router.post("/stamp-verify/pdf")
|
||||
async def stamp_verify_pdf(
|
||||
request: Request,
|
||||
p: Principal = Depends(admin_principal), # noqa: B008
|
||||
p: Principal = Depends(stamp_principal), # noqa: B008
|
||||
d: Db = Depends(get_db), # noqa: B008
|
||||
) -> dict[str, Any]:
|
||||
length = request.headers.get("content-length")
|
||||
|
||||
Reference in New Issue
Block a user