this repo has no description
1use crate::types::Handle;
2use chrono::{DateTime, Utc};
3use serde::{Deserialize, Serialize};
4use sqlx::PgPool;
5use uuid::Uuid;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct DelegationGrant {
9 pub id: Uuid,
10 pub delegated_did: String,
11 pub controller_did: String,
12 pub granted_scopes: String,
13 pub granted_at: DateTime<Utc>,
14 pub granted_by: String,
15 pub revoked_at: Option<DateTime<Utc>>,
16 pub revoked_by: Option<String>,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct DelegatedAccountInfo {
21 pub did: String,
22 pub handle: Handle,
23 pub granted_scopes: String,
24 pub granted_at: DateTime<Utc>,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct ControllerInfo {
29 pub did: String,
30 pub handle: Handle,
31 pub granted_scopes: String,
32 pub granted_at: DateTime<Utc>,
33 pub is_active: bool,
34}
35
36pub async fn is_delegated_account(pool: &PgPool, did: &str) -> Result<bool, sqlx::Error> {
37 let result = sqlx::query_scalar!(
38 r#"SELECT account_type::text = 'delegated' as "is_delegated!" FROM users WHERE did = $1"#,
39 did
40 )
41 .fetch_optional(pool)
42 .await?;
43
44 Ok(result.unwrap_or(false))
45}
46
47pub async fn create_delegation(
48 pool: &PgPool,
49 delegated_did: &str,
50 controller_did: &str,
51 granted_scopes: &str,
52 granted_by: &str,
53) -> Result<Uuid, sqlx::Error> {
54 let id = sqlx::query_scalar!(
55 r#"
56 INSERT INTO account_delegations (delegated_did, controller_did, granted_scopes, granted_by)
57 VALUES ($1, $2, $3, $4)
58 RETURNING id
59 "#,
60 delegated_did,
61 controller_did,
62 granted_scopes,
63 granted_by
64 )
65 .fetch_one(pool)
66 .await?;
67
68 Ok(id)
69}
70
71pub async fn revoke_delegation(
72 pool: &PgPool,
73 delegated_did: &str,
74 controller_did: &str,
75 revoked_by: &str,
76) -> Result<bool, sqlx::Error> {
77 let result = sqlx::query!(
78 r#"
79 UPDATE account_delegations
80 SET revoked_at = NOW(), revoked_by = $1
81 WHERE delegated_did = $2 AND controller_did = $3 AND revoked_at IS NULL
82 "#,
83 revoked_by,
84 delegated_did,
85 controller_did
86 )
87 .execute(pool)
88 .await?;
89
90 Ok(result.rows_affected() > 0)
91}
92
93pub async fn update_delegation_scopes(
94 pool: &PgPool,
95 delegated_did: &str,
96 controller_did: &str,
97 new_scopes: &str,
98) -> Result<bool, sqlx::Error> {
99 let result = sqlx::query!(
100 r#"
101 UPDATE account_delegations
102 SET granted_scopes = $1
103 WHERE delegated_did = $2 AND controller_did = $3 AND revoked_at IS NULL
104 "#,
105 new_scopes,
106 delegated_did,
107 controller_did
108 )
109 .execute(pool)
110 .await?;
111
112 Ok(result.rows_affected() > 0)
113}
114
115pub async fn get_delegation(
116 pool: &PgPool,
117 delegated_did: &str,
118 controller_did: &str,
119) -> Result<Option<DelegationGrant>, sqlx::Error> {
120 let grant = sqlx::query_as!(
121 DelegationGrant,
122 r#"
123 SELECT id, delegated_did, controller_did, granted_scopes,
124 granted_at, granted_by, revoked_at, revoked_by
125 FROM account_delegations
126 WHERE delegated_did = $1 AND controller_did = $2 AND revoked_at IS NULL
127 "#,
128 delegated_did,
129 controller_did
130 )
131 .fetch_optional(pool)
132 .await?;
133
134 Ok(grant)
135}
136
137pub async fn get_delegations_for_account(
138 pool: &PgPool,
139 delegated_did: &str,
140) -> Result<Vec<ControllerInfo>, sqlx::Error> {
141 let controllers = sqlx::query_as!(
142 ControllerInfo,
143 r#"
144 SELECT
145 u.did,
146 u.handle,
147 d.granted_scopes,
148 d.granted_at,
149 (u.deactivated_at IS NULL AND u.takedown_ref IS NULL) as "is_active!"
150 FROM account_delegations d
151 JOIN users u ON u.did = d.controller_did
152 WHERE d.delegated_did = $1 AND d.revoked_at IS NULL
153 ORDER BY d.granted_at DESC
154 "#,
155 delegated_did
156 )
157 .fetch_all(pool)
158 .await?;
159
160 Ok(controllers)
161}
162
163pub async fn get_accounts_controlled_by(
164 pool: &PgPool,
165 controller_did: &str,
166) -> Result<Vec<DelegatedAccountInfo>, sqlx::Error> {
167 let accounts = sqlx::query_as!(
168 DelegatedAccountInfo,
169 r#"
170 SELECT
171 u.did,
172 u.handle,
173 d.granted_scopes,
174 d.granted_at
175 FROM account_delegations d
176 JOIN users u ON u.did = d.delegated_did
177 WHERE d.controller_did = $1
178 AND d.revoked_at IS NULL
179 AND u.deactivated_at IS NULL
180 AND u.takedown_ref IS NULL
181 ORDER BY d.granted_at DESC
182 "#,
183 controller_did
184 )
185 .fetch_all(pool)
186 .await?;
187
188 Ok(accounts)
189}
190
191pub async fn get_active_controllers_for_account(
192 pool: &PgPool,
193 delegated_did: &str,
194) -> Result<Vec<ControllerInfo>, sqlx::Error> {
195 let controllers = sqlx::query_as!(
196 ControllerInfo,
197 r#"
198 SELECT
199 u.did,
200 u.handle,
201 d.granted_scopes,
202 d.granted_at,
203 true as "is_active!"
204 FROM account_delegations d
205 JOIN users u ON u.did = d.controller_did
206 WHERE d.delegated_did = $1
207 AND d.revoked_at IS NULL
208 AND u.deactivated_at IS NULL
209 AND u.takedown_ref IS NULL
210 ORDER BY d.granted_at DESC
211 "#,
212 delegated_did
213 )
214 .fetch_all(pool)
215 .await?;
216
217 Ok(controllers)
218}
219
220pub async fn count_active_controllers(
221 pool: &PgPool,
222 delegated_did: &str,
223) -> Result<i64, sqlx::Error> {
224 let count = sqlx::query_scalar!(
225 r#"
226 SELECT COUNT(*) as "count!"
227 FROM account_delegations d
228 JOIN users u ON u.did = d.controller_did
229 WHERE d.delegated_did = $1
230 AND d.revoked_at IS NULL
231 AND u.deactivated_at IS NULL
232 AND u.takedown_ref IS NULL
233 "#,
234 delegated_did
235 )
236 .fetch_one(pool)
237 .await?;
238
239 Ok(count)
240}
241
242pub async fn has_any_controllers(pool: &PgPool, did: &str) -> Result<bool, sqlx::Error> {
243 let exists = sqlx::query_scalar!(
244 r#"SELECT EXISTS(
245 SELECT 1 FROM account_delegations
246 WHERE delegated_did = $1 AND revoked_at IS NULL
247 ) as "exists!""#,
248 did
249 )
250 .fetch_one(pool)
251 .await?;
252
253 Ok(exists)
254}
255
256pub async fn controls_any_accounts(pool: &PgPool, did: &str) -> Result<bool, sqlx::Error> {
257 let exists = sqlx::query_scalar!(
258 r#"SELECT EXISTS(
259 SELECT 1 FROM account_delegations
260 WHERE controller_did = $1 AND revoked_at IS NULL
261 ) as "exists!""#,
262 did
263 )
264 .fetch_one(pool)
265 .await?;
266
267 Ok(exists)
268}