alibi_plugins/sso/
mutation_guard.rs1use alibi_core::{AuthResult, AuthSchema, AuthTransaction};
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::{any::Any, sync::Arc};
6#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
7#[serde(rename_all = "camelCase")]
8pub enum SsoMutationAction {
9 Update,
10 Delete,
11}
12#[derive(Clone, Debug, Serialize, Deserialize)]
14#[serde(rename_all = "camelCase")]
15pub struct SsoMutationProvider {
16 pub id: String,
17 pub provider_id: String,
18 pub organization_id: Option<String>,
19}
20#[derive(Clone, Debug, Serialize, Deserialize)]
22#[serde(rename_all = "camelCase")]
23pub struct SsoProviderMutation {
24 pub action: SsoMutationAction,
25 #[serde(skip_serializing_if = "Option::is_none")]
26 pub is_authentication_boundary_change: Option<bool>,
27 pub provider: SsoMutationProvider,
28 pub provider_reference: Value,
29}
30#[async_trait::async_trait]
31pub trait SsoProviderMutationGuard<S: AuthSchema>: Send + Sync {
32 async fn guard(
34 &self,
35 mutation: SsoProviderMutation,
36 database: &dyn AuthTransaction<S>,
37 ) -> AuthResult<()>;
38}
39#[derive(Clone)]
41pub struct SsoMutationGuard {
42 callback: Arc<dyn Any + Send + Sync>,
43}
44impl SsoMutationGuard {
45 pub fn new<S: AuthSchema>(callback: Arc<dyn SsoProviderMutationGuard<S>>) -> Self {
46 Self {
47 callback: Arc::new(callback),
48 }
49 }
50 pub(super) fn resolve<S: AuthSchema>(
51 &self,
52 ) -> AuthResult<Arc<dyn SsoProviderMutationGuard<S>>> {
53 self.callback
54 .downcast_ref::<Arc<dyn SsoProviderMutationGuard<S>>>()
55 .cloned()
56 .ok_or_else(|| {
57 alibi_core::AuthError::config(
58 "SSO mutation guard schema does not match the application schema",
59 )
60 })
61 }
62}
63impl std::fmt::Debug for SsoMutationGuard {
64 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 formatter
66 .debug_struct("SsoMutationGuard")
67 .finish_non_exhaustive()
68 }
69}
70
71pub(super) async fn run<S: AuthSchema>(
72 guard: &dyn SsoProviderMutationGuard<S>,
73 provider: &alibi_core::store::SsoProvider,
74 action: SsoMutationAction,
75 boundary: Option<bool>,
76 verified: bool,
77 database: &dyn AuthTransaction<S>,
78) -> AuthResult<()> {
79 let mutation = SsoProviderMutation {
80 action,
81 is_authentication_boundary_change: boundary,
82 provider: SsoMutationProvider {
83 id: provider.id.clone(),
84 provider_id: provider.provider_id.clone(),
85 organization_id: provider.organization_id.clone(),
86 },
87 provider_reference: super::sign_in::mutation_provider_reference(provider, verified)?,
88 };
89 guard.guard(mutation, database).await.map_err(|_| {
90 tracing::error!("SSO provider mutation guard rejected the mutation");
91 alibi_core::AuthError::Api {
92 status: 409,
93 code: Some("SSO_PROVIDER_MUTATION_REJECTED".into()),
94 message: "SSO provider mutation is not allowed".into(),
95 }
96 })
97}