headless_lms_models/
oauth_user_client_scopes.rs1use crate::prelude::*;
2use chrono::{DateTime, Utc};
3use sqlx::FromRow;
4use uuid::Uuid;
5
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, FromRow)]
7pub struct OAuthUserClientScopes {
8 pub user_id: Uuid,
9 pub client_id: Uuid,
10 pub scopes: Vec<String>,
11 pub granted_at: DateTime<Utc>,
12}
13
14#[cfg_attr(feature = "ts_rs", derive(TS))]
15#[derive(Debug, Clone, PartialEq, FromRow, Serialize, Deserialize)]
16pub struct AuthorizedClientInfo {
17 pub client_id: Uuid, pub client_name: String, pub scopes: Vec<String>,
20}
21
22impl OAuthUserClientScopes {
23 pub async fn insert(
24 conn: &mut PgConnection,
25 user_id: Uuid,
26 client_id: Uuid,
27 scopes: &[String],
28 ) -> ModelResult<()> {
29 let mut tx = conn.begin().await?;
30 sqlx::query!(
31 r#"
32 INSERT INTO oauth_user_client_scopes
33 (user_id, client_id, scopes)
34 VALUES ($1, $2, $3)
35 ON CONFLICT (user_id, client_id) DO UPDATE
36 SET scopes = EXCLUDED.scopes,
37 granted_at = NOW()
38 "#,
39 user_id,
40 client_id,
41 scopes
42 )
43 .execute(&mut *tx)
44 .await?;
45 tx.commit().await?;
46 Ok(())
47 }
48
49 pub async fn find_scopes(
50 conn: &mut PgConnection,
51 user_id: Uuid,
52 client_id: Uuid,
53 ) -> ModelResult<Vec<String>> {
54 let mut tx = conn.begin().await?;
55 let rows = sqlx::query!(
56 r#"
57 SELECT scopes
58 FROM oauth_user_client_scopes
59 WHERE user_id = $1 AND client_id = $2
60 "#,
61 user_id,
62 client_id
63 )
64 .fetch_all(&mut *tx)
65 .await?;
66 tx.commit().await?;
67 Ok(rows.into_iter().flat_map(|r| r.scopes).collect())
68 }
69
70 pub async fn find_distinct_clients(
71 conn: &mut PgConnection,
72 user_id: Uuid,
73 ) -> ModelResult<Vec<Uuid>> {
74 let mut tx = conn.begin().await?;
75 let rows = sqlx::query!(
76 r#"
77 SELECT DISTINCT client_id
78 FROM oauth_user_client_scopes
79 WHERE user_id = $1
80 "#,
81 user_id
82 )
83 .fetch_all(&mut *tx)
84 .await?;
85 tx.commit().await?;
86 Ok(rows.into_iter().map(|r| r.client_id).collect())
87 }
88
89 pub async fn delete_all_for_user_client(
90 conn: &mut PgConnection,
91 user_id: Uuid,
92 client_id: Uuid,
93 ) -> ModelResult<()> {
94 let mut tx = conn.begin().await?;
95 sqlx::query!(
96 r#"DELETE FROM oauth_user_client_scopes WHERE user_id = $1 AND client_id = $2"#,
97 user_id,
98 client_id
99 )
100 .execute(&mut *tx)
101 .await?;
102 tx.commit().await?;
103 Ok(())
104 }
105
106 pub async fn list_authorized_clients_for_user(
107 conn: &mut PgConnection,
108 user_id: Uuid,
109 ) -> ModelResult<Vec<AuthorizedClientInfo>> {
110 let mut tx = conn.begin().await?;
111 let rows = sqlx::query_as!(
114 AuthorizedClientInfo,
115 r#"
116 SELECT
117 c.id AS client_id,
118 c.client_id AS client_name,
119 COALESCE(
120 array_agg(DISTINCT s.scope ORDER BY s.scope) FILTER (WHERE s.scope IS NOT NULL),
121 '{}'::text[]
122 ) AS "scopes!: Vec<String>"
123 FROM oauth_user_client_scopes ucs
124 JOIN oauth_clients c ON c.id = ucs.client_id
125 LEFT JOIN LATERAL unnest(ucs.scopes) AS s(scope) ON TRUE
126 WHERE ucs.user_id = $1
127 GROUP BY c.id, c.client_id
128 ORDER BY c.client_id
129 "#,
130 user_id
131 )
132 .fetch_all(&mut *tx)
133 .await?;
134
135 tx.commit().await?;
136 Ok(rows)
137 }
138
139 pub async fn revoke_user_client_everything(
141 conn: &mut PgConnection,
142 user_id: Uuid,
143 client_id: Uuid,
144 ) -> ModelResult<()> {
145 let mut tx = conn.begin().await?;
146
147 sqlx::query!(
148 r#"DELETE FROM oauth_user_client_scopes WHERE user_id = $1 AND client_id = $2"#,
149 user_id,
150 client_id
151 )
152 .execute(&mut *tx)
153 .await?;
154
155 sqlx::query!(
156 r#"DELETE FROM oauth_access_tokens WHERE user_id = $1 AND client_id = $2"#,
157 user_id,
158 client_id
159 )
160 .execute(&mut *tx)
161 .await?;
162
163 sqlx::query!(
164 r#"UPDATE oauth_refresh_tokens SET revoked = true WHERE user_id = $1 AND client_id = $2"#,
165 user_id,
166 client_id
167 )
168 .execute(&mut *tx)
169 .await?;
170
171 sqlx::query!(
172 r#"DELETE FROM oauth_auth_codes WHERE user_id = $1 AND client_id = $2"#,
173 user_id,
174 client_id
175 )
176 .execute(&mut *tx)
177 .await?;
178
179 tx.commit().await?;
180 Ok(())
181 }
182}