systemprompt_security/authz/repository/
entities.rs1use std::str::FromStr;
7
8use super::AccessControlRepository;
9use crate::authz::error::AuthzResult;
10use crate::authz::types::{EntityKind, EntityRow};
11
12impl AccessControlRepository {
13 pub async fn get_entity(
14 &self,
15 entity_type: EntityKind,
16 entity_id: &str,
17 ) -> AuthzResult<Option<EntityRow>> {
18 let row = sqlx::query!(
19 r#"
20 SELECT entity_type, entity_id, default_included, source
21 FROM access_control_entities
22 WHERE entity_type = $1 AND entity_id = $2
23 "#,
24 entity_type.as_str(),
25 entity_id,
26 )
27 .fetch_optional(&*self.pool)
28 .await?;
29
30 let Some(row) = row else {
31 return Ok(None);
32 };
33 Ok(Some(EntityRow {
34 kind: EntityKind::from_str(&row.entity_type)?,
35 id: row.entity_id,
36 default_included: row.default_included,
37 source: row.source,
38 }))
39 }
40
41 pub async fn upsert_entity(
42 &self,
43 entity_type: EntityKind,
44 entity_id: &str,
45 default_included: bool,
46 source: &str,
47 ) -> AuthzResult<()> {
48 sqlx::query!(
49 r#"
50 INSERT INTO access_control_entities (entity_type, entity_id, default_included, source)
51 VALUES ($1, $2, $3, $4)
52 ON CONFLICT (entity_type, entity_id) DO UPDATE
53 SET default_included = EXCLUDED.default_included,
54 source = EXCLUDED.source,
55 updated_at = NOW()
56 "#,
57 entity_type.as_str(),
58 entity_id,
59 default_included,
60 source,
61 )
62 .execute(&*self.write_pool)
63 .await?;
64 Ok(())
65 }
66
67 pub async fn upsert_entities(
68 &self,
69 entity_type: EntityKind,
70 ids: &[&str],
71 default_included: bool,
72 source: &str,
73 ) -> AuthzResult<()> {
74 if ids.is_empty() {
75 return Ok(());
76 }
77 let ids_owned: Vec<String> = ids.iter().map(|id| (*id).to_owned()).collect();
78 sqlx::query!(
79 r#"
80 INSERT INTO access_control_entities (entity_type, entity_id, default_included, source)
81 SELECT $1, id, $3, $4
82 FROM UNNEST($2::text[]) AS id
83 ON CONFLICT (entity_type, entity_id) DO UPDATE
84 SET default_included = EXCLUDED.default_included,
85 source = EXCLUDED.source,
86 updated_at = NOW()
87 "#,
88 entity_type.as_str(),
89 &ids_owned,
90 default_included,
91 source,
92 )
93 .execute(&*self.write_pool)
94 .await?;
95 Ok(())
96 }
97
98 pub async fn list_entities(&self, entity_type: EntityKind) -> AuthzResult<Vec<EntityRow>> {
99 let rows = sqlx::query!(
100 r#"
101 SELECT entity_type, entity_id, default_included, source
102 FROM access_control_entities
103 WHERE entity_type = $1
104 ORDER BY entity_id
105 "#,
106 entity_type.as_str(),
107 )
108 .fetch_all(&*self.pool)
109 .await?;
110
111 let mut out = Vec::with_capacity(rows.len());
112 for row in rows {
113 out.push(EntityRow {
114 kind: EntityKind::from_str(&row.entity_type)?,
115 id: row.entity_id,
116 default_included: row.default_included,
117 source: row.source,
118 });
119 }
120 Ok(out)
121 }
122}