Skip to content

Documentation

Field-Level Access Control (FLAC)

Field-level security in SveltyCMS β€” what is enforced today (filter/search + API read filtering) and the schema-level utilities built for write guards.

8/10/2026
4 min read Edit on GitHub

SveltyCMS implements field-level security in two layers:

  1. Schema-based FLAC utilities (src/utils/field-access.ts): canAccessField / enforceFieldAccess evaluate per-field permissions (visibility, requiredAuth, readRoles, writeRoles) declared in the Collection Builder.
  2. FIELD_PERMISSIONS response filter (August 2026): a cached settings-level policy ({collection: {role: [readable fields]}}) applied to API read responses in handle-token-resolution.

This page describes both, and is explicit about what is enforced in the request path today versus what is built but not yet wired.


πŸ›‘οΈ Enforcement Status (Truthful)

Layer Status Where
Filter/search whitelist β€” non-readers cannot filter (or search) restricted fields βœ… Enforced collection-filter-engine.ts uses canAccessField per field schema
API read responses β€” fields filtered per role policy βœ… Enforced FIELD_PERMISSIONS setting applied in handle-token-resolution (admin / absent-policy fast-paths)
UI configuration β€” per-field readRoles / writeRoles / visibility / requiredAuth βœ… Enforced Collection Builder β†’ field β†’ Permissions tab (modal-widget-form.svelte)
Full read-stripping on fetch (enforceFieldAccess, nested + i18n key removal) ⏳ Utility ready, not wired src/utils/field-access.ts; wiring is a roadmap item
Write rejection β€” 403 + UNAUTHORIZED_ACCESS audit on guarded-field writes ⏳ Utility ready, not wired enforceFieldAccess(operation: "write"); wiring is a roadmap item
Note

Parity status (as of 2026-08-10): the read side is production-shipped; the write-guard side of the schema-level FLAC utilities is implemented and unit-tested but not yet invoked by the mutation pipeline. This is tracked in the 2026 Roadmap under Field-level write guards.


πŸ—οΈ Architecture

graph TD A[Incoming Request] --> B[Middleware: Identity & Roles] B --> C[API Handler / Local SDK] C --> D[modifyRequest Pipeline] subgraph Enforced Today D --> F[FIELD_PERMISSIONS response filter
handle-token-resolution] D --> E[Filter/search whitelist
canAccessField in collection-filter-engine] end subgraph Schema FLAC Utilities src/utils/field-access.ts F --> G[canAccessField per-field check] E --> G G --> H[enforceFieldAccess - read stripping] G --> I[enforceFieldAccess - write rejection 403 + audit] end H --> J[Sanitized JSON Response] I -- "not yet wired" --> K[Roadmap: mutation pipeline]

βš™οΈ Configuration

Per-field schema policy (Collection Builder)

FLAC is configured per-field in the Collection Builder under the Permissions tab.

// Technical Representation in Schema
{
    db_fieldName: "internal_notes",
    widget: widgets.Textarea,
    permissions: {
        visibility: "private",
        requiredAuth: true,
        readRoles: ["admin", "editor"],
        writeRoles: ["admin"]
    }
}

Settings-level response policy (FIELD_PERMISSIONS)

Admins may also define a collection β†’ role β†’ readable-fields map via System Settings. When present, it filters API read responses in handle-token-resolution:

{
  "posts": {
    "editor": ["title", "body", "status"],
    "contributor": ["title"],
  },
}

The memo is invalidated on settings save; admin and absent-policy requests skip the filter entirely (zero hot-path cost).


πŸ›‘οΈ Security Guarantees

1. Filter / Search Protection (enforced)

canAccessField is invoked by collection-filter-engine.ts before any query is built. Fields the caller’s role cannot read are excluded from where / whereBetween / search clauses β€” restricted fields cannot be used to probe data through filtering or full-text search.

2. Read Response Filtering (enforced)

API read responses are field-filtered against the FIELD_PERMISSIONS policy. Only readable fields reach the client; localized variants and nested paths are covered by the policy structure.

3. Physical Stripping + Strict Write Rejection (utility ready, wiring pending)

enforceFieldAccess (when wired) will:

  • Read: delete restricted keys from the JSON object, including all localized variants (e.g., title_de, title_fr) and nested dot-notation paths.
  • Write: collect all violations, throw AppError(403, "FORBIDDEN"), and emit an UNAUTHORIZED_ACCESS audit event with the blocked fields, collection, and entry β€” before the transaction proceeds.

4. Fail-Closed Default

Both utilities fail closed: an empty readRoles / writeRoles array denies everyone except admin/system; hidden or private fields require explicit role membership; anonymous (public) callers are denied when requiredAuth is set.


πŸ“Š Performance Impact

  • O(N) sanitization β€” a single pass over the field schema.
  • Zero-cost fast-path β€” admin and system users bypass FLAC entirely; FIELD_PERMISSIONS fast-paths skip the filter when no policy matches.
  • Memoized policy β€” the FIELD_PERMISSIONS map is cached and only invalidated on settings save.

πŸ“š Related Documentation


Related

securityflacrbacenforcement
Was this page helpful?