1use crate::{
2 admin::ProjectAuthAdmin,
3 pg::{PGConnection, StoreError},
4 types::scope::{CreateScope, Scope, ScopeKey, ScopeSearchClaims, UpdateScope},
5};
6use haste_fhir_operation_error::OperationOutcomeError;
7use haste_jwt::{ProjectId, TenantId, scopes::Scopes};
8use sqlx::{Acquire, Postgres, QueryBuilder};
9
10fn create_scope<'a, 'c, Connection: Acquire<'c, Database = Postgres> + Send + 'a>(
11 connection: Connection,
12 tenant: &'a TenantId,
13 project: &'a ProjectId,
14 scope: CreateScope,
15) -> impl Future<Output = Result<Scope, OperationOutcomeError>> + Send + 'a {
16 async move {
17 let mut conn = connection.acquire().await.map_err(StoreError::SQLXError)?;
18 let scope = sqlx::query_as!(
19 Scope,
20 r#"INSERT INTO authorization_scopes(tenant, project, client, user_, scope) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (tenant, project, client, user_) DO UPDATE set scope = $5 RETURNING client, user_, scope, created_at"#,
21 tenant.as_ref(),
22 project.as_ref(),
23 &scope.client.as_ref(),
24 &scope.user_.as_ref(),
25 &scope.scope as &Scopes,
26 ).fetch_one(&mut *conn).await.map_err(StoreError::SQLXError)?;
27
28 Ok(scope)
29 }
30}
31
32fn update_scope<'a, 'c, Connection: Acquire<'c, Database = Postgres> + Send + 'a>(
33 connection: Connection,
34 tenant: &'a TenantId,
35 project: &'a ProjectId,
36 model: UpdateScope,
37) -> impl Future<Output = Result<Scope, OperationOutcomeError>> + Send + 'a {
38 async move {
39 let mut conn = connection.acquire().await.map_err(StoreError::SQLXError)?;
40 let mut query_builder = QueryBuilder::new(
41 r#"
42 UPDATE authorization_scopes SET
43 "#,
44 );
45
46 let mut set_statements = query_builder.separated(", ");
47
48 set_statements
49 .push(" scope = ")
50 .push_bind_unseparated(model.scope);
51
52 query_builder.push(" WHERE ");
53
54 let mut where_statements = query_builder.separated(" AND ");
55 where_statements
56 .push(" tenant = ")
57 .push_bind_unseparated(tenant.as_ref())
58 .push(" project = ")
59 .push_bind_unseparated(project.as_ref())
60 .push(" client = ")
61 .push_bind_unseparated(model.client.as_ref())
62 .push(" user = ")
63 .push_bind_unseparated(model.user_.as_ref());
64
65 query_builder.push(r#" RETURNING client, user_ , scope, created_at"#);
66
67 let query = query_builder.build_query_as();
68
69 let scope = query
70 .fetch_one(&mut *conn)
71 .await
72 .map_err(StoreError::SQLXError)?;
73
74 Ok(scope)
75 }
76}
77
78fn read_scope<'a, 'c, Connection: Acquire<'c, Database = Postgres> + Send + 'a>(
79 connection: Connection,
80 tenant: &'a TenantId,
81 project: &'a ProjectId,
82 id: &'a ScopeKey,
83) -> impl Future<Output = Result<Option<Scope>, OperationOutcomeError>> + Send + 'a {
84 async move {
85 let mut conn = connection.acquire().await.map_err(StoreError::SQLXError)?;
86 let scope = sqlx::query_as!(
87 Scope,
88 r#"
89 SELECT user_, client, scope, created_at
90 FROM authorization_scopes
91 WHERE tenant = $1 AND project = $2 AND client = $3 and user_ = $4
92 "#,
93 tenant.as_ref(),
94 project.as_ref(),
95 String::from(id.0.clone()),
96 String::from(id.1.clone()),
97 )
98 .fetch_optional(&mut *conn)
99 .await
100 .map_err(StoreError::SQLXError)?;
101
102 Ok(scope)
103 }
104}
105
106fn delete_scope<'a, 'c, Connection: Acquire<'c, Database = Postgres> + Send + 'a>(
107 connection: Connection,
108 tenant: &'a TenantId,
109 project: &'a ProjectId,
110 key: &'a ScopeKey,
111) -> impl Future<Output = Result<(), OperationOutcomeError>> + Send + 'a {
112 async move {
113 let mut conn = connection.acquire().await.map_err(StoreError::SQLXError)?;
114 let _scope = sqlx::query_as!(
115 Scope,
116 r#"
117 DELETE FROM authorization_scopes
118 WHERE tenant = $1 AND project = $2 AND client = $3 AND user_ = $4
119 RETURNING user_, client, scope, created_at
120 "#,
121 tenant.as_ref(),
122 project.as_ref(),
123 key.0.as_ref(),
124 key.1.as_ref(),
125 )
126 .fetch_optional(&mut *conn)
127 .await
128 .map_err(StoreError::SQLXError)?;
129
130 Ok(())
131 }
132}
133
134fn search_scopes<'a, 'c, Connection: Acquire<'c, Database = Postgres> + Send + 'a>(
135 connection: Connection,
136 tenant: &'a TenantId,
137 project: &'a ProjectId,
138 clauses: &'a ScopeSearchClaims,
139) -> impl Future<Output = Result<Vec<Scope>, OperationOutcomeError>> + Send + 'a {
140 async move {
141 let mut conn = connection.acquire().await.map_err(StoreError::SQLXError)?;
142
143 let mut query_builder: QueryBuilder<Postgres> = QueryBuilder::new(
144 r#"SELECT user_, client, scope, created_at FROM authorization_scopes WHERE "#,
145 );
146
147 let mut seperator = query_builder.separated(" AND ");
148 seperator
149 .push(" tenant = ")
150 .push_bind_unseparated(tenant.as_ref())
151 .push(" project = ")
152 .push_bind_unseparated(project.as_ref());
153
154 if let Some(user_id) = clauses.user_.as_ref() {
155 seperator
156 .push(" user_ = ")
157 .push_bind_unseparated(user_id.as_ref());
158 }
159
160 if let Some(client) = clauses.client.as_ref() {
161 seperator
162 .push(" client = ")
163 .push_bind_unseparated(client.as_ref());
164 }
165
166 let query = query_builder.build_query_as();
167
168 let scopes: Vec<Scope> = query
169 .fetch_all(&mut *conn)
170 .await
171 .map_err(StoreError::from)?;
172
173 Ok(scopes)
174 }
175}
176
177impl ProjectAuthAdmin<CreateScope, Scope, ScopeSearchClaims, UpdateScope, ScopeKey>
178 for PGConnection
179{
180 async fn create(
181 &self,
182 tenant: &TenantId,
183 project: &ProjectId,
184 new_scope: CreateScope,
185 ) -> Result<Scope, OperationOutcomeError> {
186 match self {
187 PGConnection::Pool(pool, _) => {
188 let res = create_scope(pool, tenant, project, new_scope).await?;
189 Ok(res)
190 }
191 PGConnection::Transaction(tx, _) => {
192 let mut tx = tx.lock().await;
193 let res = create_scope(&mut *tx, tenant, project, new_scope).await?;
194 Ok(res)
195 }
196 }
197 }
198
199 async fn read(
200 &self,
201 tenant: &TenantId,
202 project: &ProjectId,
203 key: &ScopeKey,
204 ) -> Result<Option<Scope>, OperationOutcomeError> {
205 match self {
206 PGConnection::Pool(pool, _) => {
207 let res = read_scope(pool, tenant, project, key).await?;
208 Ok(res)
209 }
210 PGConnection::Transaction(tx, _) => {
211 let mut tx = tx.lock().await;
212 let res = read_scope(&mut *tx, tenant, project, key).await?;
213 Ok(res)
214 }
215 }
216 }
217
218 async fn update(
219 &self,
220 tenant: &TenantId,
221 project: &ProjectId,
222 model: UpdateScope,
223 ) -> Result<Scope, OperationOutcomeError> {
224 match self {
225 PGConnection::Pool(pool, _) => {
226 let res = update_scope(pool, tenant, project, model).await?;
227 Ok(res)
228 }
229 PGConnection::Transaction(tx, _) => {
230 let mut tx = tx.lock().await;
231 let res = update_scope(&mut *tx, tenant, project, model).await?;
232 Ok(res)
233 }
234 }
235 }
236
237 async fn delete(
238 &self,
239 tenant: &TenantId,
240 project: &ProjectId,
241 key: &ScopeKey,
242 ) -> Result<(), OperationOutcomeError> {
243 match self {
244 PGConnection::Pool(pool, _) => {
245 let res = delete_scope(pool, tenant, project, key).await?;
246 Ok(res)
247 }
248 PGConnection::Transaction(tx, _) => {
249 let mut tx = tx.lock().await;
250 let res = delete_scope(&mut *tx, tenant, project, key).await?;
251 Ok(res)
252 }
253 }
254 }
255
256 async fn search(
257 &self,
258 tenant: &TenantId,
259 project: &ProjectId,
260 clauses: &ScopeSearchClaims,
261 ) -> Result<Vec<Scope>, OperationOutcomeError> {
262 match self {
263 PGConnection::Pool(pool, _) => {
264 let res = search_scopes(pool, tenant, project, clauses).await?;
265 Ok(res)
266 }
267 PGConnection::Transaction(tx, _) => {
268 let mut tx = tx.lock().await;
269 let res = search_scopes(&mut *tx, tenant, project, clauses).await?;
270 Ok(res)
271 }
272 }
273 }
274}