Expand description
Class-level permission enforcement.
CLP evaluation is two stages, not one. Stage one, validate_permission, is a gate that
throws. Stage two, apply_pointer_permissions, is a filter that narrows the query. Passing
the gate is not authorization to read anything: {find: {requiresAuthentication: true, pointerFields: ['owner']}} passes the gate for any logged-in user and is still restricted to
that user’s own rows by stage two. Conflating them, or treating stage two as optional extra
narrowing, is a data-exposure bug rather than a missing feature.
Two more shapes here are load bearing:
- CLP is default-open. An absent operation entry means unrestricted. That lives in
parse_rust_core::clp, whereop()returnsOption<&OpPerm>andOpPermhas noDefault.test_permissionsis the only place that reads the rule, and both stages call it. - Deny-all cannot be spelled
None.PointerPermOutcomehas three variants and is#[must_use], because upstream signals deny-all by returningundefinedfrom a function that otherwise returns a query (DatabaseController.js:1770-1772), and anOption<Query>reproduces that hazard exactly:Nonereads as “nothing to add”.
Every denial in this module is one of upstream’s createSanitizedError call sites, so each
one goes through ParseError::permission_denied and the client sees Permission denied at
the default. The detailed strings are still exact, because they are what the wire carries when
enableSanitizedErrorResponse is off, and they are what the log carries either way.
Master and maintenance never reach any of this. Every upstream call site is guarded by
isMaster ? Promise.resolve() : ... (DatabaseController.js:575-578, :849-852, :935-938,
:1471-1474), and here the guard is the caller matching on crate::AclScope::Unrestricted.
Structs§
- Permission
Options - Options that change what a permission check decides, and what it says when it denies.
- Protected
Field Plan - The fields to strip from a result, plus the rules that can only be evaluated against a row.
Enums§
- Pointer
Perm Outcome - Stage two’s three outcomes.
#[must_use], and every caller matches all three. - Write
Action - Which write a permission check belongs to.
Functions§
- adds_
field canAddField(DatabaseController.js:970-998): does this write introduce a field the schema does not have?- apply_
pointer_ permissions - Stage two: the query filter.
addPointerPermissions(DatabaseController.js:1731-1819). - deny_
protected_ fields denyProtectedFields(RestQuery.js:928-984).- filter_
sensitive_ data filterSensitiveData(DatabaseController.js:192-303), applied to one row in upstream’s order.- plan_
protected_ fields addProtectedFields(DatabaseController.js:1821-1925).- test_
permissions testPermissions(SchemaController.js:1365-1382).- validate_
permission - Stage one: the gate.
validatePermission(SchemaController.js:1385-1459).