Rustelo/resources/nickel/rbac/contracts.ncl
Jesús Pérez f1010e9d07
chore: update
2026-07-18 20:16:16 +01:00

166 lines
4.6 KiB
Text

# RBAC Policy Configuration Type Contracts
#
# Defines types for the file-based RBAC layer.
# Identity (who the user is) comes from JWT claims / AuthContext.
# This file defines what groups are allowed to do (policy rules + effects).
let EffectTypes = ["redirect", "log", "audit_log", "notify", "response"] in
let ResourceKinds = ["endpoint", "page", "asset"] in
let Outcomes = ["allow", "deny"] in
{
# Validate that a value belongs to a fixed set
OneOf = fun allowed => std.contract.from_predicate (fun v => std.array.elem v allowed),
# Effect type discriminator contract
EffectType
| doc "Possible effect types: redirect | log | audit_log | notify | response"
= OneOf EffectTypes,
# Policy effect: describes what to do when a rule matches
# The `type` field discriminates the variant
PolicyEffect = {
type
| OneOf EffectTypes
| doc "Effect variant discriminator",
# --- redirect: issue HTTP redirect to another URL ------------------
to
| String
| doc "redirect: destination URL. Supports {request.path}, {user.email}, {user.id}"
| optional,
# --- log | audit_log: write to application or audit log -----------
level
| String
| doc "log|audit_log: severity level (debug|info|warn|error)"
| default = "info",
message
| String
| doc "log|audit_log|notify: message template with interpolation support"
| optional,
event
| String
| doc "audit_log: semantic event name (e.g. 'access_denied')"
| optional,
# --- notify: send to external channel ----------------------------
channel
| String
| doc "notify: destination channel (security|slack|webhook|email)"
| optional,
# --- response: return HTTP response directly ---------------------
status
| Number
| doc "response: HTTP status code (401, 403, 302 ...)"
| default = 403,
body
| { _ : Dyn }
| doc "response: JSON response body"
| optional,
},
# A single access rule: matches resource kind, URL pattern, HTTP methods
PolicyRule = {
resource
| OneOf ResourceKinds
| doc "Resource kind: endpoint | page | asset",
pattern
| String
| doc "Glob pattern matched against the request path (e.g. '/api/*', '/*')",
outcome
| OneOf Outcomes
| doc "Decision when this rule matches: allow | deny",
methods
| Array String
| doc "HTTP methods this rule applies to. Empty array = all methods"
| default = [],
effects
| Array PolicyEffect
| doc "Effects executed when this rule matches"
| default = [],
},
# Group definition with optional inheritance
FileRbacGroup = {
name
| String
| doc "Group identifier (must match role names from JWT, e.g. 'admin', 'moderator')",
parent_groups
| Array String
| doc "Groups whose permissions are inherited (BFS expansion)"
| default = [],
permissions
| Array PolicyRule
| doc "Access rules for this group (first match wins)"
| default = [],
},
# Per-user permission override (highest priority, matched by email or sub)
FileRbacUserOverride = {
id
| String
| doc "User identifier: email address or JWT sub claim",
permissions
| Array PolicyRule
| doc "User-specific rules (override all group rules)"
| default = [],
},
# Default rules for unauthenticated and fallback behaviour
FileRbacDefaults = {
unauthenticated_allow
| Array PolicyRule
| doc "Explicit allow rules for unauthenticated requests (first match wins)"
| default = [],
unauthenticated_deny
| Array PolicyRule
| doc "Explicit deny rules for unauthenticated requests (evaluated after allow)"
| default = [],
authenticated_fallback
| OneOf Outcomes
| doc "Outcome when an authenticated user matches no rule"
| default = "deny",
unauthenticated_fallback
| OneOf Outcomes
| doc "Outcome when an unauthenticated user matches no rule"
| default = "deny",
fallback_effects
| Array PolicyEffect
| doc "Effects applied when the fallback outcome is triggered"
| default = [],
},
# Root RBAC configuration record
FileRbacConfig = {
groups
| Array FileRbacGroup
| doc "Group definitions with their permissions"
| default = [],
user_overrides
| Array FileRbacUserOverride
| doc "Per-user permission overrides (highest priority)"
| default = [],
defaults
| FileRbacDefaults
| doc "Fallback rules for unmatched requests"
| default = {},
},
}