Commit the Firestore security rules that were never in source control
SAAS_PLAN.md's review found the actual finding underneath "no multi-tenancy": drb-frontend reads Firestore directly from the browser (every hook in lib/ does onSnapshot(collection(db, ...))), so drb-c2-core/app/internal/auth.py is never in that read path at all. Whatever rules were protecting calls, incidents, and nodes had been hand-set in the Firebase console - unversioned, unreviewed, and invisible to anyone reading this repo. Added infra/firestore/firestore.rules: deny-by-default, with every tenant-scoped collection (nodes, systems, calls, incidents, alert_events, alert_rules) gated on resource.data.org_id == request.auth.token.org_id, an org_id claim that doesn't exist yet - the next commits add it. All client writes stay denied; c2-core's admin SDK bypasses rules and remains the sole writer, which was already the architecture. Secret-bearing collections (node_keys, the new enrollment_tokens) are denied to clients outright rather than org-scoped, since nothing should ever hand a raw credential to the browser. trips/trip_events keep their current "signed-in users can read" shape rather than being pulled into org scoping - that feature isn't tenant-scoped in this pass (see B7), just hidden from non-founding-org users in the UI. Added infra/firestore/firestore.indexes.json for the composite indexes the org_id-scoped queries will need once the frontend hooks add the equality filter alongside their existing orderBy/range/array-contains clauses - without these, those queries fail at runtime with a FAILED_PRECONDITION "index required" error rather than at review time. Also extended internal/firestore.py's collection_where() with optional order_by/limit_to/start_after params (SAAS_PLAN.md item 1, a stated prerequisite for B2: scoped queries need to stay ordered and bounded, and the existing helper could only do unordered full-collection scans). array_contains needed no new code - it was already a pass-through op string to FieldFilter. None of this is live yet. Deploying rules/indexes is a manual step (firebase deploy --only firestore:rules,firestore:indexes --project <project-id>, from infra/firestore/) - nothing in CI does this. Until it runs, the console-configured rules are still what's actually enforced, and these rules reference an org_id claim no token carries yet. Deploy this alongside (not before) the org_id-stamping commits that follow, or every read breaks for the current single-org deployment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
7b5258cfdf
commit
94ce9d48e2
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"//": "Composite indexes required once drb-frontend's lib/use*.ts hooks add a where(\"org_id\",\"==\",orgId) equality filter alongside an existing range/orderBy/array-contains clause. Firestore auto-indexes single-field lookups and equality-only compound queries, but org_id==X combined with an inequality, orderBy on a different field, or array-contains needs an explicit composite index or the query fails at runtime with a FAILED_PRECONDITION 'index required' error (see SAAS_PLAN.md 2.8/B2 and the URL Firestore prints in that error, which is the fastest way to double-check this list against the live query shapes). Deploy with: firebase deploy --only firestore:indexes --project <project-id>",
|
||||
"indexes": [
|
||||
{
|
||||
"collectionGroup": "calls",
|
||||
"queryScope": "COLLECTION",
|
||||
"fields": [
|
||||
{ "fieldPath": "org_id", "order": "ASCENDING" },
|
||||
{ "fieldPath": "started_at", "order": "ASCENDING" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"collectionGroup": "calls",
|
||||
"queryScope": "COLLECTION",
|
||||
"fields": [
|
||||
{ "fieldPath": "org_id", "order": "ASCENDING" },
|
||||
{ "fieldPath": "incident_ids", "arrayConfig": "CONTAINS" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"collectionGroup": "incidents",
|
||||
"queryScope": "COLLECTION",
|
||||
"fields": [
|
||||
{ "fieldPath": "org_id", "order": "ASCENDING" },
|
||||
{ "fieldPath": "started_at", "order": "ASCENDING" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"collectionGroup": "alert_events",
|
||||
"queryScope": "COLLECTION",
|
||||
"fields": [
|
||||
{ "fieldPath": "org_id", "order": "ASCENDING" },
|
||||
{ "fieldPath": "triggered_at", "order": "ASCENDING" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"collectionGroup": "alert_events",
|
||||
"queryScope": "COLLECTION",
|
||||
"fields": [
|
||||
{ "fieldPath": "org_id", "order": "ASCENDING" },
|
||||
{ "fieldPath": "acknowledged", "order": "ASCENDING" },
|
||||
{ "fieldPath": "triggered_at", "order": "ASCENDING" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"fieldOverrides": []
|
||||
}
|
||||
Reference in New Issue
Block a user