CRITICAL: No case-level access control — any authenticated user can access any case #36
Labels
No labels
agentic-osint
audit
blocked:commit-ready-failed
blocked:gate-fetch
blocked:gate-fetch
blocked:gate-lint
blocked:gate-plan
blocked:gate-research
blocked:needs-decision
blocked:needs-human
blocked:no-changes
blocked:preflight
blocked:verify-failed
bug
chore
deferred
enhancement
feature-request
in-progress
lint-baseline
lint-cleanup
needs-decompose
needs-triage
performance
quickfix
security/medium
triage-approved
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Mark/osint-hub#36
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
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
When #52 and #53 are complete, they will comment here with findings and the implementation plan.
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.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.
Case-Level Access Control — Implementation Plan
Summary
The infrastructure already exists (
CaseMembershipmodel,CaseAccessService,require_case_accessdependency) but nothing uses it. This plan wires it into all routers and adds membership management.Role Hierarchy
Routers That Need Changes
14 case-scoped routers — all currently use only
require_role()with no case-level checks:4 routers with NO changes needed: auth, users, settings, audit, tasks_api (no case_id context)
Sub-Issues (Implementation Order)
#64 — Wire case-level access control into cases router (FOUNDATION)
/cases/{id}/members)created_byon case creation#65 — Wire case-level access control into evidence, sources, findings, leads routers
#66 — Wire case-level access control into orgs, people, contacts, relationships routers
#67 — Wire case-level access control into gather, investigations, monitoring, reports routers
#68 — Wire case-level access control into dashboard, entities routers + case activity endpoint
#69 — Migration: backfill CaseMembership for existing cases
#70 — Frontend: case membership UI and access-denied handling
Dependency Graph
Key Technical Decisions
CaseAccessService.can_access()—user.role == "admin"skips membership check/{id}endpoints, resolvecase_idfrom entity's FK before checking accessCaseMembershipto filter results to only cases where user has membershipCaseMembershipalready has all required fields (case_id, user_id, role)Risks
This issue has been decomposed into the following sub-issues:
Each sub-issue has been labeled with
needs-triageand will be evaluated by the triage pipeline before queue routing.