haste_repository/pg/
scope.rs

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}