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