Skip to main content

core_api/
roles.rs

1//! RBAC role definitions and sidecar I/O.
2//!
3//! [`RoleDef`] is the public unit of role configuration. Roles are declared in
4//! [`Schema::roles`](crate::schema::Schema) and persisted as `roles.json` in
5//! the database directory via [`GraphDb::apply_schema`].
6//!
7//! # Never-widen rule
8//!
9//! - Empty role (no keys, no labels) = empty mask = sees nothing.
10//! - Unknown role on a request = `Err` (never silently grant full access).
11//! - Corrupt `roles.json` at open = roles poisoned; [`GraphDb::mask_for_role`]
12//!   returns `Err` for any role name until the file is fixed and the DB
13//!   re-opened.
14//!
15//! # Persistence
16//!
17//! `roles.json` format: `{ "version": 1, "roles": [...] }`. Written
18//! atomically (temp → fsync → rename → dir-sync) only when roles change;
19//! a no-change re-apply leaves the file byte-identical.
20
21use serde::{Deserialize, Serialize};
22
23/// A named RBAC role: resolves to a node-visibility mask at query time.
24///
25/// `keys` and `labels` both default to empty when absent from JSON, so a
26/// schema snippet that names only labels is valid.
27///
28/// The resolved mask is the union of:
29/// - all nodes whose key appears in `keys` (unknown keys silently ignored), and
30/// - all nodes carrying any label in `labels` (resolved live against the current
31///   graph — new nodes of an allowed label are immediately visible without
32///   re-applying the schema).
33///
34/// An empty union (no keys, no matching label nodes) = empty mask = sees nothing.
35#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
36pub struct RoleDef {
37    pub name: String,
38    /// Explicit node keys always visible to the role.
39    #[serde(default)]
40    pub keys: Vec<String>,
41    /// All nodes carrying any of these labels are visible (resolved live).
42    #[serde(default)]
43    pub labels: Vec<String>,
44}
45
46/// On-disk wrapper for `roles.json`.  Version field allows future format bumps.
47#[derive(Serialize, Deserialize)]
48pub(crate) struct RolesFile {
49    pub version: u32,
50    pub roles: Vec<RoleDef>,
51}
52
53impl RolesFile {
54    pub(crate) fn v1(roles: Vec<RoleDef>) -> Self {
55        RolesFile { version: 1, roles }
56    }
57}