Skip to main content

systemprompt_security/authz/repository/
mod.rs

1//! `AccessControlRepository` — sqlx-backed access to the two-table authz
2//! schema.
3//!
4//! `access_control_entities` owns one row per `(entity_type, entity_id)` and
5//! carries the `default_included` flag plus a `source` provenance string.
6//! `access_control_rules` is the per-(entity, subject) grant table, with a
7//! foreign key back to the entity catalog. Callers fetch the entity row
8//! first (a `None` result signals an entity unknown to access control), then
9//! list rules for it, and hand both to [`super::resolver::resolve`].
10
11mod entities;
12mod rules;
13
14use std::sync::Arc;
15
16use sqlx::PgPool;
17use systemprompt_database::DbPool;
18
19use super::error::{AuthzError, AuthzResult};
20use super::types::{Access, EntityKind, RuleType};
21
22#[derive(Debug, Clone)]
23pub struct ExportRuleRow {
24    pub entity_type: String,
25    pub entity_id: String,
26    pub rule_type: String,
27    pub rule_value: String,
28    pub access: String,
29    pub justification: Option<String>,
30}
31
32#[derive(Debug, Clone, Copy)]
33pub struct UpsertRuleParams<'a> {
34    pub entity_type: EntityKind,
35    pub entity_id: &'a str,
36    pub rule_type: RuleType,
37    pub rule_value: &'a str,
38    pub access: Access,
39    /// Operator-supplied note explaining *why* this rule exists. Surfaced in
40    /// the matrix tooltip and in the audit row's `evaluated_rules` JSON when
41    /// the rule decides. `None` means the operator declined to give a reason.
42    pub justification: Option<&'a str>,
43}
44
45#[derive(Clone, Debug)]
46pub struct AccessControlRepository {
47    pool: Arc<PgPool>,
48    write_pool: Arc<PgPool>,
49}
50
51impl AccessControlRepository {
52    pub fn new(db: &DbPool) -> AuthzResult<Self> {
53        let pool = db
54            .pool_arc()
55            .map_err(|err| AuthzError::Validation(err.to_string()))?;
56        let write_pool = db
57            .write_pool_arc()
58            .map_err(|err| AuthzError::Validation(err.to_string()))?;
59        Ok(Self { pool, write_pool })
60    }
61
62    pub fn from_pool(pool: Arc<PgPool>) -> Self {
63        let write_pool = Arc::clone(&pool);
64        Self { pool, write_pool }
65    }
66}