Wire case-level access control into gather, investigations, monitoring, reports routers #67

Closed
opened 2026-05-23 01:35:28 +00:00 by Mark · 2 comments
Owner

Parent Issue: #36 (Case-level access control) / #53

Scope

Wire require_case_access dependency into gather, investigations, monitoring, and reports routers.

Background

These are the operational routers that trigger actions within a case context. They receive case_id via request body or path param.

Acceptance Criteria

  • POST /gather/ — requires editor case access (case_id in request body)
  • POST /investigations/ — requires editor case access (case_id in request body)
  • GET /investigations/{id} — requires viewer case access (resolve case_id from investigation)
  • GET /monitoring/?case_id=X — requires viewer case access
  • POST /monitoring/ — requires editor case access (case_id in body)
  • DELETE /monitoring/{id} — requires editor case access
  • GET /reports/{case_id} — requires viewer case access
  • POST /reports/{case_id} — requires editor case access
  • Monitoring jobs endpoints: resolve case_id from parent monitoring target
  • Admin bypass works for all endpoints
  • Existing tests updated

Dependencies

  • #64 (cases router + membership management endpoints must exist first)

Implementation Notes

  • Gather and investigations receive case_id in POST body — validate access before executing
  • Reports uses case_id as path param — straightforward require_case_access usage
  • Monitoring jobs need to resolve case_id through the parent MonitoringTarget entity
## Parent Issue: #36 (Case-level access control) / #53 ## Scope Wire `require_case_access` dependency into **gather, investigations, monitoring, and reports routers**. ## Background These are the operational routers that trigger actions within a case context. They receive `case_id` via request body or path param. ## Acceptance Criteria - [ ] `POST /gather/` — requires `editor` case access (case_id in request body) - [ ] `POST /investigations/` — requires `editor` case access (case_id in request body) - [ ] `GET /investigations/{id}` — requires `viewer` case access (resolve case_id from investigation) - [ ] `GET /monitoring/?case_id=X` — requires `viewer` case access - [ ] `POST /monitoring/` — requires `editor` case access (case_id in body) - [ ] `DELETE /monitoring/{id}` — requires `editor` case access - [ ] `GET /reports/{case_id}` — requires `viewer` case access - [ ] `POST /reports/{case_id}` — requires `editor` case access - [ ] Monitoring jobs endpoints: resolve case_id from parent monitoring target - [ ] Admin bypass works for all endpoints - [ ] Existing tests updated ## Dependencies - #64 (cases router + membership management endpoints must exist first) ## Implementation Notes - Gather and investigations receive case_id in POST body — validate access before executing - Reports uses case_id as path param — straightforward `require_case_access` usage - Monitoring jobs need to resolve case_id through the parent MonitoringTarget entity
Author
Owner

⏸ Needs Decision

Blocking factors:

  • Didn't meet auto-approve criteria — scope=medium, risk=low, spec=aligned

Scope: medium Risk: low Spec: aligned Type: enhancement
Summary: Wire the existing require_case_access dependency into 4 routers (gather, investigations, monitoring, reports) to enforce viewer/editor case-level authorization on ~15 endpoints.
Suggestion: Three patterns needed: (1) path-param case_id (reports, investigations/case/{case_id}) → Depends(require_case_access(...)) directly; (2) body-based case_id (gather POST, investigations create, parse-evidence) → inline CaseAccessService.can_access() call after body extraction; (3) entity-resolved case_id (investigations/{run_id}, monitoring/jobs/{job_id}) → DB-lookup first then inline can_access() check. Monitoring's GET /jobs with optional case_id needs a conditional check. The AC mentions POST /reports/{case_id} but that endpoint doesn't exist yet — may need creation or the AC groups existing report endpoints under a shorthand.
Rationale: Medium scope because 4 router files and ~15 endpoints are touched, including 3 distinct patterns for case_id resolution (path param, request body, entity lookup). Risk is low: this is purely additive — no existing routes, schemas, or behaviors change, the require_case_access dependency and CaseAccessService are already implemented and tested, admin bypass is built in, and only new 403 errors can emerge. Aligned with parent issues #36/#53.

Action: Review each blocking factor above. Remove blocked:needs-decision label to route back to triage.

## ⏸ Needs Decision **Blocking factors:** - **Didn't meet auto-approve criteria** — scope=medium, risk=low, spec=aligned --- **Scope:** medium **Risk:** low **Spec:** aligned **Type:** enhancement **Summary:** Wire the existing require_case_access dependency into 4 routers (gather, investigations, monitoring, reports) to enforce viewer/editor case-level authorization on ~15 endpoints. **Suggestion:** Three patterns needed: (1) path-param case_id (reports, investigations/case/{case_id}) → Depends(require_case_access(...)) directly; (2) body-based case_id (gather POST, investigations create, parse-evidence) → inline CaseAccessService.can_access() call after body extraction; (3) entity-resolved case_id (investigations/{run_id}, monitoring/jobs/{job_id}) → DB-lookup first then inline can_access() check. Monitoring's GET /jobs with optional case_id needs a conditional check. The AC mentions POST /reports/{case_id} but that endpoint doesn't exist yet — may need creation or the AC groups existing report endpoints under a shorthand. **Rationale:** Medium scope because 4 router files and ~15 endpoints are touched, including 3 distinct patterns for case_id resolution (path param, request body, entity lookup). Risk is low: this is purely additive — no existing routes, schemas, or behaviors change, the require_case_access dependency and CaseAccessService are already implemented and tested, admin bypass is built in, and only new 403 errors can emerge. Aligned with parent issues #36/#53. **Action:** Review each blocking factor above. Remove `blocked:needs-decision` label to route back to triage.
Author
Owner

GSD VERIFY phase failed after 3 attempts.
═══ Gate: verify-passed (osint-hub#67) ═══
Mode: Tier 1 (functional only)

GATE OK: All gates up to 'implement' pass
── Tier 1: Functional ────────────────────────────────
Running tests...
❌ Tests failed (exit 4)
configfile: pyproject.toml
plugins: anyio-4.13.0, httpx-0.36.2, timeout-2.4.0, xdist-3.8.0, cov-7.1.0, split-0.11.0, asyncio-1.4.0
timeout: 60.0s
timeout method: signal
timeout func_only: False
asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function
collected 0 items

============================ no tests ran in 0.00s =============================
ERROR: file or directory not found: tests/

============================= test session starts ==============================
platform linux -- Python 3.11.14, pytest-9.0.3, pluggy-1.6.0
rootdir: /home/b/Projects/osint-hub
configfile: pyproject.toml
plugins: anyio-4.13.0, httpx-0.36.2, timeout-2.4.0, xdist-3.8.0, cov-7.1.0, split-0.11.0, asyncio-1.4.0
asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function
collected 0 items

============================ no tests ran in 0.00s =============================
Running linter...
✅ Lint clean
Running type checker...
✅ Types check clean
Previous test count: unknown

❌ GATE FAIL: verify-passed
- Tests failed with exit code 4
GATE FAIL: verify — blocked at 2026-05-26T23:13:51Z
Class: test-failure
Evidence: Verification failed: 1 failures
Git tag: osint-hub#67-gate-verify-fail-ac0f77c0bf7b (use gate-retry after fixing)

GSD VERIFY phase failed after 3 attempts. ═══ Gate: verify-passed (osint-hub#67) ═══ Mode: Tier 1 (functional only) GATE OK: All gates up to 'implement' pass ── Tier 1: Functional ──────────────────────────────── Running tests... ❌ Tests failed (exit 4) configfile: pyproject.toml plugins: anyio-4.13.0, httpx-0.36.2, timeout-2.4.0, xdist-3.8.0, cov-7.1.0, split-0.11.0, asyncio-1.4.0 timeout: 60.0s timeout method: signal timeout func_only: False asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function collected 0 items ============================ no tests ran in 0.00s ============================= ERROR: file or directory not found: tests/ ============================= test session starts ============================== platform linux -- Python 3.11.14, pytest-9.0.3, pluggy-1.6.0 rootdir: /home/b/Projects/osint-hub configfile: pyproject.toml plugins: anyio-4.13.0, httpx-0.36.2, timeout-2.4.0, xdist-3.8.0, cov-7.1.0, split-0.11.0, asyncio-1.4.0 asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function collected 0 items ============================ no tests ran in 0.00s ============================= Running linter... ✅ Lint clean Running type checker... ✅ Types check clean Previous test count: unknown ❌ GATE FAIL: verify-passed - Tests failed with exit code 4 GATE FAIL: verify — blocked at 2026-05-26T23:13:51Z Class: test-failure Evidence: Verification failed: 1 failures Git tag: osint-hub#67-gate-verify-fail-ac0f77c0bf7b (use gate-retry after fixing)
Mark closed this issue 2026-06-08 18:10:20 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Mark/osint-hub#67
No description provided.