CRITICAL: No case-level access control — any authenticated user can access any case #36

Closed
opened 2026-05-09 05:18:37 +00:00 by Mark · 4 comments
Owner

Description

No case-level access control — any authenticated user can access any case.

Severity

CRITICAL

Impact

Any user can view/edit any investigation case. No permission model.

Suggested Fix

Implement an access control layer (owner + collaborators, or role-based per case).

Blockers

  • #52 Research: Access control models for case-level OSINT data
  • #53 Plan: Implementation plan for case-level access control

When #52 and #53 are complete, they will comment here with findings and the implementation plan.

## Description No case-level access control — any authenticated user can access any case. ## Severity CRITICAL ## Impact Any user can view/edit any investigation case. No permission model. ## Suggested Fix Implement an access control layer (owner + collaborators, or role-based per case). ## Blockers - #52 Research: Access control models for case-level OSINT data - #53 Plan: Implementation plan for case-level access control When #52 and #53 are complete, they will comment here with findings and the implementation plan.
Author
Owner

Feature Triage Assessment

Scope: medium
Risk: high
Spec Alignment: aligned
Summary: Wires existing (but unused) case-level access control infrastructure into all API router endpoints to enforce per-case authz via CaseMembership table
Recommendation: The infrastructure already exists (CaseMembership model, CaseAccessService service, require_case_access FastAPI dependency) but nothing uses it. Implementation: (1) auto-assign creator as 'owner' in case POST, (2) add require_case_access() to all case-id-bearing endpoints across ~10 routers, (3) filter LIST by user's memberships, (4) add member management endpoints, (5) update ~200 existing tests that assume open access. Work in order of severity: GET/PUT/DELETE /cases/{id} first, then entity/evidence routers, then LIST filter.
Rationale: SCOPE=medium: ~5-15 routers need require_case_access wired in, plus a membership management endpoint, plus test updates. Not a new subsystem but broad touch surface. RISK=high: security enforcement in authz — off-by-one or missed endpoint = data leak. Tests currently write data without CaseMembership records, so test DB setup also needs updating. SPEC=aligned: CaseMembership and CaseAccessService exist as foundation — this completes the half-built feature.

Labeled blocked:needs-decision — unblock on Forgejo to dispatch to the fixer pipeline.

## Feature Triage Assessment **Scope:** medium **Risk:** high **Spec Alignment:** aligned **Summary:** Wires existing (but unused) case-level access control infrastructure into all API router endpoints to enforce per-case authz via CaseMembership table **Recommendation:** The infrastructure already exists (CaseMembership model, CaseAccessService service, require_case_access FastAPI dependency) but nothing uses it. Implementation: (1) auto-assign creator as 'owner' in case POST, (2) add require_case_access() to all case-id-bearing endpoints across ~10 routers, (3) filter LIST by user's memberships, (4) add member management endpoints, (5) update ~200 existing tests that assume open access. Work in order of severity: GET/PUT/DELETE /cases/{id} first, then entity/evidence routers, then LIST filter. **Rationale:** SCOPE=medium: ~5-15 routers need require_case_access wired in, plus a membership management endpoint, plus test updates. Not a new subsystem but broad touch surface. RISK=high: security enforcement in authz — off-by-one or missed endpoint = data leak. Tests currently write data without CaseMembership records, so test DB setup also needs updating. SPEC=aligned: CaseMembership and CaseAccessService exist as foundation — this completes the half-built feature. Labeled `blocked:needs-decision` — unblock on Forgejo to dispatch to the fixer pipeline.
Author
Owner

Approved as bug. Infrastructure already exists (CaseMembership model, CaseAccessService, require_case_access FastAPI dependency) — just needs wiring into ~10 routers, auto-assigning creator as owner on case creation, and updating ~200 tests. This completes a half-built feature.

Approved as bug. Infrastructure already exists (CaseMembership model, CaseAccessService, require_case_access FastAPI dependency) — just needs wiring into ~10 routers, auto-assigning creator as owner on case creation, and updating ~200 tests. This completes a half-built feature.
Author
Owner

Case-Level Access Control — Implementation Plan

Summary

The infrastructure already exists (CaseMembership model, CaseAccessService, require_case_access dependency) but nothing uses it. This plan wires it into all routers and adds membership management.

Role Hierarchy

Role Level Can do
Owner 3 Everything + manage members, delete case
Editor 2 Create/edit/delete entities, trigger gathers/investigations
Viewer 1 Read-only access to all case data
Admin — Bypasses all case-level checks (global)

Routers That Need Changes

14 case-scoped routers — all currently use only require_role() with no case-level checks:

Router case_id source Min role (read) Min role (write)
cases path/body viewer editor (owner for delete/members)
evidence query param/body viewer editor
sources query param/body viewer editor
findings query param/body viewer editor
leads query param/body viewer editor
organizations query param/body viewer editor
people query param/body viewer editor
contacts query param/body viewer editor
relationships query param/body viewer editor
gather request body — editor
investigations request body/path viewer editor
monitoring query param/body viewer editor
reports path param viewer editor
dashboard query param viewer —
entities search param viewer —

4 routers with NO changes needed: auth, users, settings, audit, tasks_api (no case_id context)

Sub-Issues (Implementation Order)

  1. #64 — Wire case-level access control into cases router (FOUNDATION)

    • Auto-assign owner membership on case creation
    • Filter case list by membership
    • Add membership management endpoints (GET/POST/PUT/DELETE /cases/{id}/members)
    • Sets created_by on case creation
  2. #65 — Wire case-level access control into evidence, sources, findings, leads routers

  3. #66 — Wire case-level access control into orgs, people, contacts, relationships routers

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

  5. #68 — Wire case-level access control into dashboard, entities routers + case activity endpoint

  6. #69 — Migration: backfill CaseMembership for existing cases

    • Must run after #64 is deployed
    • Uses audit log to find case creators, falls back to admin assignment
  7. #70 — Frontend: case membership UI and access-denied handling

    • Can start 403 error handling in parallel
    • Member management UI depends on #64 endpoints

Dependency Graph

#64 (foundation)
 ├── #65 (evidence trail)
 ├── #66 (entity graph)
 ├── #67 (operations)
 ├── #68 (dashboard/search)
 └── #69 (migration)
      └── #70 (frontend) — also depends on #64

Key Technical Decisions

  • Admin bypass: Already implemented in CaseAccessService.can_access() — user.role == "admin" skips membership check
  • Entity-resolved access: For /{id} endpoints, resolve case_id from entity's FK before checking access
  • LIST filtering: Join on CaseMembership to filter results to only cases where user has membership
  • No new models needed: CaseMembership already has all required fields (case_id, user_id, role)

Risks

  • ~200 existing tests assume open access — all need membership fixtures added
  • Frontend currently has no concept of case membership — UI changes are significant
  • Migration edge cases: cases with no creator info, no admin users
## Case-Level Access Control — Implementation Plan ### Summary The infrastructure already exists (`CaseMembership` model, `CaseAccessService`, `require_case_access` dependency) but nothing uses it. This plan wires it into all routers and adds membership management. ### Role Hierarchy | Role | Level | Can do | |------|-------|--------| | Owner | 3 | Everything + manage members, delete case | | Editor | 2 | Create/edit/delete entities, trigger gathers/investigations | | Viewer | 1 | Read-only access to all case data | | Admin | — | Bypasses all case-level checks (global) | ### Routers That Need Changes **14 case-scoped routers** — all currently use only `require_role()` with no case-level checks: | Router | case_id source | Min role (read) | Min role (write) | |--------|---------------|-----------------|------------------| | cases | path/body | viewer | editor (owner for delete/members) | | evidence | query param/body | viewer | editor | | sources | query param/body | viewer | editor | | findings | query param/body | viewer | editor | | leads | query param/body | viewer | editor | | organizations | query param/body | viewer | editor | | people | query param/body | viewer | editor | | contacts | query param/body | viewer | editor | | relationships | query param/body | viewer | editor | | gather | request body | — | editor | | investigations | request body/path | viewer | editor | | monitoring | query param/body | viewer | editor | | reports | path param | viewer | editor | | dashboard | query param | viewer | — | | entities | search param | viewer | — | **4 routers with NO changes needed:** auth, users, settings, audit, tasks_api (no case_id context) ### Sub-Issues (Implementation Order) 1. **#64 — Wire case-level access control into cases router** (FOUNDATION) - Auto-assign owner membership on case creation - Filter case list by membership - Add membership management endpoints (GET/POST/PUT/DELETE `/cases/{id}/members`) - Sets `created_by` on case creation 2. **#65 — Wire case-level access control into evidence, sources, findings, leads routers** 3. **#66 — Wire case-level access control into orgs, people, contacts, relationships routers** 4. **#67 — Wire case-level access control into gather, investigations, monitoring, reports routers** 5. **#68 — Wire case-level access control into dashboard, entities routers + case activity endpoint** 6. **#69 — Migration: backfill CaseMembership for existing cases** - Must run after #64 is deployed - Uses audit log to find case creators, falls back to admin assignment 7. **#70 — Frontend: case membership UI and access-denied handling** - Can start 403 error handling in parallel - Member management UI depends on #64 endpoints ### Dependency Graph ``` #64 (foundation) ├── #65 (evidence trail) ├── #66 (entity graph) ├── #67 (operations) ├── #68 (dashboard/search) └── #69 (migration) └── #70 (frontend) — also depends on #64 ``` ### Key Technical Decisions - **Admin bypass**: Already implemented in `CaseAccessService.can_access()` — `user.role == "admin"` skips membership check - **Entity-resolved access**: For `/{id}` endpoints, resolve `case_id` from entity's FK before checking access - **LIST filtering**: Join on `CaseMembership` to filter results to only cases where user has membership - **No new models needed**: `CaseMembership` already has all required fields (case_id, user_id, role) ### Risks - ~200 existing tests assume open access — all need membership fixtures added - Frontend currently has no concept of case membership — UI changes are significant - Migration edge cases: cases with no creator info, no admin users
Author
Owner

This issue has been decomposed into the following sub-issues:

# Title Phase Scope
#71 CRITICAL: No case-level access control — any authenticated u No case-level access control — any authenticated user can access any case.
#72 CRITICAL: No case-level access control — any authenticated u CRITICAL
#73 CRITICAL: No case-level access control — any authenticated u Any user can view/edit any investigation case. No permission model.
#74 CRITICAL: No case-level access control — any authenticated u Implement an access control layer (owner + collaborators, or role-based per case
#75 CRITICAL: No case-level access control — any authenticated u - #52 Research: Access control models for case-level OSINT data - #53 Plan: Impl

Each sub-issue has been labeled with needs-triage and will be evaluated by the triage pipeline before queue routing.

This issue has been decomposed into the following sub-issues: | # | Title | Phase Scope | |---|-------|-------------| | #71 | CRITICAL: No case-level access control — any authenticated u | No case-level access control — any authenticated user can access any case. | | #72 | CRITICAL: No case-level access control — any authenticated u | CRITICAL | | #73 | CRITICAL: No case-level access control — any authenticated u | Any user can view/edit any investigation case. No permission model. | | #74 | CRITICAL: No case-level access control — any authenticated u | Implement an access control layer (owner + collaborators, or role-based per case | | #75 | CRITICAL: No case-level access control — any authenticated u | - #52 Research: Access control models for case-level OSINT data - #53 Plan: Impl | Each sub-issue has been labeled with `needs-triage` and will be evaluated by the triage pipeline before queue routing.
Mark closed this issue 2026-05-28 00:20:41 +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#36
No description provided.