scufflecloud_core/operations/
mod.rs1use diesel_async::AsyncConnection;
2use diesel_async::scoped_futures::ScopedFutureExt;
3
4use crate::CoreConfig;
5use crate::cedar::{self, Action, CedarEntity};
6use crate::common::TxError;
7use crate::http_ext::RequestExt;
8use crate::std_ext::ResultExt;
9
10pub(crate) mod login;
11pub(crate) mod organization_invitations;
12pub(crate) mod organizations;
13pub(crate) mod register;
14pub(crate) mod user_session_requests;
15pub(crate) mod user_sessions;
16pub(crate) mod users;
17
18pub(crate) trait Operation<G: CoreConfig>: RequestExt + Sized + Send {
29 type Principal: CedarEntity<G> + Send + Sync;
31 type Resource: CedarEntity<G> + Send + Sync;
33 type Response: Send;
35
36 const TRANSACTION: bool = true;
37 const ACTION: Action;
39
40 async fn validate(&mut self) -> Result<(), tonic::Status> {
44 Ok(())
45 }
46
47 fn load_principal(
48 &mut self,
49 conn: &mut diesel_async::AsyncPgConnection,
50 ) -> impl Future<Output = Result<Self::Principal, tonic::Status>> + Send;
51
52 fn load_resource(
53 &mut self,
54 conn: &mut diesel_async::AsyncPgConnection,
55 ) -> impl Future<Output = Result<Self::Resource, tonic::Status>> + Send;
56
57 fn execute(
58 self,
59 conn: &mut diesel_async::AsyncPgConnection,
60 principal: Self::Principal,
61 resource: Self::Resource,
62 ) -> impl Future<Output = Result<Self::Response, tonic::Status>> + Send;
63
64 async fn run(mut self) -> Result<Self::Response, tonic::Status> {
65 let global = &self.global::<G>()?;
66
67 self.validate().await?;
68
69 let mut db = global.db().await.into_tonic_internal_err("failed to connect to database")?;
70
71 if Self::TRANSACTION {
72 let resp = db
73 .transaction::<_, TxError, _>(move |tx| {
74 async move {
75 let principal = self.load_principal(tx).await?;
76 let resource = self.load_resource(tx).await?;
77
78 cedar::is_authorized(global, self.session(), &principal, Self::ACTION, &resource).await?;
79
80 self.execute(tx, principal, resource).await.map_err(Into::into)
81 }
82 .scope_boxed()
83 })
84 .await?;
85
86 Ok(resp)
87 } else {
88 let principal = self.load_principal(&mut db).await?;
89 let resource = self.load_resource(&mut db).await?;
90
91 cedar::is_authorized(global, self.session(), &principal, Self::ACTION, &resource).await?;
92
93 self.execute(&mut db, principal, resource).await
94 }
95 }
96}