kanidm_unix_resolver/
resolver.rs

1// use async_trait::async_trait;
2use crate::db::{Cache, Db};
3use crate::idprovider::interface::{
4    AuthCredHandler,
5    AuthResult,
6    GroupToken,
7    GroupTokenState,
8    Id,
9    IdProvider,
10    IdpError,
11    ProviderOrigin,
12    // KeyStore,
13    UserToken,
14    UserTokenState,
15};
16use crate::idprovider::system::{
17    Shadow, SystemAuthResult, SystemProvider, SystemProviderAuthInit, SystemProviderSession,
18};
19use hashbrown::HashMap;
20use kanidm_hsm_crypto::provider::BoxedDynTpm;
21use kanidm_unix_common::constants::{
22    DEFAULT_CACHE_TIMEOUT_JITTER_MS, DEFAULT_CACHE_TIMEOUT_MAXIMUM, DEFAULT_CACHE_TIMEOUT_MINIMUM,
23    DEFAULT_SHELL_SEARCH_PATHS, SYSTEM_SHADOW_PATH,
24};
25use kanidm_unix_common::unix_config::{HomeAttr, UidAttr};
26use kanidm_unix_common::unix_passwd::{EtcGroup, EtcShadow, EtcUser};
27use kanidm_unix_common::unix_proto::{
28    HomeDirectoryInfo, NssGroup, NssUser, PamAuthRequest, PamAuthResponse, PamServiceInfo,
29    ProviderStatus,
30};
31use lru::LruCache;
32use std::fmt::Display;
33use std::num::NonZeroUsize;
34use std::ops::DerefMut;
35use std::path::{Path, PathBuf};
36use std::string::ToString;
37use std::sync::Arc;
38use std::time::{Duration, SystemTime};
39use time::OffsetDateTime;
40use tokio::sync::broadcast;
41use tokio::sync::mpsc;
42use tokio::sync::Mutex;
43use uuid::Uuid;
44
45const NXCACHE_SIZE: NonZeroUsize =
46    NonZeroUsize::new(256).expect("Invalid NXCACHE constant at compile time");
47// Can sometimes have duplicates, so it can be a bit longer.
48const ASYNC_REFRESH_QUEUE: usize = 128;
49
50pub enum AuthSession {
51    Online {
52        client: Arc<dyn IdProvider + Sync + Send>,
53        account_id: String,
54        id: Id,
55        cred_handler: AuthCredHandler,
56        /// Some authentication operations may need to spawn background tasks. These tasks need
57        /// to know when to stop as the caller has disconnected. This receiver allows that, so
58        /// that tasks which .resubscribe() to this channel can then select! on it and be notified
59        /// when they need to stop.
60        shutdown_rx: broadcast::Receiver<()>,
61    },
62    Offline {
63        account_id: String,
64        id: Id,
65        client: Arc<dyn IdProvider + Sync + Send>,
66        session_token: Box<UserToken>,
67        cred_handler: AuthCredHandler,
68    },
69    System {
70        account_id: String,
71        id: Id,
72        cred_handler: AuthCredHandler,
73        shadow: Arc<Shadow>,
74    },
75    Success,
76    Denied,
77}
78
79impl AuthSession {
80    pub fn is_complete(&self) -> bool {
81        match &self {
82            Self::Success | Self::Denied => true,
83            Self::Online { .. } | Self::Offline { .. } | Self::System { .. } => false,
84        }
85    }
86}
87
88#[derive(Debug, PartialEq)]
89/// The expiration state of a cache item
90enum ExpiryState {
91    /// The item is valid
92    Valid,
93    /// The item is valid, but approaching it's expiry so a background/async refresh should be
94    /// performed.
95    ValidRefresh,
96    /// The item is expired and needs refresh before we can proceed
97    Expired,
98}
99
100pub struct Resolver {
101    // Generic / modular types.
102    db: Db,
103    hsm: Mutex<BoxedDynTpm>,
104
105    // A local passwd/shadow resolver.
106    system_provider: Arc<SystemProvider>,
107
108    // client: Box<dyn IdProvider + Sync + Send>,
109    client_ids: HashMap<ProviderOrigin, Arc<dyn IdProvider + Sync + Send>>,
110
111    // A set of remote resolvers, ordered by priority.
112    clients: Vec<Arc<dyn IdProvider + Sync + Send>>,
113
114    // The id of the primary-provider which may use name over spn.
115    primary_origin: ProviderOrigin,
116
117    timeout_seconds: u64,
118    async_refresh_seconds: u64,
119    default_shell: String,
120    home_prefix: PathBuf,
121    home_attr: HomeAttr,
122    home_alias: Option<HomeAttr>,
123    uid_attr_map: UidAttr,
124    gid_attr_map: UidAttr,
125    nxcache: Mutex<LruCache<Id, SystemTime>>,
126    async_refresh_tx: mpsc::Sender<Id>,
127}
128
129impl Display for Id {
130    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131        f.write_str(&match self {
132            Id::Name(s) => s.to_string(),
133            Id::Gid(g) => g.to_string(),
134        })
135    }
136}
137
138impl Resolver {
139    #[allow(clippy::too_many_arguments)]
140    pub async fn new(
141        db: Db,
142        system_provider: Arc<SystemProvider>,
143        clients: Vec<Arc<dyn IdProvider + Sync + Send>>,
144        hsm: BoxedDynTpm,
145        timeout_seconds: u64,
146        default_shell: String,
147        home_prefix: PathBuf,
148        home_attr: HomeAttr,
149        home_alias: Option<HomeAttr>,
150        uid_attr_map: UidAttr,
151        gid_attr_map: UidAttr,
152    ) -> Result<(Self, mpsc::Receiver<Id>), ()> {
153        let hsm = Mutex::new(hsm);
154
155        let primary_origin = clients.first().map(|c| c.origin()).unwrap_or_default();
156
157        let client_ids: HashMap<_, _> = clients
158            .iter()
159            .map(|provider| (provider.origin(), provider.clone()))
160            .collect();
161
162        let (async_refresh_tx, async_refresh_rx) = mpsc::channel(ASYNC_REFRESH_QUEUE);
163
164        // How many seconds before expiry should we be refreshing an entry.
165        // We want to balance this - we don't want too many refreshes, but we
166        // also need to account for long cache times and ensuring that we are checking
167        // account validity in the background.
168
169        let timeout_seconds =
170            timeout_seconds.clamp(DEFAULT_CACHE_TIMEOUT_MINIMUM, DEFAULT_CACHE_TIMEOUT_MAXIMUM);
171        let async_refresh_seconds = (timeout_seconds / 3) * 2;
172
173        // We assume we are offline at start up, and we mark the next "online check" as
174        // being valid from "now".
175        Ok((
176            Resolver {
177                db,
178                hsm,
179                system_provider,
180                clients,
181                primary_origin,
182                client_ids,
183                timeout_seconds,
184                async_refresh_seconds,
185                default_shell,
186                home_prefix,
187                home_attr,
188                home_alias,
189                uid_attr_map,
190                gid_attr_map,
191                nxcache: Mutex::new(LruCache::new(NXCACHE_SIZE)),
192                async_refresh_tx,
193            },
194            async_refresh_rx,
195        ))
196    }
197
198    #[instrument(level = "debug", skip_all)]
199    pub async fn mark_next_check_now(&self, now: SystemTime) {
200        for c in self.clients.iter() {
201            c.mark_next_check(now).await;
202        }
203    }
204
205    #[instrument(level = "debug", skip_all)]
206    pub async fn mark_offline(&self) {
207        for c in self.clients.iter() {
208            c.mark_offline().await;
209        }
210    }
211
212    #[instrument(level = "debug", skip_all)]
213    pub async fn clear_cache(&self) -> Result<(), ()> {
214        let mut dbtxn = self.db.write().await;
215        let mut nxcache_txn = self.nxcache.lock().await;
216        nxcache_txn.clear();
217        dbtxn.clear().and_then(|_| dbtxn.commit()).map_err(|_| ())
218    }
219
220    #[instrument(level = "debug", skip_all)]
221    pub async fn invalidate(&self) -> Result<(), ()> {
222        let mut dbtxn = self.db.write().await;
223        let mut nxcache_txn = self.nxcache.lock().await;
224        nxcache_txn.clear();
225        dbtxn
226            .invalidate()
227            .and_then(|_| dbtxn.commit())
228            .map_err(|_| ())
229    }
230
231    #[instrument(level = "debug", skip_all)]
232    async fn get_cached_usertokens(&self) -> Result<Vec<UserToken>, ()> {
233        let mut dbtxn = self.db.write().await;
234        dbtxn.get_accounts().map_err(|_| ())
235    }
236
237    #[instrument(level = "debug", skip_all)]
238    async fn get_cached_grouptokens(&self) -> Result<Vec<GroupToken>, ()> {
239        let mut dbtxn = self.db.write().await;
240        dbtxn.get_groups().map_err(|_| ())
241    }
242
243    #[instrument(level = "debug", skip_all)]
244    async fn set_nxcache(&self, id: &Id) {
245        let mut nxcache_txn = self.nxcache.lock().await;
246        // To try and prevent too many requests occuring all at the same time, we subtract a small
247        // amount of "jitter" from expiry values so that we space out refreshes.
248        let jitter = rand::random_range(0..DEFAULT_CACHE_TIMEOUT_JITTER_MS);
249        let ex_time = SystemTime::now() + Duration::from_secs(self.timeout_seconds)
250            - Duration::from_millis(jitter);
251        nxcache_txn.put(id.clone(), ex_time);
252    }
253
254    #[instrument(level = "debug", skip_all)]
255    pub async fn check_nxcache(&self, id: &Id) -> Option<SystemTime> {
256        let mut nxcache_txn = self.nxcache.lock().await;
257        nxcache_txn.get(id).copied()
258    }
259
260    #[instrument(level = "info", skip_all)]
261    pub async fn reload_system_identities(
262        &self,
263        users: Vec<EtcUser>,
264        shadow: Vec<EtcShadow>,
265        groups: Vec<EtcGroup>,
266    ) {
267        self.system_provider.reload(users, shadow, groups).await
268    }
269
270    #[instrument(level = "debug", skip_all)]
271    async fn get_cached_usertoken(
272        &self,
273        account_id: &Id,
274        current_time: SystemTime,
275    ) -> Result<(ExpiryState, Option<UserToken>), ()> {
276        // Is it in the nxcache?
277        if let Some(ex_time) = self.check_nxcache(account_id).await {
278            if current_time >= ex_time {
279                // It's in the LRU, but we are past the expiry so
280                // lets attempt a refresh.
281                return Ok((ExpiryState::ValidRefresh, None));
282            } else {
283                // It's in the LRU and still valid, so return that
284                // no check is needed.
285                return Ok((ExpiryState::Valid, None));
286            }
287        }
288
289        // Account_id could be:
290        //  * gidnumber
291        //  * name
292        //  * spn
293        //  * uuid
294        //  Attempt to search these in the db.
295        let mut dbtxn = self.db.write().await;
296        let r = dbtxn.get_account(account_id).map_err(|err| {
297            debug!(?err, "get_cached_usertoken");
298        })?;
299
300        drop(dbtxn);
301
302        match r {
303            Some((ut, ex)) => {
304                // Are we expired?
305                let ex_time = SystemTime::UNIX_EPOCH + Duration::from_secs(ex);
306
307                // Should we async refresh?
308                let async_ref_time = if ex > self.async_refresh_seconds {
309                    SystemTime::UNIX_EPOCH + Duration::from_secs(ex - self.async_refresh_seconds)
310                } else {
311                    ex_time
312                };
313
314                if current_time >= ex_time {
315                    Ok((ExpiryState::Expired, Some(ut)))
316                } else if current_time >= async_ref_time {
317                    Ok((ExpiryState::ValidRefresh, Some(ut)))
318                } else {
319                    Ok((ExpiryState::Valid, Some(ut)))
320                }
321            }
322            None => {
323                // it wasn't in the DB, and it wasn't in the nxcache.
324                Ok((ExpiryState::Expired, None))
325            }
326        } // end match r
327    }
328
329    #[instrument(level = "debug", skip_all)]
330    async fn get_cached_grouptoken(&self, grp_id: &Id) -> Result<(bool, Option<GroupToken>), ()> {
331        let current_time = SystemTime::now();
332        // Is it in the nxcache?
333        if let Some(ex_time) = self.check_nxcache(grp_id).await {
334            if current_time >= ex_time {
335                // It's in the LRU, but we are past the expiry so
336                // lets attempt a refresh.
337                return Ok((true, None));
338            } else {
339                // It's in the LRU and still valid, so return that
340                // no check is needed.
341                return Ok((false, None));
342            }
343        }
344        // grp_id could be:
345        //  * gidnumber
346        //  * name
347        //  * spn
348        //  * uuid
349        //  Attempt to search these in the db.
350        let mut dbtxn = self.db.write().await;
351        let r = dbtxn.get_group(grp_id).map_err(|err| {
352            debug!(?err, "get_cached_grouptoken");
353        })?;
354
355        drop(dbtxn);
356
357        match r {
358            Some((ut, ex)) => {
359                // Are we expired?
360                let offset = Duration::from_secs(ex);
361                let ex_time = SystemTime::UNIX_EPOCH + offset;
362
363                if current_time >= ex_time {
364                    Ok((true, Some(ut)))
365                } else {
366                    Ok((false, Some(ut)))
367                }
368            }
369            None => {
370                // it wasn't in the DB, and it wasn't in the nxcache.
371                Ok((true, None))
372            }
373        }
374    }
375
376    #[instrument(level = "debug", skip_all)]
377    async fn set_cache_usertoken(
378        &self,
379        token: &mut UserToken,
380        // This is just for proof that only one write can occur at a time.
381        _tpm: &mut BoxedDynTpm,
382    ) -> Result<(), ()> {
383        // Set an expiry
384        // To try and prevent too many requests occuring all at the same time, we subtract a small
385        // amount of "jitter" from expiry values so that we space out refreshes.
386        let jitter = rand::random_range(0..DEFAULT_CACHE_TIMEOUT_JITTER_MS);
387        let ex_time = SystemTime::now() + Duration::from_secs(self.timeout_seconds)
388            - Duration::from_millis(jitter);
389        let offset = ex_time
390            .duration_since(SystemTime::UNIX_EPOCH)
391            .map_err(|e| {
392                error!(
393                    "Time conversion error - cache expiry time became less than epoch? {:?}",
394                    e
395                );
396            })?;
397
398        // Check if requested `shell` exists on the system, else use `default_shell`
399        let maybe_shell = token.shell.as_ref().map(PathBuf::from);
400
401        let requested_shell_exists = if let Some(shell_path) = maybe_shell.as_ref() {
402            // Does the shell path as configured exist?
403            let mut exists = shell_path
404                .canonicalize()
405                .map_err(|err| {
406                    debug!(
407                        "Failed to canonicalize path, using base path. Tried: {} Error: {:?}",
408                        shell_path.to_string_lossy(),
409                        err
410                    );
411                })
412                .unwrap_or(Path::new(shell_path).to_path_buf())
413                .exists();
414
415            if !exists {
416                // Does the shell binary exist in a search path that is configured?
417                if let Some(shell_binary_name) = shell_path.file_name() {
418                    for search_path in DEFAULT_SHELL_SEARCH_PATHS {
419                        //
420                        let shell_path = Path::new(search_path).join(shell_binary_name);
421                        if shell_path.exists() {
422                            // Okay, the binary name exists but in an alternate path. This can
423                            // commonly occur with freebsd where the shell may be installed
424                            // in /usr/local/bin instead of /bin.
425                            //
426                            // This could also occur if the user configured the shell as "zsh"
427                            // rather than an absolute path.
428                            let Some(shell_path_utf8) = shell_path.to_str().map(String::from)
429                            else {
430                                warn!("Configured shell \"{}\" for {} was found but the complete path is not valid utf-8 and can not be used.",
431                                        shell_binary_name.to_string_lossy(), token.name);
432                                continue;
433                            };
434
435                            // Update the path
436                            token.shell = Some(shell_path_utf8);
437                            // We exist
438                            exists = true;
439                            // No need to loop any more
440                            break;
441                        }
442                    }
443                }
444            }
445
446            if !exists {
447                warn!(
448                        "Configured shell \"{}\" for {} is not present on this system. Check `/etc/shells` for valid shell options.",
449                        shell_path.to_string_lossy(), token.name
450                    )
451            }
452
453            exists
454        } else {
455            info!("User has not specified a shell, using default");
456            false
457        };
458
459        if !requested_shell_exists {
460            token.shell = Some(self.default_shell.clone())
461        }
462
463        let mut dbtxn = self.db.write().await;
464        token
465            .groups
466            .iter()
467            // We need to add the groups first
468            .try_for_each(|g| dbtxn.update_group(g, offset.as_secs()))
469            .and_then(|_|
470                // So that when we add the account it can make the relationships.
471                dbtxn
472                    .update_account(token, offset.as_secs()))
473            .and_then(|_| dbtxn.commit())
474            .map_err(|_| ())
475    }
476
477    #[instrument(level = "debug", skip_all)]
478    async fn set_cache_grouptoken(&self, token: &GroupToken) -> Result<(), ()> {
479        // Set an expiry
480        let ex_time = SystemTime::now() + Duration::from_secs(self.timeout_seconds);
481        let offset = ex_time
482            .duration_since(SystemTime::UNIX_EPOCH)
483            .map_err(|e| {
484                error!("time conversion error - ex_time less than epoch? {:?}", e);
485            })?;
486
487        let mut dbtxn = self.db.write().await;
488        dbtxn
489            .update_group(token, offset.as_secs())
490            .and_then(|_| dbtxn.commit())
491            .map_err(|_| ())
492    }
493
494    #[instrument(level = "debug", skip_all)]
495    async fn delete_cache_usertoken(&self, a_uuid: Uuid) -> Result<(), ()> {
496        let mut dbtxn = self.db.write().await;
497        dbtxn
498            .delete_account(a_uuid)
499            .and_then(|_| dbtxn.commit())
500            .map_err(|_| ())
501    }
502
503    #[instrument(level = "debug", skip_all)]
504    async fn delete_cache_grouptoken(&self, g_uuid: Uuid) -> Result<(), ()> {
505        let mut dbtxn = self.db.write().await;
506        dbtxn
507            .delete_group(g_uuid)
508            .and_then(|_| dbtxn.commit())
509            .map_err(|_| ())
510    }
511
512    #[instrument(level = "debug", skip_all)]
513    pub async fn refresh_usertoken(
514        &self,
515        account_id: &Id,
516        current_time: SystemTime,
517    ) -> Result<Option<UserToken>, ()> {
518        let mut hsm_lock = self.hsm.lock().await;
519
520        // We need to re-acquire the token now behind the hsmlock - this is so that
521        // we know that as we write the updated token, we know that no one else has
522        // written to this token, since we are now the only task that is allowed
523        // to be in a write phase.
524        let token = self
525            .get_cached_usertoken(account_id, current_time)
526            .await
527            .map(|(_expired, option_token)| option_token)
528            .map_err(|err| {
529                debug!(?err, "get_usertoken error");
530            })?;
531
532        let user_get_result = if let Some(tok) = token.as_ref() {
533            // Re-use the provider that the token is from.
534            match self.client_ids.get(&tok.provider) {
535                Some(client) => {
536                    client
537                        .unix_user_get(
538                            account_id,
539                            token.as_ref(),
540                            hsm_lock.deref_mut(),
541                            current_time,
542                        )
543                        .await
544                }
545                None => {
546                    error!(provider = ?tok.provider, "Token was resolved by a provider that no longer appears to be present.");
547
548                    // We don't want to use a token from a former provider, we want it refreshed,
549                    // so lets indicate that we didn't find the token. If we return useCcahed like
550                    // we did previously, we'd never clear and reset this token since we'd never
551                    // locate it's provider.
552                    Ok(UserTokenState::NotFound)
553                }
554            }
555        } else {
556            // We've never seen it before, so iterate over the providers in priority order.
557            'search: {
558                for client in self.clients.iter() {
559                    match client
560                        .unix_user_get(
561                            account_id,
562                            token.as_ref(),
563                            hsm_lock.deref_mut(),
564                            current_time,
565                        )
566                        .await
567                    {
568                        // Ignore this one.
569                        Ok(UserTokenState::NotFound) => {}
570                        result => break 'search result,
571                    }
572                }
573                break 'search Ok(UserTokenState::NotFound);
574            }
575        };
576
577        match user_get_result {
578            Ok(UserTokenState::Update(mut n_tok)) => {
579                // We have the token!
580                self.set_cache_usertoken(&mut n_tok, hsm_lock.deref_mut())
581                    .await?;
582                Ok(Some(n_tok))
583            }
584            Ok(UserTokenState::NotFound) => {
585                // It previously existed, so now purge it.
586                if let Some(tok) = token {
587                    self.delete_cache_usertoken(tok.uuid).await?;
588                };
589                // Cache the NX here.
590                self.set_nxcache(account_id).await;
591                Ok(None)
592            }
593            Ok(UserTokenState::UseCached) => Ok(token),
594            Err(err) => {
595                // Something went wrong, we don't know what, but lets return the token
596                // anyway.
597                error!(?err);
598                Ok(token)
599            }
600        }
601    }
602
603    #[instrument(level = "debug", skip_all)]
604    async fn refresh_grouptoken(
605        &self,
606        grp_id: &Id,
607        token: Option<GroupToken>,
608        current_time: SystemTime,
609    ) -> Result<Option<GroupToken>, ()> {
610        let mut hsm_lock = self.hsm.lock().await;
611
612        let group_get_result = if let Some(tok) = token.as_ref() {
613            // Re-use the provider that the token is from.
614            match self.client_ids.get(&tok.provider) {
615                Some(client) => {
616                    client
617                        .unix_group_get(grp_id, hsm_lock.deref_mut(), current_time)
618                        .await
619                }
620                None => {
621                    error!(provider = ?tok.provider, "Token was resolved by a provider that no longer appears to be present.");
622                    // We don't want to use a token from a former provider, we want it refreshed,
623                    // so lets indicate that we didn't find the token. If we return useCcahed like
624                    // we did previously, we'd never clear and reset this token since we'd never
625                    // locate it's provider.
626                    Ok(GroupTokenState::NotFound)
627                }
628            }
629        } else {
630            // We've never seen it before, so iterate over the providers in priority order.
631            'search: {
632                for client in self.clients.iter() {
633                    match client
634                        .unix_group_get(grp_id, hsm_lock.deref_mut(), current_time)
635                        .await
636                    {
637                        // Ignore this one.
638                        Ok(GroupTokenState::NotFound) => {}
639                        result => break 'search result,
640                    }
641                }
642                break 'search Ok(GroupTokenState::NotFound);
643            }
644        };
645
646        drop(hsm_lock);
647
648        match group_get_result {
649            Ok(GroupTokenState::Update(n_tok)) => {
650                self.set_cache_grouptoken(&n_tok).await?;
651                Ok(Some(n_tok))
652            }
653            Ok(GroupTokenState::NotFound) => {
654                if let Some(tok) = token {
655                    self.delete_cache_grouptoken(tok.uuid).await?;
656                };
657                // Cache the NX here.
658                self.set_nxcache(grp_id).await;
659                Ok(None)
660            }
661            Ok(GroupTokenState::UseCached) => Ok(token),
662            Err(err) => {
663                // Some other transient error, continue with the token.
664                error!(?err);
665                Ok(token)
666            }
667        }
668    }
669
670    #[instrument(level = "trace", skip_all)]
671    async fn get_usertoken(
672        &self,
673        account_id: &Id,
674        current_time: SystemTime,
675    ) -> Result<Option<UserToken>, ()> {
676        // get the item from the cache
677        let (expiry_state, item) = self
678            .get_cached_usertoken(account_id, current_time)
679            .await
680            .map_err(|e| {
681                debug!("get_usertoken error -> {:?}", e);
682            })?;
683
684        // If the token isn't found, get_cached will set expired = true.
685        if expiry_state == ExpiryState::Expired {
686            self.refresh_usertoken(account_id, current_time).await
687        } else {
688            if expiry_state == ExpiryState::ValidRefresh {
689                // We don't mind if the buffer is full.
690                if self.async_refresh_tx.try_send(account_id.clone()).is_err() {
691                    debug!(?account_id, "unable to queue async refresh");
692                }
693            }
694            // Still valid, return the cached entry.
695            Ok(item)
696        }
697        .map(|t| {
698            trace!("token -> {:?}", t);
699            t
700        })
701    }
702
703    #[instrument(level = "trace", skip(self))]
704    async fn get_grouptoken(
705        &self,
706        grp_id: Id,
707        current_time: SystemTime,
708    ) -> Result<Option<GroupToken>, ()> {
709        let (expired, item) = self.get_cached_grouptoken(&grp_id).await.map_err(|e| {
710            debug!("get_grouptoken error -> {:?}", e);
711        })?;
712
713        if expired {
714            self.refresh_grouptoken(&grp_id, item, current_time).await
715        } else {
716            // Still valid, return the cached entry.
717            Ok(item)
718        }
719        .map(|t| {
720            trace!("token -> {:?}", t);
721            t
722        })
723    }
724
725    async fn get_groupmembers(&self, g_uuid: Uuid) -> Vec<String> {
726        let mut dbtxn = self.db.write().await;
727
728        dbtxn
729            .get_group_members(g_uuid)
730            .unwrap_or_else(|_| Vec::new())
731            .into_iter()
732            .map(|ut| self.token_uidattr(&ut))
733            .collect()
734    }
735
736    // Get ssh keys for an account id
737    #[instrument(level = "debug", skip(self))]
738    pub async fn get_sshkeys(&self, account_id: &str) -> Result<Vec<String>, ()> {
739        let current_time = SystemTime::now();
740        let token = self
741            .get_usertoken(&Id::Name(account_id.to_string()), current_time)
742            .await?;
743        Ok(token
744            .map(|t| {
745                // Only return keys if the account is valid
746                if t.valid {
747                    t.sshkeys
748                } else {
749                    Vec::with_capacity(0)
750                }
751            })
752            .unwrap_or_else(|| Vec::with_capacity(0)))
753    }
754
755    fn token_homedirectory_alias(&self, token: &UserToken) -> Option<String> {
756        let is_primary_origin = token.provider == self.primary_origin;
757        self.home_alias.map(|t| match t {
758            // If we have an alias. use it.
759            HomeAttr::Name if is_primary_origin => token.name.as_str().to_string(),
760            HomeAttr::Uuid => token.uuid.hyphenated().to_string(),
761            HomeAttr::Spn | HomeAttr::Name => token.spn.as_str().to_string(),
762        })
763    }
764
765    fn token_homedirectory_attr(&self, token: &UserToken) -> String {
766        let is_primary_origin = token.provider == self.primary_origin;
767        match self.home_attr {
768            HomeAttr::Name if is_primary_origin => token.name.as_str().to_string(),
769            HomeAttr::Uuid => token.uuid.hyphenated().to_string(),
770            HomeAttr::Spn | HomeAttr::Name => token.spn.as_str().to_string(),
771        }
772    }
773
774    fn token_homedirectory(&self, token: &UserToken) -> String {
775        self.token_homedirectory_alias(token)
776            .unwrap_or_else(|| self.token_homedirectory_attr(token))
777    }
778
779    fn token_abs_homedirectory(&self, token: &UserToken) -> String {
780        self.home_prefix
781            .join(self.token_homedirectory(token))
782            .to_string_lossy()
783            .to_string()
784    }
785
786    fn token_uidattr(&self, token: &UserToken) -> String {
787        let is_primary_origin = token.provider == self.primary_origin;
788        match self.uid_attr_map {
789            UidAttr::Name if is_primary_origin => token.name.as_str(),
790            UidAttr::Spn | UidAttr::Name => token.spn.as_str(),
791        }
792        .to_string()
793    }
794
795    #[instrument(level = "debug", skip_all)]
796    pub async fn get_nssaccounts(&self) -> Result<Vec<NssUser>, ()> {
797        // We don't need to filter the cached tokens as the cache shouldn't
798        // have anything that collides with system.
799        let system_nss_users = self.system_provider.get_nssaccounts().await;
800
801        let cached = self.get_cached_usertokens().await?;
802
803        Ok(system_nss_users
804            .into_iter()
805            .chain(cached.into_iter().map(|tok| NssUser {
806                homedir: self.token_abs_homedirectory(&tok),
807                name: self.token_uidattr(&tok),
808                uid: tok.gidnumber,
809                gid: tok.gidnumber,
810                gecos: tok.displayname,
811                shell: tok.shell.unwrap_or_else(|| self.default_shell.clone()),
812            }))
813            .collect())
814    }
815
816    #[instrument(level = "trace", skip_all)]
817    async fn get_nssaccount(
818        &self,
819        account_id: Id,
820        current_time: SystemTime,
821    ) -> Result<Option<NssUser>, ()> {
822        if let Some(nss_user) = self.system_provider.get_nssaccount(&account_id).await {
823            debug!("system provider satisfied request");
824            return Ok(Some(nss_user));
825        }
826
827        let token = self.get_usertoken(&account_id, current_time).await?;
828        Ok(token.map(|tok| NssUser {
829            homedir: self.token_abs_homedirectory(&tok),
830            name: self.token_uidattr(&tok),
831            uid: tok.gidnumber,
832            gid: tok.gidnumber,
833            gecos: tok.displayname,
834            shell: tok.shell.unwrap_or_else(|| self.default_shell.clone()),
835        }))
836    }
837
838    #[instrument(level = "debug", skip(self))]
839    pub async fn get_nssaccount_name_time(
840        &self,
841        account_id: &str,
842        current_time: SystemTime,
843    ) -> Result<Option<NssUser>, ()> {
844        self.get_nssaccount(Id::Name(account_id.to_string()), current_time)
845            .await
846    }
847
848    #[instrument(level = "debug", skip(self))]
849    pub async fn get_nssaccount_name(&self, account_id: &str) -> Result<Option<NssUser>, ()> {
850        let current_time = SystemTime::now();
851        self.get_nssaccount(Id::Name(account_id.to_string()), current_time)
852            .await
853    }
854
855    #[instrument(level = "debug", skip(self))]
856    pub async fn get_nssaccount_gid(&self, gid: u32) -> Result<Option<NssUser>, ()> {
857        let current_time = SystemTime::now();
858        self.get_nssaccount(Id::Gid(gid), current_time).await
859    }
860
861    fn token_gidattr(&self, token: &GroupToken) -> String {
862        match self.gid_attr_map {
863            UidAttr::Spn => token.spn.as_str(),
864            UidAttr::Name => token.name.as_str(),
865        }
866        .to_string()
867    }
868
869    #[instrument(level = "debug", skip_all)]
870    pub async fn get_nssgroups(&self) -> Result<Vec<NssGroup>, ()> {
871        let mut r = self.system_provider.get_nssgroups().await;
872
873        // Extend all the local groups if maps exist.
874        for nss_group in r.iter_mut() {
875            for client in self.clients.iter() {
876                if let Some(extend_group_id) = client.has_map_group(&nss_group.name) {
877                    let (_, token) = self.get_cached_grouptoken(extend_group_id).await?;
878                    if let Some(token) = token {
879                        let members = self.get_groupmembers(token.uuid).await;
880                        nss_group.members.extend(members);
881                        debug!(
882                            "extended group {} with members from {}",
883                            nss_group.name, token.name
884                        );
885                    }
886                }
887            }
888        }
889
890        let l = self.get_cached_grouptokens().await?;
891        r.reserve(l.len());
892        for tok in l.into_iter() {
893            let members = self.get_groupmembers(tok.uuid).await;
894            r.push(NssGroup {
895                name: self.token_gidattr(&tok),
896                gid: tok.gidnumber,
897                members,
898            })
899        }
900        Ok(r)
901    }
902
903    #[instrument(level = "trace", skip_all)]
904    async fn get_nssgroup(&self, grp_id: Id) -> Result<Option<NssGroup>, ()> {
905        if let Some(mut nss_group) = self.system_provider.get_nssgroup(&grp_id).await {
906            debug!("system provider satisfied request");
907
908            for client in self.clients.iter() {
909                if let Some(extend_group_id) = client.has_map_group(&nss_group.name) {
910                    let token = self
911                        .get_grouptoken(extend_group_id.clone(), SystemTime::now())
912                        .await?;
913                    if let Some(token) = token {
914                        let members = self.get_groupmembers(token.uuid).await;
915                        nss_group.members.extend(members);
916                        debug!(
917                            "extended group {} with members from {}",
918                            nss_group.name, token.name
919                        );
920                    }
921                }
922            }
923
924            nss_group.members.sort_unstable();
925            nss_group.members.dedup();
926
927            return Ok(Some(nss_group));
928        }
929
930        let token = self.get_grouptoken(grp_id, SystemTime::now()).await?;
931        // Get members set.
932        match token {
933            Some(tok) => {
934                let members = self.get_groupmembers(tok.uuid).await;
935                Ok(Some(NssGroup {
936                    name: self.token_gidattr(&tok),
937                    gid: tok.gidnumber,
938                    members,
939                }))
940            }
941            None => Ok(None),
942        }
943    }
944
945    #[instrument(level = "debug", skip(self))]
946    pub async fn get_nssgroup_name(&self, grp_id: &str) -> Result<Option<NssGroup>, ()> {
947        self.get_nssgroup(Id::Name(grp_id.to_string())).await
948    }
949
950    #[instrument(level = "debug", skip(self))]
951    pub async fn get_nssgroup_gid(&self, gid: u32) -> Result<Option<NssGroup>, ()> {
952        self.get_nssgroup(Id::Gid(gid)).await
953    }
954
955    #[instrument(level = "debug", skip(self))]
956    pub async fn pam_account_allowed(&self, account_id: &str) -> Result<Option<bool>, ()> {
957        let current_time = SystemTime::now();
958        let id = Id::Name(account_id.to_string());
959
960        if let Some(answer) = self.system_provider.authorise(&id).await {
961            return Ok(Some(answer));
962        };
963
964        // Not a system account, handle with the provider.
965        let token = self.get_usertoken(&id, current_time).await?;
966
967        // If there is no token, return Ok(None) to trigger unknown-user path in pam.
968        match token {
969            Some(token) => {
970                let client = self.client_ids.get(&token.provider)
971                    .cloned()
972                    .ok_or_else(|| {
973                        error!(provider = ?token.provider, "Token was resolved by a provider that no longer appears to be present.");
974                    })?;
975
976                client.unix_user_authorise(&token).await.map_err(|err| {
977                    error!(?err, "unable to authorise account");
978                })
979            }
980            None => Ok(None),
981        }
982    }
983
984    #[instrument(level = "debug", skip(self, shutdown_rx, current_time))]
985    pub async fn pam_account_authenticate_init(
986        &self,
987        account_id: &str,
988        pam_info: &PamServiceInfo,
989        current_time: OffsetDateTime,
990        shutdown_rx: broadcast::Receiver<()>,
991    ) -> Result<(AuthSession, PamAuthResponse), ()> {
992        // Setup an auth session. If possible bring the resolver online.
993        // Further steps won't attempt to bring the cache online to prevent
994        // weird interactions - they should assume online/offline only for
995        // the duration of their operation. A failure of connectivity during
996        // an online operation will take the cache offline however.
997        let now = SystemTime::now();
998
999        let id = Id::Name(account_id.to_string());
1000
1001        match self.system_provider.auth_init(&id, current_time).await {
1002            // The system provider will not take part in this authentication.
1003            SystemProviderAuthInit::Ignore => {
1004                debug!(?account_id, "account unknown to system provider, continue.");
1005            }
1006            // The provider knows the account, and is unable to proceed,
1007            // We return unknown here so that pam_kanidm can be skipped and fall back
1008            // to pam_unix.so.
1009            SystemProviderAuthInit::ShadowMissing => {
1010                warn!(
1011                    ?account_id,
1012                    "Resolver unable to proceed, {SYSTEM_SHADOW_PATH} was not accessible."
1013                );
1014                return Ok((AuthSession::Denied, PamAuthResponse::Unknown));
1015            }
1016            // There are no credentials for this account
1017            SystemProviderAuthInit::CredentialsUnavailable => {
1018                warn!(
1019                    ?account_id,
1020                    "Denying auth request for system user with no valid credentials"
1021                );
1022                return Ok((AuthSession::Denied, PamAuthResponse::Denied));
1023            }
1024            // The account has expired
1025            SystemProviderAuthInit::Expired => {
1026                warn!(
1027                    ?account_id,
1028                    "Denying auth request for system user with expired credentials"
1029                );
1030                return Ok((AuthSession::Denied, PamAuthResponse::Denied));
1031            }
1032            // The provider knows the account and wants to proceed,
1033            SystemProviderAuthInit::Begin {
1034                next_request,
1035                cred_handler,
1036                shadow,
1037            } => {
1038                let auth_session = AuthSession::System {
1039                    account_id: account_id.to_string(),
1040                    id,
1041                    shadow,
1042                    cred_handler,
1043                };
1044
1045                return Ok((auth_session, next_request.into()));
1046            }
1047        }
1048
1049        let token = self.get_usertoken(&id, now).await?;
1050
1051        // Get the provider associated to this token.
1052
1053        let mut hsm_lock = self.hsm.lock().await;
1054
1055        // We don't care if we are expired - we will always attempt to go
1056        // online and perform this operation online if possible.
1057
1058        if let Some(token) = token {
1059            // We have a token, we know what provider is needed
1060            let client = self.client_ids.get(&token.provider)
1061                .cloned()
1062                .ok_or_else(|| {
1063                    error!(provider = ?token.provider, "Token was resolved by a provider that no longer appears to be present.");
1064                })?;
1065
1066            // Can the auth proceed offline? This is important for us to make choices about
1067            // if we attempt to force an online state check.
1068            let can_proceed_offline = client.unix_user_can_offline_auth(&token).await;
1069
1070            // We can use is_online here because the earlier get_usertoken call leading up to this
1071            // will have taken the provider online if possible. This way if we are already in an
1072            // offline state, we don't needlessly delay the authentication operation.
1073            // In addition as we now have background prefetching of accounts, this will also
1074            // be checking frequently enough if we are online and will assist us to be online as
1075            // much as possible.
1076            let online_at_init = if can_proceed_offline {
1077                // This is only for accounts we have previously resolved and authenticated.
1078                client.is_online().await
1079            } else {
1080                // We know who the user is, but don't have cached credentials, so we need to try and
1081                // force online if possible.
1082                client.attempt_online(hsm_lock.deref_mut(), now).await
1083            };
1084
1085            // We're online, go for it.
1086            if online_at_init {
1087                let init_result = client
1088                    .unix_user_online_auth_init(
1089                        account_id,
1090                        &token,
1091                        hsm_lock.deref_mut(),
1092                        &shutdown_rx,
1093                    )
1094                    .await;
1095
1096                match init_result {
1097                    Ok((next_req, cred_handler)) => {
1098                        let auth_session = AuthSession::Online {
1099                            client,
1100                            account_id: account_id.to_string(),
1101                            id,
1102                            cred_handler,
1103                            shutdown_rx,
1104                        };
1105                        Ok((auth_session, next_req.into()))
1106                    }
1107                    Err(err) => {
1108                        error!(?err, "Unable to start authentication");
1109                        Err(())
1110                    }
1111                }
1112            } else {
1113                let init_result = client.unix_user_offline_auth_init(&token).await;
1114                match init_result {
1115                    Ok((next_req, cred_handler)) => {
1116                        let auth_session = AuthSession::Offline {
1117                            account_id: account_id.to_string(),
1118                            id,
1119                            client,
1120                            session_token: Box::new(token),
1121                            cred_handler,
1122                        };
1123                        Ok((auth_session, next_req.into()))
1124                    }
1125                    Err(err) => {
1126                        error!(?err, "Unable to start authentication");
1127                        Err(())
1128                    }
1129                }
1130            }
1131        } else {
1132            // We don't know anything about this user. Can we try to auth them?
1133
1134            // TODO: If any provider is offline should we fail the auth? I can imagine a possible
1135            // issue where if we had provides A, B, C stacked, and A was offline, then B could
1136            // service an auth that A *should* have serviced.
1137
1138            for client in self.clients.iter() {
1139                let online_at_init = client.attempt_online(hsm_lock.deref_mut(), now).await;
1140                debug!(?online_at_init);
1141
1142                if !online_at_init {
1143                    warn!(?account_id, "Unable to proceed with authentication, all providers must be online for unknown user authentication.");
1144                    return Ok((AuthSession::Denied, PamAuthResponse::Unknown));
1145                }
1146            }
1147
1148            for client in self.clients.iter() {
1149                let init_result = client
1150                    .unix_unknown_user_online_auth_init(
1151                        account_id,
1152                        hsm_lock.deref_mut(),
1153                        &shutdown_rx,
1154                    )
1155                    .await;
1156
1157                match init_result {
1158                    Ok(Some((next_req, cred_handler))) => {
1159                        let auth_session = AuthSession::Online {
1160                            client: client.clone(),
1161                            account_id: account_id.to_string(),
1162                            id,
1163                            cred_handler,
1164                            shutdown_rx,
1165                        };
1166                        return Ok((auth_session, next_req.into()));
1167                    }
1168                    Ok(None) => {
1169                        // Not for us, check the next provider.
1170                    }
1171                    Err(err) => {
1172                        error!(?err, "Unable to start authentication");
1173                        return Err(());
1174                    }
1175                }
1176            }
1177
1178            // No module signaled that they want it, bail.
1179            warn!("No provider is willing to service authentication of unknown account.");
1180            Ok((AuthSession::Denied, PamAuthResponse::Unknown))
1181        }
1182    }
1183
1184    #[instrument(level = "debug", skip_all)]
1185    pub async fn pam_account_authenticate_step(
1186        &self,
1187        auth_session: &mut AuthSession,
1188        pam_next_req: PamAuthRequest,
1189    ) -> Result<PamAuthResponse, ()> {
1190        let current_time = SystemTime::now();
1191        let mut hsm_lock = self.hsm.lock().await;
1192
1193        let maybe_err = match &mut *auth_session {
1194            &mut AuthSession::Online {
1195                ref client,
1196                ref account_id,
1197                ref id,
1198                ref mut cred_handler,
1199                ref shutdown_rx,
1200            } => {
1201                // This is not used in the authentication, but is so that any new
1202                // extra keys or data on the token are updated correctly if the authentication
1203                // requests an update. Since we hold the hsm_lock, no other task can
1204                // update this token between now and completion of the fn.
1205                let current_token = self
1206                    .get_cached_usertoken(id, current_time)
1207                    .await
1208                    .map(|(_expired, option_token)| option_token)
1209                    .map_err(|err| {
1210                        debug!(?err, "get_usertoken error");
1211                    })?;
1212
1213                let result = client
1214                    .unix_user_online_auth_step(
1215                        account_id,
1216                        current_token.as_ref(),
1217                        cred_handler,
1218                        pam_next_req,
1219                        hsm_lock.deref_mut(),
1220                        shutdown_rx,
1221                    )
1222                    .await;
1223
1224                match result {
1225                    Ok(AuthResult::SuccessUpdate { .. } | AuthResult::Success) => {
1226                        info!(?account_id, "Authentication Success");
1227                    }
1228                    Ok(AuthResult::Denied) => {
1229                        info!(?account_id, "Authentication Denied");
1230                    }
1231                    Ok(AuthResult::Next(_)) => {
1232                        info!(?account_id, "Authentication Continue");
1233                    }
1234                    _ => {}
1235                };
1236
1237                result
1238            }
1239            &mut AuthSession::Offline {
1240                ref account_id,
1241                ref id,
1242                ref client,
1243                ref session_token,
1244                ref mut cred_handler,
1245            } => {
1246                // This is not used in the authentication, but is so that any new
1247                // extra keys or data on the token are updated correctly if the authentication
1248                // requests an update. Since we hold the hsm_lock, no other task can
1249                // update this token between now and completion of the fn.
1250                let current_token = self
1251                    .get_cached_usertoken(id, current_time)
1252                    .await
1253                    .map(|(_expired, option_token)| option_token)
1254                    .map_err(|err| {
1255                        debug!(?err, "get_usertoken error");
1256                    })?;
1257
1258                // We are offline, continue. Remember, authsession should have
1259                // *everything you need* to proceed here!
1260                let result = client
1261                    .unix_user_offline_auth_step(
1262                        current_token.as_ref(),
1263                        session_token,
1264                        cred_handler,
1265                        pam_next_req,
1266                        hsm_lock.deref_mut(),
1267                    )
1268                    .await;
1269
1270                match result {
1271                    Ok(AuthResult::SuccessUpdate { .. } | AuthResult::Success) => {
1272                        info!(?account_id, "Authentication Success");
1273                    }
1274                    Ok(AuthResult::Denied) => {
1275                        info!(?account_id, "Authentication Denied");
1276                    }
1277                    Ok(AuthResult::Next(_)) => {
1278                        info!(?account_id, "Authentication Continue");
1279                    }
1280                    _ => {}
1281                };
1282
1283                result
1284            }
1285            &mut AuthSession::System {
1286                ref account_id,
1287                id: _,
1288                ref mut cred_handler,
1289                ref shadow,
1290            } => {
1291                // I had a lot of thoughts here, but I think system auth is
1292                // not the same as provider, so I think we special case here and have a separate
1293                // return type.
1294                let system_auth_result = shadow.auth_step(cred_handler, pam_next_req);
1295
1296                let next = match system_auth_result {
1297                    SystemAuthResult::Denied => {
1298                        info!(?account_id, "Authentication Denied");
1299
1300                        *auth_session = AuthSession::Denied;
1301
1302                        Ok(PamAuthResponse::Denied)
1303                    }
1304                    SystemAuthResult::Success => {
1305                        info!(?account_id, "Authentication Success");
1306
1307                        *auth_session = AuthSession::Success;
1308
1309                        Ok(PamAuthResponse::Success)
1310                    }
1311                    SystemAuthResult::Next(req) => Ok(req.into()),
1312                };
1313
1314                // We shortcut here
1315                return next;
1316            }
1317            &mut AuthSession::Success | &mut AuthSession::Denied => Err(IdpError::BadRequest),
1318        };
1319
1320        match maybe_err {
1321            // What did the provider direct us to do next?
1322            Ok(AuthResult::Success) => {
1323                *auth_session = AuthSession::Success;
1324                Ok(PamAuthResponse::Success)
1325            }
1326            Ok(AuthResult::SuccessUpdate { mut new_token }) => {
1327                self.set_cache_usertoken(&mut new_token, hsm_lock.deref_mut())
1328                    .await?;
1329                *auth_session = AuthSession::Success;
1330
1331                Ok(PamAuthResponse::Success)
1332            }
1333            Ok(AuthResult::Denied) => {
1334                *auth_session = AuthSession::Denied;
1335
1336                Ok(PamAuthResponse::Denied)
1337            }
1338            Ok(AuthResult::Next(req)) => Ok(req.into()),
1339            Err(IdpError::NotFound) => {
1340                *auth_session = AuthSession::Denied;
1341
1342                Ok(PamAuthResponse::Unknown)
1343            }
1344            Err(err) => {
1345                *auth_session = AuthSession::Denied;
1346
1347                error!(?err, "Unable to proceed, failing the session");
1348                Err(())
1349            }
1350        }
1351    }
1352
1353    // Can this be cfg debug/test?
1354    #[instrument(level = "debug", skip(self, password, current_time))]
1355    pub async fn pam_account_authenticate(
1356        &self,
1357        account_id: &str,
1358        current_time: OffsetDateTime,
1359        password: &str,
1360    ) -> Result<Option<bool>, ()> {
1361        let (_shutdown_tx, shutdown_rx) = broadcast::channel(1);
1362
1363        let pam_info = PamServiceInfo {
1364            service: "kanidm-unix-test".to_string(),
1365            tty: Some("/dev/null".to_string()),
1366            rhost: None,
1367        };
1368
1369        let mut auth_session = match self
1370            .pam_account_authenticate_init(account_id, &pam_info, current_time, shutdown_rx)
1371            .await?
1372        {
1373            (auth_session, PamAuthResponse::Password) => {
1374                // Can continue!
1375                auth_session
1376            }
1377            (auth_session, PamAuthResponse::DeviceAuthorizationGrant { .. }) => {
1378                // Can continue!
1379                auth_session
1380            }
1381            (auth_session, PamAuthResponse::MFACode { .. }) => {
1382                // Can continue!
1383                auth_session
1384            }
1385            (auth_session, PamAuthResponse::MFAPoll { .. }) => {
1386                // Can continue!
1387                auth_session
1388            }
1389            (auth_session, PamAuthResponse::MFAPollWait) => {
1390                // Can continue!
1391                auth_session
1392            }
1393            (auth_session, PamAuthResponse::SetupPin { .. }) => {
1394                // Can continue!
1395                auth_session
1396            }
1397            (auth_session, PamAuthResponse::Pin) => {
1398                // Can continue!
1399                auth_session
1400            }
1401            (_, PamAuthResponse::Unknown) => return Ok(None),
1402            (_, PamAuthResponse::Denied) => return Ok(Some(false)),
1403            (_, PamAuthResponse::Success) => {
1404                // Should never get here "off the rip".
1405                debug_assert!(false);
1406                return Ok(Some(true));
1407            }
1408        };
1409
1410        // Now we can make the next step.
1411        let pam_next_req = PamAuthRequest::Password {
1412            cred: password.to_string(),
1413        };
1414        match self
1415            .pam_account_authenticate_step(&mut auth_session, pam_next_req)
1416            .await?
1417        {
1418            PamAuthResponse::Success => Ok(Some(true)),
1419            PamAuthResponse::Denied => Ok(Some(false)),
1420            _ => {
1421                // Should not be able to get here, if the user was unknown they should
1422                // be out. If it wants more mechanisms, we can't proceed here.
1423                // debug_assert!(false);
1424                Ok(None)
1425            }
1426        }
1427    }
1428
1429    #[instrument(level = "debug", skip(self))]
1430    pub async fn pam_account_beginsession(
1431        &self,
1432        account_id: &str,
1433    ) -> Result<Option<HomeDirectoryInfo>, ()> {
1434        let current_time = SystemTime::now();
1435        let id = Id::Name(account_id.to_string());
1436
1437        match self.system_provider.begin_session(&id).await {
1438            SystemProviderSession::Start => {
1439                return Ok(None);
1440            }
1441            /*
1442            SystemProviderSession::StartCreateHome(
1443                info
1444            ) => {
1445                return Ok(Some(info));
1446            }
1447            */
1448            SystemProviderSession::Ignore => {}
1449        };
1450
1451        // Not a system account, check based on the token and resolve.
1452        let token = self.get_usertoken(&id, current_time).await?;
1453        Ok(token.as_ref().map(|tok| HomeDirectoryInfo {
1454            uid: tok.gidnumber,
1455            gid: tok.gidnumber,
1456            name: self.token_homedirectory_attr(tok),
1457            alias: self.token_homedirectory_alias(tok),
1458        }))
1459    }
1460
1461    #[instrument(level = "debug", skip_all)]
1462    pub async fn provider_status(&self) -> Vec<ProviderStatus> {
1463        let now = SystemTime::now();
1464        let mut hsm_lock = self.hsm.lock().await;
1465
1466        let mut results = Vec::with_capacity(self.clients.len() + 1);
1467
1468        results.push(ProviderStatus {
1469            name: "system".to_string(),
1470            online: true,
1471        });
1472
1473        for client in self.clients.iter() {
1474            let online = client.attempt_online(hsm_lock.deref_mut(), now).await;
1475
1476            let name = client.origin().to_string();
1477
1478            results.push(ProviderStatus { name, online })
1479        }
1480
1481        results
1482    }
1483
1484    #[instrument(level = "debug", skip_all)]
1485    pub async fn test_connection(&self) -> bool {
1486        let now = SystemTime::now();
1487        let mut hsm_lock = self.hsm.lock().await;
1488
1489        for client in self.clients.iter() {
1490            let status = client.attempt_online(hsm_lock.deref_mut(), now).await;
1491
1492            if !status {
1493                return false;
1494            }
1495        }
1496
1497        // All online
1498        true
1499    }
1500}