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