167 lines
4.6 KiB
Text
167 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 = {},
|
||
|
|
},
|
||
|
|
}
|