Skip to main content

alibi_core/plugin/
initialization.rs

1use crate::plugin::MetadataMap;
2use crate::{
3    AuthConfig, AuthContext, AuthResult, AuthSchema, AuthStore, ContextExtensions, EmailProvider,
4    VerificationEmailOverride, VerificationEmailOverrideHandle,
5};
6use std::sync::Arc;
7pub struct AuthInitParts {
8    pub metadata: MetadataMap,
9    pub email_provider: Option<Arc<dyn EmailProvider>>,
10    pub extensions: ContextExtensions,
11}
12
13/// Initialization context passed to plugin setup.
14pub struct AuthInitContext<S: AuthSchema> {
15    pub config: Arc<AuthConfig>,
16    pub database: Arc<dyn AuthStore<S>>,
17    pub email_provider: Option<Arc<dyn EmailProvider>>,
18    pub metadata: MetadataMap,
19    pub extensions: ContextExtensions,
20}
21
22impl<S: AuthSchema> AuthInitContext<S> {
23    #[must_use]
24    pub fn new(config: Arc<AuthConfig>, database: Arc<dyn AuthStore<S>>) -> Self {
25        let email_provider = config.email_provider.clone();
26        Self {
27            config,
28            database,
29            email_provider,
30            metadata: MetadataMap::new(),
31            extensions: ContextExtensions::default(),
32        }
33    }
34
35    pub fn set_metadata(&mut self, key: impl Into<String>, value: serde_json::Value) {
36        _ = self.metadata.insert(key.into(), value);
37    }
38
39    /// Decorate the instance's actual password hashing boundary. Later
40    /// registrations run first, matching initialized hash wrappers.
41    pub fn register_password_hash_hook(
42        &mut self,
43        hook: Arc<dyn crate::utils::password::PasswordHashHook>,
44    ) {
45        let mut hooks = self
46            .extensions
47            .cloned_or_default::<crate::utils::password::PasswordHashHooks>();
48        hooks.0.push(hook);
49        self.extensions.insert(hooks);
50    }
51
52    #[must_use]
53    pub fn get_metadata(&self, key: &str) -> Option<&serde_json::Value> {
54        self.metadata.get(key)
55    }
56
57    /// Register a transform for user creation, including transactional creation.
58    pub fn register_user_create_transform<F>(&mut self, transform: F)
59    where
60        F: Fn(crate::types::CreateUser) -> AuthResult<crate::types::CreateUser>
61            + Send
62            + Sync
63            + 'static,
64    {
65        let mut transforms = self
66            .extensions
67            .cloned_or_default::<crate::store::UserTransforms>();
68        transforms.creates.push(Arc::new(transform));
69        self.extensions.insert(transforms);
70    }
71
72    /// Register model defaults applied after creation hooks to an identity
73    /// candidate admitted by the configured user validation policy.
74    pub fn register_user_creation_adapter_default<F>(&mut self, default: F)
75    where
76        F: Fn(crate::types::CreateUser) -> AuthResult<crate::types::CreateUser>
77            + Send
78            + Sync
79            + 'static,
80    {
81        let mut transforms = self
82            .extensions
83            .cloned_or_default::<crate::store::UserTransforms>();
84        transforms.adapter_defaults.0.push(Arc::new(default));
85        self.extensions.insert(transforms);
86    }
87
88    /// Register a transform applied to every user update through this auth instance.
89    pub fn register_user_update_transform<F>(&mut self, transform: F)
90    where
91        F: Fn(&str, crate::types::UpdateUser) -> AuthResult<crate::types::UpdateUser>
92            + Send
93            + Sync
94            + 'static,
95    {
96        let mut transforms = self
97            .extensions
98            .cloned_or_default::<crate::store::UserTransforms>();
99        transforms.updates.push(Arc::new(transform));
100        self.extensions.insert(transforms);
101    }
102
103    /// Register an adapter lifecycle callback for each committed session creation.
104    /// Transactional creations defer callbacks until commit and discard them on
105    /// rollback. A callback error propagates after persistence, as for other
106    /// adapter after callbacks. Trusted server operations have no request context.
107    pub fn register_session_created_hook(
108        &mut self,
109        callback: Arc<dyn crate::store::SessionCreatedHook<S>>,
110    ) {
111        let mut callbacks = self
112            .extensions
113            .cloned_or_default::<crate::store::SessionCreatedCallbacks<S>>();
114        callbacks.callbacks.push(callback);
115        self.extensions.insert(callbacks);
116    }
117
118    /// Observe genuine retained adapter output after a write, with transaction
119    /// callbacks deferred until commit. Existing typed physical hooks are separate.
120    pub fn register_adapter_after_hook(
121        &mut self,
122        callback: Arc<dyn crate::store::AdapterAfterHook<S>>,
123    ) {
124        let mut callbacks = self
125            .extensions
126            .cloned_or_default::<crate::store::AdapterCallbacks<S>>();
127        callbacks.0.push(callback);
128        self.extensions.insert(callbacks);
129    }
130
131    /// Register instance-local dependant work around successful session deletion.
132    pub fn register_session_deletion_hook(
133        &mut self,
134        hook: Arc<dyn crate::store::SessionDeletionHook<S>>,
135    ) {
136        let mut hooks = self
137            .extensions
138            .cloned_or_default::<crate::store::SessionDeletionHooks<S>>();
139        hooks.0.push(hook);
140        self.extensions.insert(hooks);
141    }
142    /// Finalize the instance's store without mutating a shared underlying adapter.
143    #[must_use]
144    pub fn database_with_registered_transforms(&self) -> Arc<dyn AuthStore<S>> {
145        let transforms = self
146            .extensions
147            .cloned_or_default::<crate::store::UserTransforms>();
148        let session_callbacks = self
149            .extensions
150            .cloned_or_default::<crate::store::SessionCreatedCallbacks<S>>();
151        let fields = self.extensions.get::<crate::field_policy::SessionFields>();
152        if self.config.session.secondary_storage.is_none()
153            && transforms.creates.is_empty()
154            && transforms.updates.is_empty()
155            && session_callbacks.callbacks.is_empty()
156            && fields.is_none()
157            && self.config.user.additional_fields.is_empty()
158            && self.config.session.additional_fields.is_empty()
159            && self.config.account.additional_fields.is_empty()
160            && self
161                .extensions
162                .get::<crate::store::AdapterCallbacks<S>>()
163                .is_none_or(|callbacks| callbacks.0.is_empty())
164            && self
165                .extensions
166                .get::<crate::store::SessionDeletionHooks<S>>()
167                .is_none_or(|hooks| hooks.0.is_empty())
168            && self.config.user_validation.is_none()
169        {
170            return Arc::clone(&self.database);
171        }
172        Arc::new(crate::store::PluginStore::new(
173            Arc::clone(&self.database),
174            Arc::clone(&self.config),
175            transforms,
176            session_callbacks,
177            fields.map_or_else(
178                || {
179                    crate::field_policy::SessionFields(
180                        self.config.session.additional_fields.clone(),
181                    )
182                },
183                |fields| (*fields).clone(),
184            ),
185            self.extensions
186                .get::<crate::field_policy::SessionAdapterFields>()
187                .map_or_else(
188                    || {
189                        crate::field_policy::SessionAdapterFields(Arc::new(
190                            self.config.session.additional_fields.clone(),
191                        ))
192                    },
193                    |fields| (*fields).clone(),
194                ),
195            AuthContext::with_metadata(
196                Arc::clone(&self.config),
197                Arc::clone(&self.database),
198                self.metadata.clone(),
199            )
200            .with_extensions(self.extensions.clone()),
201        ))
202    }
203
204    pub fn set_email_verification_override(
205        &mut self,
206        sender: Arc<dyn VerificationEmailOverride<S>>,
207    ) {
208        self.extensions
209            .insert(VerificationEmailOverrideHandle(sender));
210    }
211
212    #[must_use]
213    pub fn into_parts(self) -> AuthInitParts {
214        AuthInitParts {
215            metadata: self.metadata,
216            email_provider: self.email_provider,
217            extensions: self.extensions,
218        }
219    }
220}
221
222impl std::fmt::Debug for AuthInitParts {
223    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
224        f.debug_struct("AuthInitParts").finish_non_exhaustive()
225    }
226}
227
228impl<S: AuthSchema> std::fmt::Debug for AuthInitContext<S> {
229    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
230        f.debug_struct("AuthInitContext").finish_non_exhaustive()
231    }
232}