Access control
Choose who can read, create, update, and delete content, and protect individual fields.
Access rules decide who can use your content. Collection and global rules protect documents; field rules protect individual values. Ridu checks these rules for requests from the admin, REST, the SDK, and the local Go API.
If operation.Context, store.Document, or query.Path is unfamiliar, start with
Go packages. That guide explains the values and return types used below.
Access configuration
| API | Where it applies | Result |
|---|---|---|
ridu.Allow() |
Collection or global access rule | Permits the operation, subject to field rules and normal validation. |
ridu.Deny() |
Collection or global access rule | Rejects the operation. |
ridu.Where(expression) |
Collection document operations | Adds a database predicate atomically to the caller’s query or target lookup. |
Collection.Access |
Create, Read, Update, Delete, and capability-specific operations |
Configures document-level rules for each operation family. |
Global.Access |
Read, Update |
Configures singleton access. |
Field .Access(field.Access{...}) |
Create, Read, Update |
Replaces the field’s boolean rules. A denied read removes the field from output. |
Field .RestrictAccess(...) |
Existing field access | Conjoins supplied rules with inherited or reusable rules. |
ctx.Actor / ctx.ActorCollection |
Every access callback | Identifies the signed-in document and the auth collection that owns it. |
ctx.Data, ctx.Document, ctx.Siblings |
Phase-specific callbacks | Exposes current candidate or saved values; see each context before assuming availability. |
Rules never run for a Local API call made with System: true. Keep server-owned writes in such
calls rather than teaching every rule about a context flag; see
Trusted server work.
Write an access rule
A collection rule receives ridu.AccessContext, which includes the signed-in user (Actor),
the requested operation, document ID, submitted values, and locale. Return one of these decisions:
ridu.Allow()permits the operation.ridu.Deny()rejects it.ridu.Where(expression)permits it only for documents that match a filter, such as posts belonging to the signed-in author.
Keep reusable rules beside your content model. These helpers allow public reads, require a signed-in user, or limit access to documents belonging to that user:
package content
import (
"github.com/riducms/ridu"
"github.com/riducms/ridu/query"
)
func publicRead(ridu.AccessContext) (ridu.AccessDecision, error) {
return ridu.Allow(), nil
}
func signedIn(ctx ridu.AccessContext) (ridu.AccessDecision, error) {
// Actor is nil when no user is signed in.
if ctx.Actor == nil {
return ridu.Deny(), nil
}
return ridu.Allow(), nil
}
func ownDocuments(field string) ridu.AccessRule {
// Return a rule that remembers this collection's owner field.
return func(ctx ridu.AccessContext) (ridu.AccessDecision, error) {
if ctx.Actor == nil {
return ridu.Deny(), nil
}
// Check ownership as part of the database operation.
return ridu.Where(query.Equal(field, ctx.Actor.ID)), nil
}
}Protect a collection
Add the rules to Access on your collection. This example lets anyone read posts, lets signed-in
users create them, and lets authors update or delete their own posts. The author relationship
stores the user ID checked by ownDocuments. The internalNotes helper is defined below.
package content
import (
"github.com/riducms/ridu"
"github.com/riducms/ridu/field"
)
var Posts = ridu.Collection{
Slug: "posts",
Fields: field.Fields{
field.Text("title").Required(),
field.Relationship("author", "users").Required(),
internalNotes("internalNotes"),
},
Access: ridu.CollectionAccess{
Create: signedIn,
Read: publicRead,
Update: ownDocuments("author"),
Delete: ownDocuments("author"),
},
}Add Posts to your config’s Collections list.
Protect an individual field
Field rules return true to allow access or false to deny it. If a write is denied, Ridu rejects
the write. If a read is denied, Ridu leaves the field out of the response.
Their operation.Context is a field callback context. See
Operations and callbacks for the caller, nearby values, and
how this differs from a collection’s ridu.AccessContext.
This helper creates a notes field that only administrators can write and that editors and administrators can read. Reuse it in any collection, group, array, or block that needs the same protection.
package content
import (
"github.com/riducms/ridu/field"
"github.com/riducms/ridu/operation"
)
func internalNotes(name string) field.TextField {
allowed := func(roles ...string) field.AccessRule {
return func(ctx operation.Context) (bool, error) {
// A missing role matches none of the allowed names.
role, _ := ctx.Actor.Data.String("role")
for _, candidate := range roles {
if role == candidate {
return true, nil
}
}
return false, nil
}
}
return field.Textarea(name).Access(field.Access{
// Protect creates as well as updates.
Create: allowed("admin"),
Read: allowed("editor", "admin"),
Update: allowed("admin"),
})
}Here only administrators may create or update internalNotes; editors may read it. Omitting the
Create rule would leave that write allowed by default, even if Update were restricted.
Setting field.Admin{ReadOnly: true} only disables editing in the admin. Use access rules when
the API must reject changes as well.
.Access(...) replaces all of a field’s access rules. When extending a reusable field, use
.RestrictAccess(...) to add a restriction while keeping its existing rules; both rules must
allow the operation.
Filtering and sorting protected fields
Adding a Read rule also prevents API callers from filtering or sorting by that field. Otherwise,
someone could infer a hidden value by trying different filters, even though the response omitted
it. This restriction applies to every user, including users who can read the field in an individual
document.
It covers filters used by lists, counts, distinct values, select-all operations, and index windows.
Protecting a group, array, or block also protects its nested fields. Sorting the parent is blocked
when a nested field has a Read rule. Choosing another locale does not bypass the restriction.
These queries return HTTP 403 access_denied; the local API and GraphQL use
field_access_denied.
Your own ridu.Where(...) access rules can still filter by a protected field. For example, Ridu
can enforce ownership without exposing the owner field to the caller. Inverse relationships also
continue to find their related documents, but callers cannot add filters or sorting on a protected
backing field.
Use the field paths described in Querying data. Invalid paths, including block
paths without a variant and paths into internal rich-text storage, fail with bad_query (400).
Queries inside an unrestricted JSON field depend on the database adapter’s support for JSON queries.
Use another field in an access rule
Read ctx.Siblings to check other fields in the same group, array row, or block. For a field at
the document’s top level, it contains the document’s top-level fields. It also includes the field
being checked.
Here each link has a membersOnly checkbox. Anonymous readers receive the URL only when that
checkbox is off; signed-in readers can see every URL. Field rules identify an anonymous reader
with an empty ctx.Actor.ID.
var Pages = ridu.Collection{
Slug: "pages",
Fields: field.Fields{
field.Array("links", field.Fields{
field.Text("label").Required(),
field.Text("url").Required().Access(field.Access{
Read: func(ctx operation.Context) (bool, error) {
// Read the checkbox in this link row.
membersOnly, _ := ctx.Siblings.Get(
"membersOnly",
).BooleanValue()
return !membersOnly || ctx.Actor.ID != "", nil
},
}),
field.Checkbox("membersOnly").Default(false),
}),
},
}During a write, ctx.Prior contains the saved values from the same group or row before the change.
Ridu matches array and block rows by their key, so reordering rows does not mix up their previous
values. New rows and ordinary reads have no previous values in Prior.
Access rules can read these values but cannot change them. Use a field hook
to change a value. See Using other field values for examples of
Root, Siblings, Prior, and looking up a related document, or
operation.Context for the full reference.