1use super::ldap::{LdapBoundToken, LdapSession};
2use crate::credential::{softlock::CredSoftLock, Credential};
3use crate::idm::account::Account;
4use crate::idm::application::{
5    LdapApplications, LdapApplicationsReadTransaction, LdapApplicationsWriteTransaction,
6};
7use crate::idm::audit::AuditEvent;
8use crate::idm::authsession::{AuthSession, AuthSessionData};
9use crate::idm::credupdatesession::CredentialUpdateSessionMutex;
10use crate::idm::delayed::{
11    AuthSessionRecord, BackupCodeRemoval, DelayedAction, PasswordUpgrade, UnixPasswordUpgrade,
12    WebauthnCounterIncrement,
13};
14use crate::idm::event::{AuthEvent, AuthEventStep, AuthResult};
15use crate::idm::event::{
16    CredentialStatusEvent, LdapAuthEvent, LdapTokenAuthEvent, RadiusAuthTokenEvent,
17    RegenerateRadiusSecretEvent, UnixGroupTokenEvent, UnixPasswordChangeEvent, UnixUserAuthEvent,
18    UnixUserTokenEvent,
19};
20use crate::idm::group::{Group, Unix};
21use crate::idm::oauth2::{
22    Oauth2ResourceServers, Oauth2ResourceServersReadTransaction,
23    Oauth2ResourceServersWriteTransaction,
24};
25use crate::idm::radius::RadiusAccount;
26use crate::idm::scim::SyncAccount;
27use crate::idm::serviceaccount::ServiceAccount;
28use crate::idm::AuthState;
29use crate::idm::PreValidatedTokenStatus;
30use crate::prelude::*;
31use crate::server::keys::KeyProvidersTransaction;
32use crate::server::DomainInfo;
33use crate::utils::{password_from_random, readable_password_from_random, uuid_from_duration, Sid};
34use crate::value::{Session, SessionState};
35use compact_jwt::{Jwk, JwsCompact};
36use concread::bptree::{BptreeMap, BptreeMapReadTxn, BptreeMapWriteTxn};
37use concread::cowcell::CowCellReadTxn;
38use concread::hashmap::HashMap;
39use kanidm_lib_crypto::CryptoPolicy;
40use kanidm_proto::internal::{
41    ApiToken, CredentialStatus, PasswordFeedback, RadiusAuthToken, ScimSyncToken, UatPurpose,
42    UserAuthToken,
43};
44use kanidm_proto::v1::{UnixGroupToken, UnixUserToken};
45use rand::prelude::*;
46use std::convert::TryFrom;
47use std::sync::Arc;
48use std::time::Duration;
49use tokio::sync::mpsc::{
50    unbounded_channel as unbounded, UnboundedReceiver as Receiver, UnboundedSender as Sender,
51};
52use tokio::sync::{Mutex, Semaphore};
53use tracing::trace;
54use url::Url;
55use webauthn_rs::prelude::{Webauthn, WebauthnBuilder};
56use zxcvbn::{zxcvbn, Score};
57
58#[cfg(test)]
59use crate::idm::event::PasswordChangeEvent;
60
61pub(crate) type AuthSessionMutex = Arc<Mutex<AuthSession>>;
62pub(crate) type CredSoftLockMutex = Arc<Mutex<CredSoftLock>>;
63
64pub type DomainInfoRead = CowCellReadTxn<DomainInfo>;
65
66pub struct IdmServer {
67    session_ticket: Semaphore,
72    sessions: BptreeMap<Uuid, AuthSessionMutex>,
73    softlocks: HashMap<Uuid, CredSoftLockMutex>,
74    cred_update_sessions: BptreeMap<Uuid, CredentialUpdateSessionMutex>,
76    qs: QueryServer,
78    crypto_policy: CryptoPolicy,
80    async_tx: Sender<DelayedAction>,
81    audit_tx: Sender<AuditEvent>,
82    webauthn: Webauthn,
84    oauth2rs: Arc<Oauth2ResourceServers>,
85    applications: Arc<LdapApplications>,
86}
87
88pub struct IdmServerAuthTransaction<'a> {
90    pub(crate) session_ticket: &'a Semaphore,
91    pub(crate) sessions: &'a BptreeMap<Uuid, AuthSessionMutex>,
92    pub(crate) softlocks: &'a HashMap<Uuid, CredSoftLockMutex>,
93
94    pub qs_read: QueryServerReadTransaction<'a>,
95    pub(crate) sid: Sid,
97    pub(crate) async_tx: Sender<DelayedAction>,
99    pub(crate) audit_tx: Sender<AuditEvent>,
100    pub(crate) webauthn: &'a Webauthn,
101    pub(crate) applications: LdapApplicationsReadTransaction,
102}
103
104pub struct IdmServerCredUpdateTransaction<'a> {
105    pub(crate) qs_read: QueryServerReadTransaction<'a>,
106    pub(crate) webauthn: &'a Webauthn,
108    pub(crate) cred_update_sessions: BptreeMapReadTxn<'a, Uuid, CredentialUpdateSessionMutex>,
109    pub(crate) crypto_policy: &'a CryptoPolicy,
110}
111
112pub struct IdmServerProxyReadTransaction<'a> {
114    pub qs_read: QueryServerReadTransaction<'a>,
115    pub(crate) oauth2rs: Oauth2ResourceServersReadTransaction,
116}
117
118pub struct IdmServerProxyWriteTransaction<'a> {
119    pub qs_write: QueryServerWriteTransaction<'a>,
122    pub(crate) cred_update_sessions: BptreeMapWriteTxn<'a, Uuid, CredentialUpdateSessionMutex>,
124    pub(crate) sid: Sid,
125    crypto_policy: &'a CryptoPolicy,
126    webauthn: &'a Webauthn,
127    pub(crate) oauth2rs: Oauth2ResourceServersWriteTransaction<'a>,
128    pub(crate) applications: LdapApplicationsWriteTransaction<'a>,
129}
130
131pub struct IdmServerDelayed {
132    pub(crate) async_rx: Receiver<DelayedAction>,
133}
134
135pub struct IdmServerAudit {
136    pub(crate) audit_rx: Receiver<AuditEvent>,
137}
138
139impl IdmServer {
140    pub async fn new(
141        qs: QueryServer,
142        origin: &Url,
143        is_integration_test: bool,
144        current_time: Duration,
145    ) -> Result<(IdmServer, IdmServerDelayed, IdmServerAudit), OperationError> {
146        let crypto_policy = if cfg!(test) || is_integration_test {
147            CryptoPolicy::danger_test_minimum()
148        } else {
149            CryptoPolicy::time_target(Duration::from_millis(10))
152        };
153
154        let (async_tx, async_rx) = unbounded();
155        let (audit_tx, audit_rx) = unbounded();
156
157        let (rp_id, rp_name, application_set) = {
159            let mut qs_read = qs.read().await?;
160            (
161                qs_read.get_domain_name().to_string(),
162                qs_read.get_domain_display_name().to_string(),
163                qs_read.get_applications_set()?,
165            )
166        };
167
168        let valid = origin
170            .domain()
171            .map(|effective_domain| {
172                effective_domain.ends_with(&format!(".{rp_id}")) || effective_domain == rp_id
175            })
176            .unwrap_or(false);
177
178        if !valid {
179            admin_error!(
180                "Effective domain (ed) is not a descendent of server domain name (rp_id)."
181            );
182            admin_error!(
183                "You must change origin or domain name to be consistent. ded: {:?} - rp_id: {:?}",
184                origin,
185                rp_id
186            );
187            admin_error!("To change the origin or domain name see: https://kanidm.github.io/kanidm/master/server_configuration.html");
188            return Err(OperationError::InvalidState);
189        };
190
191        let webauthn = WebauthnBuilder::new(&rp_id, origin)
192            .and_then(|builder| builder.allow_subdomains(true).rp_name(&rp_name).build())
193            .map_err(|e| {
194                admin_error!("Invalid Webauthn Configuration - {:?}", e);
195                OperationError::InvalidState
196            })?;
197
198        let oauth2rs = Oauth2ResourceServers::new(origin.to_owned()).map_err(|err| {
199            error!(?err, "Failed to load oauth2 resource servers");
200            err
201        })?;
202
203        let applications = LdapApplications::try_from(application_set).map_err(|e| {
204            admin_error!("Failed to load ldap applications - {:?}", e);
205            e
206        })?;
207
208        let idm_server = IdmServer {
209            session_ticket: Semaphore::new(1),
210            sessions: BptreeMap::new(),
211            softlocks: HashMap::new(),
212            cred_update_sessions: BptreeMap::new(),
213            qs,
214            crypto_policy,
215            async_tx,
216            audit_tx,
217            webauthn,
218            oauth2rs: Arc::new(oauth2rs),
219            applications: Arc::new(applications),
220        };
221        let idm_server_delayed = IdmServerDelayed { async_rx };
222        let idm_server_audit = IdmServerAudit { audit_rx };
223
224        let mut idm_write_txn = idm_server.proxy_write(current_time).await?;
225
226        idm_write_txn.reload_applications()?;
227        idm_write_txn.reload_oauth2()?;
228
229        idm_write_txn.commit()?;
230
231        Ok((idm_server, idm_server_delayed, idm_server_audit))
232    }
233
234    pub async fn auth(&self) -> Result<IdmServerAuthTransaction<'_>, OperationError> {
236        let qs_read = self.qs.read().await?;
237
238        let mut sid = [0; 4];
239        let mut rng = StdRng::from_os_rng();
240        rng.fill(&mut sid);
241
242        Ok(IdmServerAuthTransaction {
243            session_ticket: &self.session_ticket,
244            sessions: &self.sessions,
245            softlocks: &self.softlocks,
246            qs_read,
247            sid,
248            async_tx: self.async_tx.clone(),
249            audit_tx: self.audit_tx.clone(),
250            webauthn: &self.webauthn,
251            applications: self.applications.read(),
252        })
253    }
254
255    #[instrument(level = "debug", skip_all)]
259    pub fn domain_read(&self) -> DomainInfoRead {
260        self.qs.d_info.read()
261    }
262
263    #[instrument(level = "debug", skip_all)]
265    pub async fn proxy_read(&self) -> Result<IdmServerProxyReadTransaction<'_>, OperationError> {
266        let qs_read = self.qs.read().await?;
267        Ok(IdmServerProxyReadTransaction {
268            qs_read,
269            oauth2rs: self.oauth2rs.read(),
270            })
272    }
273
274    #[instrument(level = "debug", skip_all)]
275    pub async fn proxy_write(
276        &self,
277        ts: Duration,
278    ) -> Result<IdmServerProxyWriteTransaction<'_>, OperationError> {
279        let qs_write = self.qs.write(ts).await?;
280
281        let mut sid = [0; 4];
282        let mut rng = StdRng::from_os_rng();
283        rng.fill(&mut sid);
284
285        Ok(IdmServerProxyWriteTransaction {
286            cred_update_sessions: self.cred_update_sessions.write(),
287            qs_write,
288            sid,
289            crypto_policy: &self.crypto_policy,
290            webauthn: &self.webauthn,
291            oauth2rs: self.oauth2rs.write(),
292            applications: self.applications.write(),
293        })
294    }
295
296    pub async fn cred_update_transaction(
297        &self,
298    ) -> Result<IdmServerCredUpdateTransaction<'_>, OperationError> {
299        let qs_read = self.qs.read().await?;
300        Ok(IdmServerCredUpdateTransaction {
301            qs_read,
302            webauthn: &self.webauthn,
304            cred_update_sessions: self.cred_update_sessions.read(),
305            crypto_policy: &self.crypto_policy,
306        })
307    }
308
309    #[cfg(test)]
310    pub(crate) async fn delayed_action(
311        &self,
312        ct: Duration,
313        da: DelayedAction,
314    ) -> Result<bool, OperationError> {
315        let mut pw = self.proxy_write(ct).await?;
316        pw.process_delayedaction(&da, ct)
317            .and_then(|_| pw.commit())
318            .map(|()| true)
319    }
320}
321
322impl IdmServerAudit {
323    #[cfg(test)]
324    pub(crate) fn check_is_empty_or_panic(&mut self) {
325        use tokio::sync::mpsc::error::TryRecvError;
326
327        match self.audit_rx.try_recv() {
328            Err(TryRecvError::Empty) => {}
329            Err(TryRecvError::Disconnected) => {
330                panic!("Task queue disconnected");
331            }
332            Ok(m) => {
333                trace!(?m);
334                panic!("Task queue not empty");
335            }
336        }
337    }
338
339    pub fn audit_rx(&mut self) -> &mut Receiver<AuditEvent> {
340        &mut self.audit_rx
341    }
342}
343
344impl IdmServerDelayed {
345    #[cfg(test)]
346    pub(crate) fn check_is_empty_or_panic(&mut self) {
347        use tokio::sync::mpsc::error::TryRecvError;
348
349        match self.async_rx.try_recv() {
350            Err(TryRecvError::Empty) => {}
351            Err(TryRecvError::Disconnected) => {
352                panic!("Task queue disconnected");
353            }
354            #[allow(clippy::panic)]
355            Ok(m) => {
356                trace!(?m);
357                panic!("Task queue not empty");
358            }
359        }
360    }
361
362    #[cfg(test)]
363    pub(crate) fn try_recv(&mut self) -> Result<DelayedAction, OperationError> {
364        use core::task::{Context, Poll};
365        use futures::task as futures_task;
366
367        let waker = futures_task::noop_waker();
368        let mut cx = Context::from_waker(&waker);
369        match self.async_rx.poll_recv(&mut cx) {
370            Poll::Pending => Err(OperationError::InvalidState),
371            Poll::Ready(None) => Err(OperationError::QueueDisconnected),
372            Poll::Ready(Some(m)) => Ok(m),
373        }
374    }
375
376    pub async fn recv_many(&mut self, buffer: &mut Vec<DelayedAction>) -> usize {
377        debug_assert!(buffer.is_empty());
378        let limit = buffer.capacity();
379        self.async_rx.recv_many(buffer, limit).await
380    }
381}
382
383pub enum Token {
384    UserAuthToken(UserAuthToken),
385    ApiToken(ApiToken, Arc<EntrySealedCommitted>),
386}
387
388pub trait IdmServerTransaction<'a> {
389    type QsTransactionType: QueryServerTransaction<'a>;
390
391    fn get_qs_txn(&mut self) -> &mut Self::QsTransactionType;
392
393    #[instrument(level = "info", skip_all)]
402    fn validate_client_auth_info_to_ident(
403        &mut self,
404        client_auth_info: ClientAuthInfo,
405        ct: Duration,
406    ) -> Result<Identity, OperationError> {
407        let ClientAuthInfo {
408            source,
409            client_cert,
410            bearer_token,
411            basic_authz: _,
412            pre_validated_token: _,
413        } = client_auth_info;
414
415        match (client_cert, bearer_token) {
422            (Some(client_cert_info), _) => {
423                self.client_certificate_to_identity(&client_cert_info, ct, source)
424            }
425            (None, Some(token)) => {
426                match self.validate_and_parse_token_to_identity_token(&token, ct)? {
427                    Token::UserAuthToken(uat) => self.process_uat_to_identity(&uat, ct, source),
428                    Token::ApiToken(apit, entry) => {
429                        self.process_apit_to_identity(&apit, source, entry, ct)
430                    }
431                }
432            }
433            (None, None) => {
434                debug!("No client certificate or bearer tokens were supplied");
435                Err(OperationError::NotAuthenticated)
436            }
437        }
438    }
439
440    #[instrument(level = "info", skip_all)]
446    fn pre_validate_client_auth_info(
447        &mut self,
448        client_auth_info: &mut ClientAuthInfo,
449        ct: Duration,
450    ) -> Result<(), OperationError> {
451        let (result, status) = match self.validate_client_auth_info_to_uat(client_auth_info, ct) {
452            Ok(uat) => (Ok(()), PreValidatedTokenStatus::Valid(Box::new(uat))),
453            Err(OperationError::NotAuthenticated) => {
454                (Ok(()), PreValidatedTokenStatus::NotAuthenticated)
455            }
456            Err(OperationError::SessionExpired) => {
457                (Ok(()), PreValidatedTokenStatus::SessionExpired)
458            }
459            Err(err) => (Err(err), PreValidatedTokenStatus::None),
460        };
461
462        client_auth_info.set_pre_validated_uat(status);
463
464        result
466    }
467
468    #[instrument(level = "info", skip_all)]
472    fn validate_client_auth_info_to_uat(
473        &mut self,
474        client_auth_info: &ClientAuthInfo,
475        ct: Duration,
476    ) -> Result<UserAuthToken, OperationError> {
477        match (
480            client_auth_info.client_cert.as_ref(),
481            client_auth_info.bearer_token.as_ref(),
482        ) {
483            (Some(client_cert_info), _) => {
484                self.client_certificate_to_user_auth_token(client_cert_info, ct)
485            }
486            (None, Some(token)) => {
487                match self.validate_and_parse_token_to_identity_token(token, ct)? {
488                    Token::UserAuthToken(uat) => Ok(uat),
489                    Token::ApiToken(_apit, _entry) => {
490                        warn!("Unable to process non user auth token");
491                        Err(OperationError::NotAuthenticated)
492                    }
493                }
494            }
495            (None, None) => {
496                debug!("No client certificate or bearer tokens were supplied");
497                Err(OperationError::NotAuthenticated)
498            }
499        }
500    }
501
502    fn validate_and_parse_token_to_identity_token(
503        &mut self,
504        jwsu: &JwsCompact,
505        ct: Duration,
506    ) -> Result<Token, OperationError> {
507        let jws_inner = self
510            .get_qs_txn()
511            .get_domain_key_object_handle()?
512            .jws_verify(jwsu)
513            .map_err(|err| {
514                security_info!(?err, "Unable to verify token");
515                OperationError::NotAuthenticated
516            })?;
517
518        if let Ok(uat) = jws_inner.from_json::<UserAuthToken>() {
520            if let Some(exp) = uat.expiry {
521                let ct_odt = time::OffsetDateTime::UNIX_EPOCH + ct;
522                if exp < ct_odt {
523                    security_info!(?ct_odt, ?exp, "Session expired");
524                    return Err(OperationError::SessionExpired);
525                } else {
526                    trace!(?ct_odt, ?exp, "Session not yet expired");
527                    return Ok(Token::UserAuthToken(uat));
528                }
529            } else {
530                debug!("Session has no expiry");
531                return Ok(Token::UserAuthToken(uat));
532            }
533        };
534
535        if let Ok(apit) = jws_inner.from_json::<ApiToken>() {
537            if let Some(expiry) = apit.expiry {
538                if time::OffsetDateTime::UNIX_EPOCH + ct >= expiry {
539                    security_info!("Session expired");
540                    return Err(OperationError::SessionExpired);
541                }
542            }
543
544            let entry = self
545                .get_qs_txn()
546                .internal_search_uuid(apit.account_id)
547                .map_err(|err| {
548                    security_info!(?err, "Account associated with api token no longer exists.");
549                    OperationError::NotAuthenticated
550                })?;
551
552            return Ok(Token::ApiToken(apit, entry));
553        };
554
555        security_info!("Unable to verify token, invalid inner JSON");
556        Err(OperationError::NotAuthenticated)
557    }
558
559    fn check_oauth2_account_uuid_valid(
560        &mut self,
561        uuid: Uuid,
562        session_id: Uuid,
563        parent_session_id: Option<Uuid>,
564        iat: i64,
565        ct: Duration,
566    ) -> Result<Option<Arc<Entry<EntrySealed, EntryCommitted>>>, OperationError> {
567        let entry = self.get_qs_txn().internal_search_uuid(uuid).map_err(|e| {
568            admin_error!(?e, "check_oauth2_account_uuid_valid failed");
569            e
570        })?;
571
572        let within_valid_window = Account::check_within_valid_time(
573            ct,
574            entry
575                .get_ava_single_datetime(Attribute::AccountValidFrom)
576                .as_ref(),
577            entry
578                .get_ava_single_datetime(Attribute::AccountExpire)
579                .as_ref(),
580        );
581
582        if !within_valid_window {
583            security_info!("Account has expired or is not yet valid, not allowing to proceed");
584            return Ok(None);
585        }
586
587        let grace_valid = ct < (Duration::from_secs(iat as u64) + AUTH_TOKEN_GRACE_WINDOW);
592
593        let oauth2_session = entry
594            .get_ava_as_oauth2session_map(Attribute::OAuth2Session)
595            .and_then(|sessions| sessions.get(&session_id));
596
597        if let Some(oauth2_session) = oauth2_session {
598            let oauth2_session_valid = !matches!(oauth2_session.state, SessionState::RevokedAt(_));
600
601            if !oauth2_session_valid {
602                security_info!("The oauth2 session associated to this token is revoked.");
603                return Ok(None);
604            }
605
606            if let Some(parent_session_id) = parent_session_id {
608                let uat_session = entry
609                    .get_ava_as_session_map(Attribute::UserAuthTokenSession)
610                    .and_then(|sessions| sessions.get(&parent_session_id));
611
612                if let Some(uat_session) = uat_session {
613                    let parent_session_valid =
614                        !matches!(uat_session.state, SessionState::RevokedAt(_));
615                    if parent_session_valid {
616                        security_info!(
617                            "A valid parent and oauth2 session value exists for this token"
618                        );
619                    } else {
620                        security_info!(
621                            "The parent oauth2 session associated to this token is revoked."
622                        );
623                        return Ok(None);
624                    }
625                } else if grace_valid {
626                    security_info!(
627                        "The token grace window is in effect. Assuming parent session valid."
628                    );
629                } else {
630                    security_info!("The token grace window has passed and no entry parent sessions exist. Assuming invalid.");
631                    return Ok(None);
632                }
633            }
634            } else if grace_valid {
636            security_info!("The token grace window is in effect. Assuming valid.");
637        } else {
638            security_info!(
639                "The token grace window has passed and no entry sessions exist. Assuming invalid."
640            );
641            return Ok(None);
642        }
643
644        Ok(Some(entry))
645    }
646
647    #[instrument(level = "debug", skip_all)]
660    fn process_uat_to_identity(
661        &mut self,
662        uat: &UserAuthToken,
663        ct: Duration,
664        source: Source,
665    ) -> Result<Identity, OperationError> {
666        let entry = self
668            .get_qs_txn()
669            .internal_search_uuid(uat.uuid)
670            .map_err(|e| {
671                admin_error!(?e, "from_ro_uat failed");
672                e
673            })?;
674
675        let valid = Account::check_user_auth_token_valid(ct, uat, &entry);
676
677        if !valid {
678            return Err(OperationError::SessionExpired);
679        }
680
681        let scope = match uat.purpose {
684            UatPurpose::ReadOnly => AccessScope::ReadOnly,
685            UatPurpose::ReadWrite { expiry: None } => AccessScope::ReadOnly,
686            UatPurpose::ReadWrite {
687                expiry: Some(expiry),
688            } => {
689                let cot = time::OffsetDateTime::UNIX_EPOCH + ct;
690                if cot < expiry {
691                    AccessScope::ReadWrite
692                } else {
693                    AccessScope::ReadOnly
694                }
695            }
696        };
697
698        let mut limits = Limits::default();
699        if let Some(lim) = uat.limit_search_max_results.and_then(|v| v.try_into().ok()) {
701            limits.search_max_results = lim;
702        }
703        if let Some(lim) = uat
704            .limit_search_max_filter_test
705            .and_then(|v| v.try_into().ok())
706        {
707            limits.search_max_filter_test = lim;
708        }
709
710        Ok(Identity::new(
726            IdentType::User(IdentUser { entry }),
727            source,
728            uat.session_id,
729            scope,
730            limits,
731        ))
732    }
733
734    #[instrument(level = "debug", skip_all)]
735    fn process_apit_to_identity(
736        &mut self,
737        apit: &ApiToken,
738        source: Source,
739        entry: Arc<EntrySealedCommitted>,
740        ct: Duration,
741    ) -> Result<Identity, OperationError> {
742        let valid = ServiceAccount::check_api_token_valid(ct, apit, &entry);
743
744        if !valid {
745            return Err(OperationError::SessionExpired);
747        }
748
749        let scope = (&apit.purpose).into();
750
751        let limits = Limits::api_token();
752        Ok(Identity::new(
753            IdentType::User(IdentUser { entry }),
754            source,
755            apit.token_id,
756            scope,
757            limits,
758        ))
759    }
760
761    fn client_cert_info_entry(
762        &mut self,
763        client_cert_info: &ClientCertInfo,
764    ) -> Result<Arc<EntrySealedCommitted>, OperationError> {
765        let pks256 = hex::encode(client_cert_info.public_key_s256);
766        let mut maybe_cert_entries = self.get_qs_txn().internal_search(filter!(f_eq(
768            Attribute::Certificate,
769            PartialValue::HexString(pks256.clone())
770        )))?;
771
772        let maybe_cert_entry = maybe_cert_entries.pop();
773
774        if let Some(cert_entry) = maybe_cert_entry {
775            if maybe_cert_entries.is_empty() {
776                Ok(cert_entry)
777            } else {
778                debug!(?pks256, "Multiple certificates matched, unable to proceed.");
779                Err(OperationError::NotAuthenticated)
780            }
781        } else {
782            debug!(?pks256, "No certificates were able to be mapped.");
783            Err(OperationError::NotAuthenticated)
784        }
785    }
786
787    #[instrument(level = "debug", skip_all)]
796    fn client_certificate_to_identity(
797        &mut self,
798        client_cert_info: &ClientCertInfo,
799        ct: Duration,
800        source: Source,
801    ) -> Result<Identity, OperationError> {
802        let cert_entry = self.client_cert_info_entry(client_cert_info)?;
803
804        let refers_uuid = cert_entry
806            .get_ava_single_refer(Attribute::Refers)
807            .ok_or_else(|| {
808                warn!("Invalid certificate entry, missing refers");
809                OperationError::InvalidState
810            })?;
811
812        let entry = self.get_qs_txn().internal_search_uuid(refers_uuid)?;
814
815        let (account, account_policy) =
816            Account::try_from_entry_with_policy(entry.as_ref(), self.get_qs_txn())?;
817
818        if !account.is_within_valid_time(ct) {
820            return Err(OperationError::SessionExpired);
822        };
823
824        let scope = AccessScope::ReadOnly;
826
827        let mut limits = Limits::default();
828        if let Some(lim) = account_policy
830            .limit_search_max_results()
831            .and_then(|v| v.try_into().ok())
832        {
833            limits.search_max_results = lim;
834        }
835        if let Some(lim) = account_policy
836            .limit_search_max_filter_test()
837            .and_then(|v| v.try_into().ok())
838        {
839            limits.search_max_filter_test = lim;
840        }
841
842        let certificate_uuid = cert_entry.get_uuid();
843
844        Ok(Identity::new(
845            IdentType::User(IdentUser { entry }),
846            source,
847            certificate_uuid,
849            scope,
850            limits,
851        ))
852    }
853
854    #[instrument(level = "debug", skip_all)]
855    fn client_certificate_to_user_auth_token(
856        &mut self,
857        client_cert_info: &ClientCertInfo,
858        ct: Duration,
859    ) -> Result<UserAuthToken, OperationError> {
860        let cert_entry = self.client_cert_info_entry(client_cert_info)?;
861
862        let refers_uuid = cert_entry
864            .get_ava_single_refer(Attribute::Refers)
865            .ok_or_else(|| {
866                warn!("Invalid certificate entry, missing refers");
867                OperationError::InvalidState
868            })?;
869
870        let entry = self.get_qs_txn().internal_search_uuid(refers_uuid)?;
872
873        let (account, account_policy) =
874            Account::try_from_entry_with_policy(entry.as_ref(), self.get_qs_txn())?;
875
876        if !account.is_within_valid_time(ct) {
878            return Err(OperationError::SessionExpired);
880        };
881
882        let certificate_uuid = cert_entry.get_uuid();
883        let session_is_rw = false;
884
885        account
886            .client_cert_info_to_userauthtoken(certificate_uuid, session_is_rw, ct, &account_policy)
887            .ok_or(OperationError::InvalidState)
888    }
889
890    fn process_ldap_uuid_to_identity(
891        &mut self,
892        uuid: &Uuid,
893        ct: Duration,
894        source: Source,
895    ) -> Result<Identity, OperationError> {
896        let entry = self
897            .get_qs_txn()
898            .internal_search_uuid(*uuid)
899            .map_err(|err| {
900                error!(?err, ?uuid, "Failed to search user by uuid");
901                err
902            })?;
903
904        let (account, account_policy) =
905            Account::try_from_entry_with_policy(entry.as_ref(), self.get_qs_txn())?;
906
907        if !account.is_within_valid_time(ct) {
908            info!("Account is expired or not yet valid.");
909            return Err(OperationError::SessionExpired);
910        }
911
912        let anon_entry = if *uuid == UUID_ANONYMOUS {
914            entry
916        } else {
917            self.get_qs_txn()
919                .internal_search_uuid(UUID_ANONYMOUS)
920                .map_err(|err| {
921                    error!(
922                        ?err,
923                        "Unable to search anonymous user for privilege bounding."
924                    );
925                    err
926                })?
927        };
928
929        let mut limits = Limits::default();
930        let session_id = Uuid::new_v4();
931
932        if let Some(max_results) = account_policy.limit_search_max_results() {
934            limits.search_max_results = max_results as usize;
935        }
936        if let Some(max_filter) = account_policy.limit_search_max_filter_test() {
937            limits.search_max_filter_test = max_filter as usize;
938        }
939
940        Ok(Identity::new(
943            IdentType::User(IdentUser { entry: anon_entry }),
944            source,
945            session_id,
946            AccessScope::ReadOnly,
947            limits,
948        ))
949    }
950
951    #[instrument(level = "debug", skip_all)]
952    fn validate_ldap_session(
953        &mut self,
954        session: &LdapSession,
955        source: Source,
956        ct: Duration,
957    ) -> Result<Identity, OperationError> {
958        match session {
959            LdapSession::UnixBind(uuid) | LdapSession::ApplicationPasswordBind(_, uuid) => {
960                self.process_ldap_uuid_to_identity(uuid, ct, source)
961            }
962            LdapSession::UserAuthToken(uat) => self.process_uat_to_identity(uat, ct, source),
963            LdapSession::ApiToken(apit) => {
964                let entry = self
965                    .get_qs_txn()
966                    .internal_search_uuid(apit.account_id)
967                    .map_err(|e| {
968                        admin_error!("Failed to validate ldap session -> {:?}", e);
969                        e
970                    })?;
971
972                self.process_apit_to_identity(apit, source, entry, ct)
973            }
974        }
975    }
976
977    #[instrument(level = "info", skip_all)]
978    fn validate_sync_client_auth_info_to_ident(
979        &mut self,
980        client_auth_info: ClientAuthInfo,
981        ct: Duration,
982    ) -> Result<Identity, OperationError> {
983        let jwsu = client_auth_info.bearer_token.ok_or_else(|| {
986            security_info!("No token provided");
987            OperationError::NotAuthenticated
988        })?;
989
990        let jws_inner = self
991            .get_qs_txn()
992            .get_domain_key_object_handle()?
993            .jws_verify(&jwsu)
994            .map_err(|err| {
995                security_info!(?err, "Unable to verify token");
996                OperationError::NotAuthenticated
997            })?;
998
999        let sync_token = jws_inner.from_json::<ScimSyncToken>().map_err(|err| {
1000            error!(?err, "Unable to deserialise JWS");
1001            OperationError::SerdeJsonError
1002        })?;
1003
1004        let entry = self
1005            .get_qs_txn()
1006            .internal_search(filter!(f_eq(
1007                Attribute::SyncTokenSession,
1008                PartialValue::Refer(sync_token.token_id)
1009            )))
1010            .and_then(|mut vs| match vs.pop() {
1011                Some(entry) if vs.is_empty() => Ok(entry),
1012                _ => {
1013                    admin_error!(
1014                        token_id = ?sync_token.token_id,
1015                        "entries was empty, or matched multiple results for token id"
1016                    );
1017                    Err(OperationError::NotAuthenticated)
1018                }
1019            })?;
1020
1021        let valid = SyncAccount::check_sync_token_valid(ct, &sync_token, &entry);
1022
1023        if !valid {
1024            security_info!("Unable to proceed with invalid sync token");
1025            return Err(OperationError::NotAuthenticated);
1026        }
1027
1028        let scope = (&sync_token.purpose).into();
1030
1031        let limits = Limits::unlimited();
1032        Ok(Identity::new(
1033            IdentType::Synch(entry.get_uuid()),
1034            client_auth_info.source,
1035            sync_token.token_id,
1036            scope,
1037            limits,
1038        ))
1039    }
1040}
1041
1042impl<'a> IdmServerTransaction<'a> for IdmServerAuthTransaction<'a> {
1043    type QsTransactionType = QueryServerReadTransaction<'a>;
1044
1045    fn get_qs_txn(&mut self) -> &mut Self::QsTransactionType {
1046        &mut self.qs_read
1047    }
1048}
1049
1050impl IdmServerAuthTransaction<'_> {
1051    #[cfg(test)]
1052    pub fn is_sessionid_present(&self, sessionid: Uuid) -> bool {
1053        let session_read = self.sessions.read();
1054        session_read.contains_key(&sessionid)
1055    }
1056
1057    pub fn get_origin(&self) -> &Url {
1058        #[allow(clippy::unwrap_used)]
1059        self.webauthn.get_allowed_origins().first().unwrap()
1060    }
1061
1062    #[instrument(level = "trace", skip(self))]
1063    pub async fn expire_auth_sessions(&mut self, ct: Duration) {
1064        let expire = ct - Duration::from_secs(AUTH_SESSION_TIMEOUT);
1066        let split_at = uuid_from_duration(expire, self.sid);
1067        let _session_ticket = self.session_ticket.acquire().await;
1069        let mut session_write = self.sessions.write();
1070        session_write.split_off_lt(&split_at);
1071        session_write.commit();
1073    }
1074
1075    pub async fn auth(
1076        &mut self,
1077        ae: &AuthEvent,
1078        ct: Duration,
1079        client_auth_info: ClientAuthInfo,
1080    ) -> Result<AuthResult, OperationError> {
1081        match &ae.step {
1083            AuthEventStep::Init(init) => {
1084                let sessionid = uuid_from_duration(ct, self.sid);
1087
1088                let euuid = self.qs_read.name_to_uuid(init.username.as_str())?;
1104
1105                let entry = self.qs_read.internal_search_uuid(euuid)?;
1107
1108                security_info!(
1109                    username = %init.username,
1110                    issue = ?init.issue,
1111                    privileged = ?init.privileged,
1112                    uuid = %euuid,
1113                    "Initiating Authentication Session",
1114                );
1115
1116                let (account, account_policy) =
1121                    Account::try_from_entry_with_policy(entry.as_ref(), &mut self.qs_read)?;
1122
1123                trace!(?account.primary);
1124
1125                let _session_ticket = self.session_ticket.acquire().await;
1127
1128                let _maybe_slock_ref =
1134                    account
1135                        .primary_cred_uuid_and_policy()
1136                        .map(|(cred_uuid, policy)| {
1137                            let mut softlock_write = self.softlocks.write();
1142                            let slock_ref: CredSoftLockMutex =
1143                                if let Some(slock_ref) = softlock_write.get(&cred_uuid) {
1144                                    slock_ref.clone()
1145                                } else {
1146                                    let slock = Arc::new(Mutex::new(CredSoftLock::new(policy)));
1148                                    softlock_write.insert(cred_uuid, slock.clone());
1149                                    slock
1150                                };
1151                            softlock_write.commit();
1152                            slock_ref
1153                        });
1154
1155                let asd: AuthSessionData = AuthSessionData {
1156                    account,
1157                    account_policy,
1158                    issue: init.issue,
1159                    webauthn: self.webauthn,
1160                    ct,
1161                    client_auth_info,
1162                };
1163
1164                let domain_keys = self.qs_read.get_domain_key_object_handle()?;
1165
1166                let (auth_session, state) = AuthSession::new(asd, init.privileged, domain_keys);
1167
1168                match auth_session {
1169                    Some(auth_session) => {
1170                        let mut session_write = self.sessions.write();
1171                        if session_write.contains_key(&sessionid) {
1172                            Err(OperationError::InvalidSessionState)
1175                        } else {
1176                            session_write.insert(sessionid, Arc::new(Mutex::new(auth_session)));
1177                            debug_assert!(session_write.get(&sessionid).is_some());
1179                            Ok(())
1180                        }?;
1181                        session_write.commit();
1182                    }
1183                    None => {
1184                        security_info!("Authentication Session Unable to begin");
1185                    }
1186                };
1187
1188                Ok(AuthResult { sessionid, state })
1189            } AuthEventStep::Begin(mech) => {
1191                let session_read = self.sessions.read();
1192                let auth_session_ref = session_read
1194                    .get(&mech.sessionid)
1196                    .cloned()
1197                    .ok_or_else(|| {
1198                        admin_error!("Invalid Session State (no present session uuid)");
1199                        OperationError::InvalidSessionState
1200                    })?;
1201
1202                let mut auth_session = auth_session_ref.lock().await;
1203
1204                let auth_result = auth_session.start_session(&mech.mech);
1206
1207                let is_valid = match auth_session.get_credential_uuid()? {
1208                    Some(cred_uuid) => {
1209                        let softlock_read = self.softlocks.read();
1212                        if let Some(slock_ref) = softlock_read.get(&cred_uuid) {
1213                            let mut slock = slock_ref.lock().await;
1214                            slock.apply_time_step(ct);
1216                            slock.is_valid()
1218                        } else {
1219                            trace!("slock not found");
1220                            false
1221                        }
1222                    }
1223                    None => true,
1224                };
1225
1226                if is_valid {
1227                    auth_result
1228                } else {
1229                    trace!("lock step begin");
1231                    auth_session.end_session("Account is temporarily locked")
1232                }
1233                .map(|aus| AuthResult {
1234                    sessionid: mech.sessionid,
1235                    state: aus,
1236                })
1237            } AuthEventStep::Cred(creds) => {
1239                let session_read = self.sessions.read();
1243                let auth_session_ref = session_read
1245                    .get(&creds.sessionid)
1247                    .cloned()
1248                    .ok_or_else(|| {
1249                        admin_error!("Invalid Session State (no present session uuid)");
1250                        OperationError::InvalidSessionState
1251                    })?;
1252
1253                let mut auth_session = auth_session_ref.lock().await;
1254
1255                let maybe_slock_ref = match auth_session.get_credential_uuid()? {
1256                    Some(cred_uuid) => {
1257                        let softlock_read = self.softlocks.read();
1258                        softlock_read.get(&cred_uuid).cloned()
1259                    }
1260                    None => None,
1261                };
1262
1263                let mut maybe_slock = if let Some(s) = maybe_slock_ref.as_ref() {
1266                    Some(s.lock().await)
1267                } else {
1268                    None
1269                };
1270
1271                let is_valid = if let Some(ref mut slock) = maybe_slock {
1272                    slock.apply_time_step(ct);
1274                    slock.is_valid()
1276                } else {
1277                    true
1279                };
1280
1281                if is_valid {
1282                    auth_session
1286                        .validate_creds(
1287                            &creds.cred,
1288                            ct,
1289                            &self.async_tx,
1290                            &self.audit_tx,
1291                            self.webauthn,
1292                            self.qs_read.pw_badlist(),
1293                        )
1294                        .inspect(|aus| {
1295                            if let AuthState::Denied(_) = aus {
1298                                if let Some(ref mut slock) = maybe_slock {
1300                                    slock.record_failure(ct);
1301                                }
1302                            };
1303                        })
1304                } else {
1305                    trace!("lock step cred");
1307                    auth_session.end_session("Account is temporarily locked")
1308                }
1309                .map(|aus| AuthResult {
1310                    sessionid: creds.sessionid,
1311                    state: aus,
1312                })
1313            } }
1315    }
1316
1317    async fn auth_with_unix_pass(
1318        &mut self,
1319        id: Uuid,
1320        cleartext: &str,
1321        ct: Duration,
1322    ) -> Result<Option<Account>, OperationError> {
1323        let entry = match self.qs_read.internal_search_uuid(id) {
1324            Ok(entry) => entry,
1325            Err(e) => {
1326                admin_error!("Failed to start auth unix -> {:?}", e);
1327                return Err(e);
1328            }
1329        };
1330
1331        let (account, acp) =
1332            Account::try_from_entry_with_policy(entry.as_ref(), &mut self.qs_read)?;
1333
1334        if !account.is_within_valid_time(ct) {
1335            security_info!("Account is expired or not yet valid.");
1336            return Ok(None);
1337        }
1338
1339        let cred = if acp.allow_primary_cred_fallback() == Some(true) {
1340            account
1341                .unix_extn()
1342                .and_then(|extn| extn.ucred())
1343                .or_else(|| account.primary())
1344        } else {
1345            account.unix_extn().and_then(|extn| extn.ucred())
1346        };
1347
1348        let (cred, cred_id, cred_slock_policy) = match cred {
1349            None => {
1350                if acp.allow_primary_cred_fallback() == Some(true) {
1351                    security_info!("Account does not have a POSIX or primary password configured.");
1352                } else {
1353                    security_info!("Account does not have a POSIX password configured.");
1354                }
1355                return Ok(None);
1356            }
1357            Some(cred) => (cred, cred.uuid, cred.softlock_policy()),
1358        };
1359
1360        let Ok(password) = cred.password_ref() else {
1362            error!("User's UNIX or primary credential is not a password, can't authenticate!");
1363            return Err(OperationError::InvalidState);
1364        };
1365
1366        let slock_ref = {
1367            let softlock_read = self.softlocks.read();
1368            if let Some(slock_ref) = softlock_read.get(&cred_id) {
1369                slock_ref.clone()
1370            } else {
1371                let _session_ticket = self.session_ticket.acquire().await;
1372                let mut softlock_write = self.softlocks.write();
1373                let slock = Arc::new(Mutex::new(CredSoftLock::new(cred_slock_policy)));
1374                softlock_write.insert(cred_id, slock.clone());
1375                softlock_write.commit();
1376                slock
1377            }
1378        };
1379
1380        let mut slock = slock_ref.lock().await;
1381
1382        slock.apply_time_step(ct);
1383
1384        if !slock.is_valid() {
1385            security_info!("Account is softlocked.");
1386            return Ok(None);
1387        }
1388
1389        let valid = password.verify(cleartext).map_err(|e| {
1391            error!(crypto_err = ?e);
1392            e.into()
1393        })?;
1394
1395        if !valid {
1396            slock.record_failure(ct);
1398
1399            return Ok(None);
1400        }
1401
1402        security_info!("Successfully authenticated with unix (or primary) password");
1403        if password.requires_upgrade() {
1404            self.async_tx
1405                .send(DelayedAction::UnixPwUpgrade(UnixPasswordUpgrade {
1406                    target_uuid: id,
1407                    existing_password: cleartext.to_string(),
1408                }))
1409                .map_err(|_| {
1410                    admin_error!("failed to queue delayed action - unix password upgrade");
1411                    OperationError::InvalidState
1412                })?;
1413        }
1414
1415        Ok(Some(account))
1416    }
1417
1418    pub async fn auth_unix(
1419        &mut self,
1420        uae: &UnixUserAuthEvent,
1421        ct: Duration,
1422    ) -> Result<Option<UnixUserToken>, OperationError> {
1423        Ok(self
1424            .auth_with_unix_pass(uae.target, &uae.cleartext, ct)
1425            .await?
1426            .and_then(|acc| acc.to_unixusertoken(ct).ok()))
1427    }
1428
1429    pub async fn auth_ldap(
1430        &mut self,
1431        lae: &LdapAuthEvent,
1432        ct: Duration,
1433    ) -> Result<Option<LdapBoundToken>, OperationError> {
1434        if lae.target == UUID_ANONYMOUS {
1435            let account_entry = self.qs_read.internal_search_uuid(lae.target).map_err(|e| {
1436                admin_error!("Failed to start auth ldap -> {:?}", e);
1437                e
1438            })?;
1439
1440            let account = Account::try_from_entry_ro(account_entry.as_ref(), &mut self.qs_read)?;
1441
1442            if !account.is_within_valid_time(ct) {
1444                security_info!("Account is not within valid time period");
1445                return Ok(None);
1446            }
1447
1448            let session_id = Uuid::new_v4();
1449            security_info!(
1450                "Starting session {} for {} {}",
1451                session_id,
1452                account.spn,
1453                account.uuid
1454            );
1455
1456            Ok(Some(LdapBoundToken {
1458                session_id,
1459                spn: account.spn,
1460                effective_session: LdapSession::UnixBind(UUID_ANONYMOUS),
1461            }))
1462        } else {
1463            if !self.qs_read.d_info.d_ldap_allow_unix_pw_bind {
1464                security_info!("Bind not allowed through Unix passwords.");
1465                return Ok(None);
1466            }
1467
1468            let auth = self
1469                .auth_with_unix_pass(lae.target, &lae.cleartext, ct)
1470                .await?;
1471
1472            match auth {
1473                Some(account) => {
1474                    let session_id = Uuid::new_v4();
1475                    security_info!(
1476                        "Starting session {} for {} {}",
1477                        session_id,
1478                        account.spn,
1479                        account.uuid
1480                    );
1481
1482                    Ok(Some(LdapBoundToken {
1483                        spn: account.spn,
1484                        session_id,
1485                        effective_session: LdapSession::UnixBind(account.uuid),
1486                    }))
1487                }
1488                None => Ok(None),
1489            }
1490        }
1491    }
1492
1493    pub async fn token_auth_ldap(
1494        &mut self,
1495        lae: &LdapTokenAuthEvent,
1496        ct: Duration,
1497    ) -> Result<Option<LdapBoundToken>, OperationError> {
1498        match self.validate_and_parse_token_to_identity_token(&lae.token, ct)? {
1499            Token::UserAuthToken(uat) => {
1500                let spn = uat.spn.clone();
1501                Ok(Some(LdapBoundToken {
1502                    session_id: uat.session_id,
1503                    spn,
1504                    effective_session: LdapSession::UserAuthToken(uat),
1505                }))
1506            }
1507            Token::ApiToken(apit, entry) => {
1508                let spn = entry
1509                    .get_ava_single_proto_string(Attribute::Spn)
1510                    .ok_or_else(|| OperationError::MissingAttribute(Attribute::Spn))?;
1511
1512                Ok(Some(LdapBoundToken {
1513                    session_id: apit.token_id,
1514                    spn,
1515                    effective_session: LdapSession::ApiToken(apit),
1516                }))
1517            }
1518        }
1519    }
1520
1521    pub fn commit(self) -> Result<(), OperationError> {
1522        Ok(())
1523    }
1524}
1525
1526impl<'a> IdmServerTransaction<'a> for IdmServerProxyReadTransaction<'a> {
1527    type QsTransactionType = QueryServerReadTransaction<'a>;
1528
1529    fn get_qs_txn(&mut self) -> &mut Self::QsTransactionType {
1530        &mut self.qs_read
1531    }
1532}
1533
1534fn gen_password_mod(
1535    cleartext: &str,
1536    crypto_policy: &CryptoPolicy,
1537) -> Result<ModifyList<ModifyInvalid>, OperationError> {
1538    let new_cred = Credential::new_password_only(crypto_policy, cleartext)?;
1539    let cred_value = Value::new_credential("unix", new_cred);
1540    Ok(ModifyList::new_purge_and_set(
1541        Attribute::UnixPassword,
1542        cred_value,
1543    ))
1544}
1545
1546fn gen_password_upgrade_mod(
1547    unix_cred: &Credential,
1548    cleartext: &str,
1549    crypto_policy: &CryptoPolicy,
1550) -> Result<Option<ModifyList<ModifyInvalid>>, OperationError> {
1551    if let Some(new_cred) = unix_cred.upgrade_password(crypto_policy, cleartext)? {
1552        let cred_value = Value::new_credential("primary", new_cred);
1553        Ok(Some(ModifyList::new_purge_and_set(
1554            Attribute::UnixPassword,
1555            cred_value,
1556        )))
1557    } else {
1558        Ok(None)
1560    }
1561}
1562
1563impl IdmServerProxyReadTransaction<'_> {
1564    pub fn jws_public_jwk(&mut self, key_id: &str) -> Result<Jwk, OperationError> {
1565        self.qs_read
1566            .get_key_providers()
1567            .get_key_object_handle(UUID_DOMAIN_INFO)
1568            .ok_or(OperationError::NoMatchingEntries)
1570            .and_then(|key_object| key_object.jws_public_jwk(key_id))
1571            .and_then(|maybe_key: Option<Jwk>| maybe_key.ok_or(OperationError::NoMatchingEntries))
1572    }
1573
1574    pub fn get_radiusauthtoken(
1575        &mut self,
1576        rate: &RadiusAuthTokenEvent,
1577        ct: Duration,
1578    ) -> Result<RadiusAuthToken, OperationError> {
1579        let account = self
1580            .qs_read
1581            .impersonate_search_ext_uuid(rate.target, &rate.ident)
1582            .and_then(|account_entry| {
1583                RadiusAccount::try_from_entry_reduced(&account_entry, &mut self.qs_read)
1584            })
1585            .map_err(|e| {
1586                admin_error!("Failed to start radius auth token {:?}", e);
1587                e
1588            })?;
1589
1590        account.to_radiusauthtoken(ct)
1591    }
1592
1593    pub fn get_unixusertoken(
1594        &mut self,
1595        uute: &UnixUserTokenEvent,
1596        ct: Duration,
1597    ) -> Result<UnixUserToken, OperationError> {
1598        let account = self
1599            .qs_read
1600            .impersonate_search_uuid(uute.target, &uute.ident)
1601            .and_then(|account_entry| Account::try_from_entry_ro(&account_entry, &mut self.qs_read))
1602            .map_err(|e| {
1603                admin_error!("Failed to start unix user token -> {:?}", e);
1604                e
1605            })?;
1606
1607        account.to_unixusertoken(ct)
1608    }
1609
1610    pub fn get_unixgrouptoken(
1611        &mut self,
1612        uute: &UnixGroupTokenEvent,
1613    ) -> Result<UnixGroupToken, OperationError> {
1614        let group = self
1615            .qs_read
1616            .impersonate_search_ext_uuid(uute.target, &uute.ident)
1617            .and_then(|e| Group::<Unix>::try_from_entry(&e))
1618            .map_err(|e| {
1619                admin_error!("Failed to start unix group token {:?}", e);
1620                e
1621            })?;
1622        Ok(group.to_unixgrouptoken())
1623    }
1624
1625    pub fn get_credentialstatus(
1626        &mut self,
1627        cse: &CredentialStatusEvent,
1628    ) -> Result<CredentialStatus, OperationError> {
1629        let account = self
1630            .qs_read
1631            .impersonate_search_ext_uuid(cse.target, &cse.ident)
1632            .and_then(|account_entry| {
1633                Account::try_from_entry_reduced(&account_entry, &mut self.qs_read)
1634            })
1635            .map_err(|e| {
1636                admin_error!("Failed to search account {:?}", e);
1637                e
1638            })?;
1639
1640        account.to_credentialstatus()
1641    }
1642}
1643
1644impl<'a> IdmServerTransaction<'a> for IdmServerProxyWriteTransaction<'a> {
1645    type QsTransactionType = QueryServerWriteTransaction<'a>;
1646
1647    fn get_qs_txn(&mut self) -> &mut Self::QsTransactionType {
1648        &mut self.qs_write
1649    }
1650}
1651
1652impl IdmServerProxyWriteTransaction<'_> {
1653    pub(crate) fn crypto_policy(&self) -> &CryptoPolicy {
1654        self.crypto_policy
1655    }
1656
1657    pub fn get_origin(&self) -> &Url {
1658        #[allow(clippy::unwrap_used)]
1659        self.webauthn.get_allowed_origins().first().unwrap()
1660    }
1661
1662    fn check_password_quality(
1663        &mut self,
1664        cleartext: &str,
1665        related_inputs: &[&str],
1666    ) -> Result<(), OperationError> {
1667        if cleartext.len() < PW_MIN_LENGTH as usize {
1673            return Err(OperationError::PasswordQuality(vec![
1674                PasswordFeedback::TooShort(PW_MIN_LENGTH),
1675            ]));
1676        }
1677
1678        let entropy = zxcvbn(cleartext, related_inputs);
1681
1682        if entropy.score() < Score::Four {
1684            let feedback: zxcvbn::feedback::Feedback = entropy
1687                .feedback()
1688                .ok_or(OperationError::InvalidState)
1689                .cloned()
1690                .inspect_err(|err| {
1691                    security_info!(?err, "zxcvbn returned no feedback when score < 3");
1692                })?;
1693
1694            security_info!(?feedback, "pw quality feedback");
1695
1696            return Err(OperationError::PasswordQuality(vec![
1699                PasswordFeedback::BadListed,
1700            ]));
1701        }
1702
1703        if self
1707            .qs_write
1708            .pw_badlist()
1709            .contains(&cleartext.to_lowercase())
1710        {
1711            security_info!("Password found in badlist, rejecting");
1712            Err(OperationError::PasswordQuality(vec![
1713                PasswordFeedback::BadListed,
1714            ]))
1715        } else {
1716            Ok(())
1717        }
1718    }
1719
1720    pub(crate) fn target_to_account(&mut self, target: Uuid) -> Result<Account, OperationError> {
1721        let account = self
1723            .qs_write
1724            .internal_search_uuid(target)
1725            .and_then(|account_entry| {
1726                Account::try_from_entry_rw(&account_entry, &mut self.qs_write)
1727            })
1728            .map_err(|e| {
1729                admin_error!("Failed to search account {:?}", e);
1730                e
1731            })?;
1732        if account.is_anonymous() {
1736            admin_warn!("Unable to convert anonymous to account during write txn");
1737            Err(OperationError::SystemProtectedObject)
1738        } else {
1739            Ok(account)
1740        }
1741    }
1742
1743    #[cfg(test)]
1744    pub(crate) fn set_account_password(
1745        &mut self,
1746        pce: &PasswordChangeEvent,
1747    ) -> Result<(), OperationError> {
1748        let account = self.target_to_account(pce.target)?;
1749
1750        let modlist = account
1752            .gen_password_mod(pce.cleartext.as_str(), self.crypto_policy)
1753            .map_err(|e| {
1754                admin_error!("Failed to generate password mod {:?}", e);
1755                e
1756            })?;
1757        trace!(?modlist, "processing change");
1758
1759        let me = self
1762            .qs_write
1763            .impersonate_modify_gen_event(
1764                &filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(pce.target))),
1766                &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(pce.target))),
1768                &modlist,
1769                &pce.ident,
1770            )
1771            .map_err(|e| {
1772                request_error!(error = ?e);
1773                e
1774            })?;
1775
1776        let mp = self
1777            .qs_write
1778            .modify_pre_apply(&me)
1779            .and_then(|opt_mp| opt_mp.ok_or(OperationError::NoMatchingEntries))
1780            .map_err(|e| {
1781                request_error!(error = ?e);
1782                e
1783            })?;
1784
1785        self.qs_write.modify_apply(mp).map_err(|e| {
1790            request_error!(error = ?e);
1791            e
1792        })?;
1793
1794        Ok(())
1795    }
1796
1797    pub fn set_unix_account_password(
1798        &mut self,
1799        pce: &UnixPasswordChangeEvent,
1800    ) -> Result<(), OperationError> {
1801        let account = self
1803            .qs_write
1804            .internal_search_uuid(pce.target)
1805            .and_then(|account_entry| {
1806                Account::try_from_entry_rw(&account_entry, &mut self.qs_write)
1808            })
1809            .map_err(|e| {
1810                admin_error!("Failed to start set unix account password {:?}", e);
1811                e
1812            })?;
1813
1814        if account.unix_extn().is_none() {
1816            return Err(OperationError::MissingClass(
1817                ENTRYCLASS_POSIX_ACCOUNT.into(),
1818            ));
1819        }
1820
1821        if account.is_anonymous() {
1823            trace!("Unable to use anonymous to change UNIX account password");
1824            return Err(OperationError::SystemProtectedObject);
1825        }
1826
1827        let modlist =
1828            gen_password_mod(pce.cleartext.as_str(), self.crypto_policy).map_err(|e| {
1829                admin_error!(?e, "Unable to generate password change modlist");
1830                e
1831            })?;
1832        trace!(?modlist, "processing change");
1833
1834        let me = self
1837            .qs_write
1838            .impersonate_modify_gen_event(
1839                &filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(pce.target))),
1841                &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(pce.target))),
1843                &modlist,
1844                &pce.ident,
1845            )
1846            .map_err(|e| {
1847                request_error!(error = ?e);
1848                e
1849            })?;
1850
1851        let mp = self
1852            .qs_write
1853            .modify_pre_apply(&me)
1854            .and_then(|opt_mp| opt_mp.ok_or(OperationError::NoMatchingEntries))
1855            .map_err(|e| {
1856                request_error!(error = ?e);
1857                e
1858            })?;
1859
1860        self.check_password_quality(pce.cleartext.as_str(), account.related_inputs().as_slice())
1864            .map_err(|e| {
1865                admin_error!(?e, "Failed to checked password quality");
1866                e
1867            })?;
1868
1869        self.qs_write.modify_apply(mp).map_err(|e| {
1871            request_error!(error = ?e);
1872            e
1873        })?;
1874
1875        Ok(())
1876    }
1877
1878    #[instrument(level = "debug", skip_all)]
1879    pub fn recover_account(
1880        &mut self,
1881        name: &str,
1882        cleartext: Option<&str>,
1883    ) -> Result<String, OperationError> {
1884        let target = self.qs_write.name_to_uuid(name).inspect_err(|err| {
1886            error!(?err, "name to uuid failed");
1887        })?;
1888
1889        let cleartext = cleartext
1890            .map(|s| s.to_string())
1891            .unwrap_or_else(password_from_random);
1892
1893        let ncred = Credential::new_generatedpassword_only(self.crypto_policy, &cleartext)
1894            .inspect_err(|err| {
1895                error!(?err, "unable to generate password modification");
1896            })?;
1897        let vcred = Value::new_credential("primary", ncred);
1898        let v_valid_from = Value::new_datetime_epoch(self.qs_write.get_curtime());
1899
1900        let modlist = ModifyList::new_list(vec![
1901            m_purge(Attribute::AccountExpire),
1903            m_purge(Attribute::AccountValidFrom),
1904            Modify::Present(Attribute::AccountValidFrom, v_valid_from),
1905            m_purge(Attribute::PassKeys),
1907            m_purge(Attribute::PrimaryCredential),
1908            Modify::Present(Attribute::PrimaryCredential, vcred),
1909        ]);
1910
1911        trace!(?modlist, "processing change");
1912
1913        self.qs_write
1914            .internal_modify(
1915                &filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(target))),
1917                &modlist,
1918            )
1919            .inspect_err(|err| {
1920                error!(?err);
1921            })?;
1922
1923        Ok(cleartext)
1924    }
1925
1926    #[instrument(level = "debug", skip(self))]
1927    pub fn disable_account(&mut self, name: &str) -> Result<(), OperationError> {
1928        let target = self.qs_write.name_to_uuid(name).inspect_err(|err| {
1930            error!(?err, "name to uuid failed");
1931        })?;
1932
1933        let v_expire = Value::new_datetime_epoch(self.qs_write.get_curtime());
1934
1935        let modlist = ModifyList::new_list(vec![
1936            m_purge(Attribute::AccountValidFrom),
1938            m_purge(Attribute::AccountExpire),
1939            Modify::Present(Attribute::AccountExpire, v_expire),
1940        ]);
1941
1942        trace!(?modlist, "processing change");
1943
1944        self.qs_write
1945            .internal_modify(
1946                &filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(target))),
1948                &modlist,
1949            )
1950            .inspect_err(|err| {
1951                error!(?err);
1952            })?;
1953
1954        Ok(())
1955    }
1956
1957    #[instrument(level = "debug", skip_all)]
1958    pub fn regenerate_radius_secret(
1959        &mut self,
1960        rrse: &RegenerateRadiusSecretEvent,
1961    ) -> Result<String, OperationError> {
1962        let account = self.target_to_account(rrse.target)?;
1963
1964        let cleartext = readable_password_from_random();
1967
1968        let modlist = account
1970            .regenerate_radius_secret_mod(cleartext.as_str())
1971            .map_err(|e| {
1972                admin_error!("Unable to generate radius secret mod {:?}", e);
1973                e
1974            })?;
1975        trace!(?modlist, "processing change");
1976
1977        self.qs_write
1979            .impersonate_modify(
1980                &filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(rrse.target))),
1982                &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(rrse.target))),
1984                &modlist,
1985                &rrse.ident,
1987            )
1988            .map_err(|e| {
1989                request_error!(error = ?e);
1990                e
1991            })
1992            .map(|_| cleartext)
1993    }
1994
1995    #[instrument(level = "debug", skip_all)]
1997    fn process_pwupgrade(&mut self, pwu: &PasswordUpgrade) -> Result<(), OperationError> {
1998        let account = self.target_to_account(pwu.target_uuid)?;
2000
2001        info!(session_id = %pwu.target_uuid, "Processing password hash upgrade");
2002
2003        let maybe_modlist = account
2004            .gen_password_upgrade_mod(pwu.existing_password.as_str(), self.crypto_policy)
2005            .map_err(|e| {
2006                admin_error!("Unable to generate password mod {:?}", e);
2007                e
2008            })?;
2009
2010        if let Some(modlist) = maybe_modlist {
2011            self.qs_write.internal_modify(
2012                &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(pwu.target_uuid))),
2013                &modlist,
2014            )
2015        } else {
2016            Ok(())
2018        }
2019    }
2020
2021    #[instrument(level = "debug", skip_all)]
2022    fn process_unixpwupgrade(&mut self, pwu: &UnixPasswordUpgrade) -> Result<(), OperationError> {
2023        info!(session_id = %pwu.target_uuid, "Processing unix password hash upgrade");
2024
2025        let account = self
2026            .qs_write
2027            .internal_search_uuid(pwu.target_uuid)
2028            .and_then(|account_entry| {
2029                Account::try_from_entry_rw(&account_entry, &mut self.qs_write)
2030            })
2031            .map_err(|e| {
2032                admin_error!("Failed to start unix pw upgrade -> {:?}", e);
2033                e
2034            })?;
2035
2036        let cred = match account.unix_extn() {
2037            Some(ue) => ue.ucred(),
2038            None => {
2039                return Err(OperationError::MissingClass(
2040                    ENTRYCLASS_POSIX_ACCOUNT.into(),
2041                ));
2042            }
2043        };
2044
2045        let Some(cred) = cred else {
2047            return Ok(());
2048        };
2049
2050        let maybe_modlist =
2051            gen_password_upgrade_mod(cred, pwu.existing_password.as_str(), self.crypto_policy)?;
2052
2053        match maybe_modlist {
2054            Some(modlist) => self.qs_write.internal_modify(
2055                &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(pwu.target_uuid))),
2056                &modlist,
2057            ),
2058            None => Ok(()),
2059        }
2060    }
2061
2062    #[instrument(level = "debug", skip_all)]
2063    pub(crate) fn process_webauthncounterinc(
2064        &mut self,
2065        wci: &WebauthnCounterIncrement,
2066    ) -> Result<(), OperationError> {
2067        info!(session_id = %wci.target_uuid, "Processing webauthn counter increment");
2068
2069        let mut account = self.target_to_account(wci.target_uuid)?;
2070
2071        let opt_modlist = account
2073            .gen_webauthn_counter_mod(&wci.auth_result)
2074            .map_err(|e| {
2075                admin_error!("Unable to generate webauthn counter mod {:?}", e);
2076                e
2077            })?;
2078
2079        if let Some(modlist) = opt_modlist {
2080            self.qs_write.internal_modify(
2081                &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(wci.target_uuid))),
2082                &modlist,
2083            )
2084        } else {
2085            trace!("No modification required");
2087            Ok(())
2088        }
2089    }
2090
2091    #[instrument(level = "debug", skip_all)]
2092    pub(crate) fn process_backupcoderemoval(
2093        &mut self,
2094        bcr: &BackupCodeRemoval,
2095    ) -> Result<(), OperationError> {
2096        info!(session_id = %bcr.target_uuid, "Processing backup code removal");
2097
2098        let account = self.target_to_account(bcr.target_uuid)?;
2099        let modlist = account
2101            .invalidate_backup_code_mod(&bcr.code_to_remove)
2102            .map_err(|e| {
2103                admin_error!("Unable to generate backup code mod {:?}", e);
2104                e
2105            })?;
2106
2107        self.qs_write.internal_modify(
2108            &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(bcr.target_uuid))),
2109            &modlist,
2110        )
2111    }
2112
2113    #[instrument(level = "debug", skip_all)]
2114    pub(crate) fn process_authsessionrecord(
2115        &mut self,
2116        asr: &AuthSessionRecord,
2117    ) -> Result<(), OperationError> {
2118        let state = match asr.expiry {
2120            Some(e) => SessionState::ExpiresAt(e),
2121            None => SessionState::NeverExpires,
2122        };
2123
2124        let session = Value::Session(
2125            asr.session_id,
2126            Session {
2127                label: asr.label.clone(),
2128                state,
2129                issued_at: asr.issued_at,
2132                issued_by: asr.issued_by.clone(),
2134                cred_id: asr.cred_id,
2136                scope: asr.scope,
2139                type_: asr.type_,
2140            },
2141        );
2142
2143        info!(session_id = %asr.session_id, "Persisting auth session");
2144
2145        let modlist = ModifyList::new_append(Attribute::UserAuthTokenSession, session);
2147
2148        self.qs_write
2149            .internal_modify(
2150                &filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(asr.target_uuid))),
2151                &modlist,
2152            )
2153            .map_err(|e| {
2154                admin_error!("Failed to persist user auth token {:?}", e);
2155                e
2156            })
2157        }
2159
2160    #[instrument(level = "debug", skip_all)]
2161    pub fn process_delayedaction(
2162        &mut self,
2163        da: &DelayedAction,
2164        _ct: Duration,
2165    ) -> Result<(), OperationError> {
2166        match da {
2167            DelayedAction::PwUpgrade(pwu) => self.process_pwupgrade(pwu),
2168            DelayedAction::UnixPwUpgrade(upwu) => self.process_unixpwupgrade(upwu),
2169            DelayedAction::WebauthnCounterIncrement(wci) => self.process_webauthncounterinc(wci),
2170            DelayedAction::BackupCodeRemoval(bcr) => self.process_backupcoderemoval(bcr),
2171            DelayedAction::AuthSessionRecord(asr) => self.process_authsessionrecord(asr),
2172        }
2173    }
2174
2175    fn reload_applications(&mut self) -> Result<(), OperationError> {
2176        self.qs_write
2177            .get_applications_set()
2178            .and_then(|application_set| self.applications.reload(application_set))
2179    }
2180
2181    fn reload_oauth2(&mut self) -> Result<(), OperationError> {
2182        let domain_level = self.qs_write.get_domain_version();
2183        self.qs_write.get_oauth2rs_set().and_then(|oauth2rs_set| {
2184            let key_providers = self.qs_write.get_key_providers();
2185            self.oauth2rs
2186                .reload(oauth2rs_set, key_providers, domain_level)
2187        })?;
2188        self.qs_write.clear_changed_oauth2();
2190        Ok(())
2191    }
2192
2193    #[instrument(level = "debug", skip_all)]
2194    pub fn commit(mut self) -> Result<(), OperationError> {
2195        self.qs_write.reload()?;
2198
2199        if self.qs_write.get_changed_app() {
2201            self.reload_applications()?;
2202        }
2203
2204        if self.qs_write.get_changed_oauth2() {
2205            self.reload_oauth2()?;
2206        }
2207
2208        self.applications.commit();
2210        self.oauth2rs.commit();
2211        self.cred_update_sessions.commit();
2212
2213        trace!("cred_update_session.commit");
2214        self.qs_write.commit()
2215    }
2216}
2217
2218#[cfg(test)]
2221mod tests {
2222    use std::convert::TryFrom;
2223    use std::time::Duration;
2224
2225    use kanidm_proto::v1::{AuthAllowed, AuthIssueSession, AuthMech};
2226    use time::OffsetDateTime;
2227    use uuid::Uuid;
2228
2229    use crate::credential::{Credential, Password};
2230    use crate::idm::account::DestroySessionTokenEvent;
2231    use crate::idm::accountpolicy::ResolvedAccountPolicy;
2232    use crate::idm::audit::AuditEvent;
2233    use crate::idm::delayed::{AuthSessionRecord, DelayedAction};
2234    use crate::idm::event::{AuthEvent, AuthResult};
2235    use crate::idm::event::{
2236        LdapAuthEvent, PasswordChangeEvent, RadiusAuthTokenEvent, RegenerateRadiusSecretEvent,
2237        UnixGroupTokenEvent, UnixPasswordChangeEvent, UnixUserAuthEvent, UnixUserTokenEvent,
2238    };
2239
2240    use crate::idm::server::{IdmServer, IdmServerTransaction, Token};
2241    use crate::idm::AuthState;
2242    use crate::modify::{Modify, ModifyList};
2243    use crate::prelude::*;
2244    use crate::server::keys::KeyProvidersTransaction;
2245    use crate::value::{AuthType, SessionState};
2246    use compact_jwt::{traits::JwsVerifiable, JwsCompact, JwsEs256Verifier, JwsVerifier};
2247    use kanidm_lib_crypto::CryptoPolicy;
2248
2249    const TEST_PASSWORD: &str = "ntaoeuntnaoeuhraohuercahu😍";
2250    const TEST_PASSWORD_INC: &str = "ntaoentu nkrcgaeunhibwmwmqj;k wqjbkx ";
2251    const TEST_CURRENT_TIME: u64 = 6000;
2252
2253    #[idm_test]
2254    async fn test_idm_anonymous_auth(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2255        let mut idms_auth = idms.auth().await.unwrap();
2257        let anon_init = AuthEvent::anonymous_init();
2259        let r1 = idms_auth
2261            .auth(
2262                &anon_init,
2263                Duration::from_secs(TEST_CURRENT_TIME),
2264                Source::Internal.into(),
2265            )
2266            .await;
2267        let sid = match r1 {
2270            Ok(ar) => {
2271                let AuthResult { sessionid, state } = ar;
2272                match state {
2273                    AuthState::Choose(mut conts) => {
2274                        assert_eq!(conts.len(), 1);
2276                        let m = conts.pop().expect("Should not fail");
2278                        assert_eq!(m, AuthMech::Anonymous);
2279                    }
2280                    _ => {
2281                        error!("A critical error has occurred! We have a non-continue result!");
2282                        panic!();
2283                    }
2284                };
2285                sessionid
2287            }
2288            Err(e) => {
2289                error!("A critical error has occurred! {:?}", e);
2291                panic!();
2292            }
2293        };
2294
2295        debug!("sessionid is ==> {:?}", sid);
2296
2297        idms_auth.commit().expect("Must not fail");
2298
2299        let mut idms_auth = idms.auth().await.unwrap();
2300        let anon_begin = AuthEvent::begin_mech(sid, AuthMech::Anonymous);
2301
2302        let r2 = idms_auth
2303            .auth(
2304                &anon_begin,
2305                Duration::from_secs(TEST_CURRENT_TIME),
2306                Source::Internal.into(),
2307            )
2308            .await;
2309        debug!("r2 ==> {:?}", r2);
2310
2311        match r2 {
2312            Ok(ar) => {
2313                let AuthResult {
2314                    sessionid: _,
2315                    state,
2316                } = ar;
2317
2318                match state {
2319                    AuthState::Continue(allowed) => {
2320                        assert_eq!(allowed.len(), 1);
2322                        assert_eq!(allowed.first(), Some(&AuthAllowed::Anonymous));
2323                    }
2324                    _ => {
2325                        error!("A critical error has occurred! We have a non-continue result!");
2326                        panic!();
2327                    }
2328                }
2329            }
2330            Err(e) => {
2331                error!("A critical error has occurred! {:?}", e);
2332                panic!();
2334            }
2335        };
2336
2337        idms_auth.commit().expect("Must not fail");
2338
2339        let mut idms_auth = idms.auth().await.unwrap();
2340        let anon_step = AuthEvent::cred_step_anonymous(sid);
2342
2343        let r2 = idms_auth
2345            .auth(
2346                &anon_step,
2347                Duration::from_secs(TEST_CURRENT_TIME),
2348                Source::Internal.into(),
2349            )
2350            .await;
2351        debug!("r2 ==> {:?}", r2);
2352
2353        match r2 {
2354            Ok(ar) => {
2355                let AuthResult {
2356                    sessionid: _,
2357                    state,
2358                } = ar;
2359
2360                match state {
2361                    AuthState::Success(_uat, AuthIssueSession::Token) => {
2362                        }
2364                    _ => {
2365                        error!("A critical error has occurred! We have a non-success result!");
2366                        panic!();
2367                    }
2368                }
2369            }
2370            Err(e) => {
2371                error!("A critical error has occurred! {:?}", e);
2372                panic!();
2374            }
2375        };
2376
2377        idms_auth.commit().expect("Must not fail");
2378    }
2379
2380    #[idm_test]
2382    async fn test_idm_anonymous_auth_invalid_states(
2383        idms: &IdmServer,
2384        _idms_delayed: &IdmServerDelayed,
2385    ) {
2386        {
2387            let mut idms_auth = idms.auth().await.unwrap();
2388            let sid = Uuid::new_v4();
2389            let anon_step = AuthEvent::cred_step_anonymous(sid);
2390
2391            let r2 = idms_auth
2393                .auth(
2394                    &anon_step,
2395                    Duration::from_secs(TEST_CURRENT_TIME),
2396                    Source::Internal.into(),
2397                )
2398                .await;
2399            debug!("r2 ==> {:?}", r2);
2400
2401            match r2 {
2402                Ok(_) => {
2403                    error!("Auth state machine not correctly enforced!");
2404                    panic!();
2405                }
2406                Err(e) => match e {
2407                    OperationError::InvalidSessionState => {}
2408                    _ => panic!(),
2409                },
2410            };
2411        }
2412    }
2413
2414    async fn init_testperson_w_password(
2415        idms: &IdmServer,
2416        pw: &str,
2417    ) -> Result<Uuid, OperationError> {
2418        let p = CryptoPolicy::minimum();
2419        let cred = Credential::new_password_only(&p, pw)?;
2420        let cred_id = cred.uuid;
2421        let v_cred = Value::new_credential("primary", cred);
2422        let mut idms_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2423
2424        idms_write
2425            .qs_write
2426            .internal_create(vec![E_TESTPERSON_1.clone()])
2427            .expect("Failed to create test person");
2428
2429        let me_inv_m = ModifyEvent::new_internal_invalid(
2431            filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_TESTPERSON_1))),
2432            ModifyList::new_list(vec![Modify::Present(Attribute::PrimaryCredential, v_cred)]),
2433        );
2434        assert!(idms_write.qs_write.modify(&me_inv_m).is_ok());
2436
2437        idms_write.commit().map(|()| cred_id)
2438    }
2439
2440    async fn init_authsession_sid(idms: &IdmServer, ct: Duration, name: &str) -> Uuid {
2441        let mut idms_auth = idms.auth().await.unwrap();
2442        let admin_init = AuthEvent::named_init(name);
2443
2444        let r1 = idms_auth
2445            .auth(&admin_init, ct, Source::Internal.into())
2446            .await;
2447        let ar = r1.unwrap();
2448        let AuthResult { sessionid, state } = ar;
2449
2450        assert!(matches!(state, AuthState::Choose(_)));
2451
2452        let admin_begin = AuthEvent::begin_mech(sessionid, AuthMech::Password);
2454
2455        let r2 = idms_auth
2456            .auth(&admin_begin, ct, Source::Internal.into())
2457            .await;
2458        let ar = r2.unwrap();
2459        let AuthResult { sessionid, state } = ar;
2460
2461        match state {
2462            AuthState::Continue(_) => {}
2463            s => {
2464                error!(?s, "Sessions was not initialised");
2465                panic!();
2466            }
2467        };
2468
2469        idms_auth.commit().expect("Must not fail");
2470
2471        sessionid
2472    }
2473
2474    async fn check_testperson_password(idms: &IdmServer, pw: &str, ct: Duration) -> JwsCompact {
2475        let sid = init_authsession_sid(idms, ct, "testperson1").await;
2476
2477        let mut idms_auth = idms.auth().await.unwrap();
2478        let anon_step = AuthEvent::cred_step_password(sid, pw);
2479
2480        let r2 = idms_auth
2482            .auth(&anon_step, ct, Source::Internal.into())
2483            .await;
2484        debug!("r2 ==> {:?}", r2);
2485
2486        let token = match r2 {
2487            Ok(ar) => {
2488                let AuthResult {
2489                    sessionid: _,
2490                    state,
2491                } = ar;
2492
2493                match state {
2494                    AuthState::Success(token, AuthIssueSession::Token) => {
2495                        token
2497                    }
2498                    _ => {
2499                        error!("A critical error has occurred! We have a non-success result!");
2500                        panic!();
2501                    }
2502                }
2503            }
2504            Err(e) => {
2505                error!("A critical error has occurred! {:?}", e);
2506                panic!();
2508            }
2509        };
2510
2511        idms_auth.commit().expect("Must not fail");
2512
2513        *token
2514    }
2515
2516    #[idm_test]
2517    async fn test_idm_simple_password_auth(idms: &IdmServer, idms_delayed: &mut IdmServerDelayed) {
2518        let ct = duration_from_epoch_now();
2519        init_testperson_w_password(idms, TEST_PASSWORD)
2520            .await
2521            .expect("Failed to setup admin account");
2522        check_testperson_password(idms, TEST_PASSWORD, ct).await;
2523
2524        let da = idms_delayed.try_recv().expect("invalid");
2526        assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
2527        idms_delayed.check_is_empty_or_panic();
2528    }
2529
2530    #[idm_test]
2531    async fn test_idm_simple_password_spn_auth(
2532        idms: &IdmServer,
2533        idms_delayed: &mut IdmServerDelayed,
2534    ) {
2535        init_testperson_w_password(idms, TEST_PASSWORD)
2536            .await
2537            .expect("Failed to setup admin account");
2538
2539        let sid = init_authsession_sid(
2540            idms,
2541            Duration::from_secs(TEST_CURRENT_TIME),
2542            "testperson1@example.com",
2543        )
2544        .await;
2545
2546        let mut idms_auth = idms.auth().await.unwrap();
2547        let anon_step = AuthEvent::cred_step_password(sid, TEST_PASSWORD);
2548
2549        let r2 = idms_auth
2551            .auth(
2552                &anon_step,
2553                Duration::from_secs(TEST_CURRENT_TIME),
2554                Source::Internal.into(),
2555            )
2556            .await;
2557        debug!("r2 ==> {:?}", r2);
2558
2559        match r2 {
2560            Ok(ar) => {
2561                let AuthResult {
2562                    sessionid: _,
2563                    state,
2564                } = ar;
2565                match state {
2566                    AuthState::Success(_uat, AuthIssueSession::Token) => {
2567                        }
2569                    _ => {
2570                        error!("A critical error has occurred! We have a non-success result!");
2571                        panic!();
2572                    }
2573                }
2574            }
2575            Err(e) => {
2576                error!("A critical error has occurred! {:?}", e);
2577                panic!();
2579            }
2580        };
2581
2582        let da = idms_delayed.try_recv().expect("invalid");
2584        assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
2585        idms_delayed.check_is_empty_or_panic();
2586
2587        idms_auth.commit().expect("Must not fail");
2588    }
2589
2590    #[idm_test(audit = 1)]
2591    async fn test_idm_simple_password_invalid(
2592        idms: &IdmServer,
2593        _idms_delayed: &IdmServerDelayed,
2594        idms_audit: &mut IdmServerAudit,
2595    ) {
2596        init_testperson_w_password(idms, TEST_PASSWORD)
2597            .await
2598            .expect("Failed to setup admin account");
2599        let sid =
2600            init_authsession_sid(idms, Duration::from_secs(TEST_CURRENT_TIME), "testperson1").await;
2601        let mut idms_auth = idms.auth().await.unwrap();
2602        let anon_step = AuthEvent::cred_step_password(sid, TEST_PASSWORD_INC);
2603
2604        let r2 = idms_auth
2606            .auth(
2607                &anon_step,
2608                Duration::from_secs(TEST_CURRENT_TIME),
2609                Source::Internal.into(),
2610            )
2611            .await;
2612        debug!("r2 ==> {:?}", r2);
2613
2614        match r2 {
2615            Ok(ar) => {
2616                let AuthResult {
2617                    sessionid: _,
2618                    state,
2619                } = ar;
2620                match state {
2621                    AuthState::Denied(_reason) => {
2622                        }
2624                    _ => {
2625                        error!("A critical error has occurred! We have a non-denied result!");
2626                        panic!();
2627                    }
2628                }
2629            }
2630            Err(e) => {
2631                error!("A critical error has occurred! {:?}", e);
2632                panic!();
2634            }
2635        };
2636
2637        match idms_audit.audit_rx().try_recv() {
2639            Ok(AuditEvent::AuthenticationDenied { .. }) => {}
2640            _ => panic!("Oh no"),
2641        }
2642
2643        idms_auth.commit().expect("Must not fail");
2644    }
2645
2646    #[idm_test]
2647    async fn test_idm_simple_password_reset(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2648        let pce = PasswordChangeEvent::new_internal(UUID_ADMIN, TEST_PASSWORD);
2649
2650        let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2651        assert!(idms_prox_write.set_account_password(&pce).is_ok());
2652        assert!(idms_prox_write.set_account_password(&pce).is_ok());
2653        assert!(idms_prox_write.commit().is_ok());
2654    }
2655
2656    #[idm_test]
2657    async fn test_idm_anonymous_set_password_denied(
2658        idms: &IdmServer,
2659        _idms_delayed: &IdmServerDelayed,
2660    ) {
2661        let pce = PasswordChangeEvent::new_internal(UUID_ANONYMOUS, TEST_PASSWORD);
2662
2663        let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2664        assert!(idms_prox_write.set_account_password(&pce).is_err());
2665        assert!(idms_prox_write.commit().is_ok());
2666    }
2667
2668    #[idm_test]
2669    async fn test_idm_regenerate_radius_secret(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2670        let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2671
2672        idms_prox_write
2673            .qs_write
2674            .internal_create(vec![E_TESTPERSON_1.clone()])
2675            .expect("unable to create test person");
2676
2677        let rrse = RegenerateRadiusSecretEvent::new_internal(UUID_TESTPERSON_1);
2678
2679        let r1 = idms_prox_write
2681            .regenerate_radius_secret(&rrse)
2682            .expect("Failed to reset radius credential 1");
2683        let r2 = idms_prox_write
2685            .regenerate_radius_secret(&rrse)
2686            .expect("Failed to reset radius credential 2");
2687        assert!(r1 != r2);
2688    }
2689
2690    #[idm_test]
2691    async fn test_idm_radiusauthtoken(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2692        let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2693
2694        idms_prox_write
2695            .qs_write
2696            .internal_create(vec![E_TESTPERSON_1.clone()])
2697            .expect("unable to create test person");
2698
2699        let rrse = RegenerateRadiusSecretEvent::new_internal(UUID_TESTPERSON_1);
2700        let r1 = idms_prox_write
2701            .regenerate_radius_secret(&rrse)
2702            .expect("Failed to reset radius credential 1");
2703        idms_prox_write.commit().expect("failed to commit");
2704
2705        let mut idms_prox_read = idms.proxy_read().await.unwrap();
2706        let person_entry = idms_prox_read
2707            .qs_read
2708            .internal_search_uuid(UUID_TESTPERSON_1)
2709            .expect("Can't access admin entry.");
2710
2711        let rate = RadiusAuthTokenEvent::new_impersonate(person_entry, UUID_TESTPERSON_1);
2712        let tok_r = idms_prox_read
2713            .get_radiusauthtoken(&rate, duration_from_epoch_now())
2714            .expect("Failed to generate radius auth token");
2715
2716        assert_eq!(r1, tok_r.secret);
2718    }
2719
2720    #[idm_test]
2721    async fn test_idm_unixusertoken(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2722        let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2723        let me_posix = ModifyEvent::new_internal_invalid(
2725            filter!(f_eq(Attribute::Name, PartialValue::new_iname("admin"))),
2726            ModifyList::new_list(vec![
2727                Modify::Present(Attribute::Class, EntryClass::PosixAccount.into()),
2728                Modify::Present(Attribute::GidNumber, Value::new_uint32(2001)),
2729            ]),
2730        );
2731        assert!(idms_prox_write.qs_write.modify(&me_posix).is_ok());
2732        let e: Entry<EntryInit, EntryNew> = entry_init!(
2734            (Attribute::Class, EntryClass::Object.to_value()),
2735            (Attribute::Class, EntryClass::Group.to_value()),
2736            (Attribute::Class, EntryClass::PosixGroup.to_value()),
2737            (Attribute::Name, Value::new_iname("testgroup")),
2738            (
2739                Attribute::Uuid,
2740                Value::Uuid(uuid::uuid!("01609135-a1c4-43d5-966b-a28227644445"))
2741            ),
2742            (Attribute::Description, Value::new_utf8s("testgroup")),
2743            (
2744                Attribute::Member,
2745                Value::Refer(uuid::uuid!("00000000-0000-0000-0000-000000000000"))
2746            )
2747        );
2748
2749        let ce = CreateEvent::new_internal(vec![e]);
2750
2751        assert!(idms_prox_write.qs_write.create(&ce).is_ok());
2752
2753        idms_prox_write.commit().expect("failed to commit");
2754
2755        let mut idms_prox_read = idms.proxy_read().await.unwrap();
2756
2757        let admin_entry = idms_prox_read
2759            .qs_read
2760            .internal_search_uuid(UUID_ADMIN)
2761            .expect("Can't access admin entry.");
2762
2763        let ugte = UnixGroupTokenEvent::new_impersonate(
2764            admin_entry.clone(),
2765            uuid!("01609135-a1c4-43d5-966b-a28227644445"),
2766        );
2767        let tok_g = idms_prox_read
2768            .get_unixgrouptoken(&ugte)
2769            .expect("Failed to generate unix group token");
2770
2771        assert_eq!(tok_g.name, "testgroup");
2772        assert_eq!(tok_g.spn, "testgroup@example.com");
2773
2774        let uute = UnixUserTokenEvent::new_internal(UUID_ADMIN);
2775        let tok_r = idms_prox_read
2776            .get_unixusertoken(&uute, duration_from_epoch_now())
2777            .expect("Failed to generate unix user token");
2778
2779        assert_eq!(tok_r.name, "admin");
2780        assert_eq!(tok_r.spn, "admin@example.com");
2781        assert_eq!(tok_r.groups.len(), 2);
2782        assert_eq!(tok_r.groups[0].name, "admin");
2783        assert_eq!(tok_r.groups[1].name, "testgroup");
2784        assert!(tok_r.valid);
2785
2786        let ugte = UnixGroupTokenEvent::new_impersonate(
2788            admin_entry,
2789            uuid!("00000000-0000-0000-0000-000000000000"),
2790        );
2791        let tok_g = idms_prox_read
2792            .get_unixgrouptoken(&ugte)
2793            .expect("Failed to generate unix group token");
2794
2795        assert_eq!(tok_g.name, "admin");
2796        assert_eq!(tok_g.spn, "admin@example.com");
2797    }
2798
2799    #[idm_test]
2800    async fn test_idm_simple_unix_password_reset(
2801        idms: &IdmServer,
2802        _idms_delayed: &IdmServerDelayed,
2803    ) {
2804        let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2805        let me_posix = ModifyEvent::new_internal_invalid(
2807            filter!(f_eq(Attribute::Name, PartialValue::new_iname("admin"))),
2808            ModifyList::new_list(vec![
2809                Modify::Present(Attribute::Class, EntryClass::PosixAccount.into()),
2810                Modify::Present(Attribute::GidNumber, Value::new_uint32(2001)),
2811            ]),
2812        );
2813        assert!(idms_prox_write.qs_write.modify(&me_posix).is_ok());
2814
2815        let pce = UnixPasswordChangeEvent::new_internal(UUID_ADMIN, TEST_PASSWORD);
2816
2817        assert!(idms_prox_write.set_unix_account_password(&pce).is_ok());
2818        assert!(idms_prox_write.commit().is_ok());
2819
2820        let mut idms_auth = idms.auth().await.unwrap();
2821        let uuae_good = UnixUserAuthEvent::new_internal(UUID_ADMIN, TEST_PASSWORD);
2824        let a1 = idms_auth
2825            .auth_unix(&uuae_good, Duration::from_secs(TEST_CURRENT_TIME))
2826            .await;
2827        match a1 {
2828            Ok(Some(_tok)) => {}
2829            _ => panic!("Oh no"),
2830        };
2831        let uuae_bad = UnixUserAuthEvent::new_internal(UUID_ADMIN, TEST_PASSWORD_INC);
2833        let a2 = idms_auth
2834            .auth_unix(&uuae_bad, Duration::from_secs(TEST_CURRENT_TIME))
2835            .await;
2836        match a2 {
2837            Ok(None) => {}
2838            _ => panic!("Oh no"),
2839        };
2840        assert!(idms_auth.commit().is_ok());
2841
2842        let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2844        let me_purge_up = ModifyEvent::new_internal_invalid(
2845            filter!(f_eq(Attribute::Name, PartialValue::new_iname("admin"))),
2846            ModifyList::new_list(vec![Modify::Purged(Attribute::UnixPassword)]),
2847        );
2848        assert!(idms_prox_write.qs_write.modify(&me_purge_up).is_ok());
2849        assert!(idms_prox_write.commit().is_ok());
2850
2851        let mut idms_auth = idms.auth().await.unwrap();
2854        let a3 = idms_auth
2855            .auth_unix(&uuae_good, Duration::from_secs(TEST_CURRENT_TIME))
2856            .await;
2857        match a3 {
2858            Ok(None) => {}
2859            _ => panic!("Oh no"),
2860        };
2861        assert!(idms_auth.commit().is_ok());
2862    }
2863
2864    #[idm_test]
2865    async fn test_idm_simple_password_upgrade(
2866        idms: &IdmServer,
2867        idms_delayed: &mut IdmServerDelayed,
2868    ) {
2869        let ct = duration_from_epoch_now();
2870        idms_delayed.check_is_empty_or_panic();
2872        {
2874            let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
2875            idms_prox_write
2878                .qs_write
2879                .internal_create(vec![E_TESTPERSON_1.clone()])
2880                .expect("Failed to create test person");
2881
2882            let me_inv_m =
2883                ModifyEvent::new_internal_invalid(
2884                        filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_TESTPERSON_1))),
2885                        ModifyList::new_list(vec![Modify::Present(
2886                            Attribute::PasswordImport,
2887                            Value::from("{SSHA512}JwrSUHkI7FTAfHRVR6KoFlSN0E3dmaQWARjZ+/UsShYlENOqDtFVU77HJLLrY2MuSp0jve52+pwtdVl2QUAHukQ0XUf5LDtM")
2888                        )]),
2889                    );
2890            assert!(idms_prox_write.qs_write.modify(&me_inv_m).is_ok());
2892            assert!(idms_prox_write.commit().is_ok());
2893        }
2894        idms_delayed.check_is_empty_or_panic();
2896
2897        let mut idms_prox_read = idms.proxy_read().await.unwrap();
2898        let person_entry = idms_prox_read
2899            .qs_read
2900            .internal_search_uuid(UUID_TESTPERSON_1)
2901            .expect("Can't access admin entry.");
2902        let cred_before = person_entry
2903            .get_ava_single_credential(Attribute::PrimaryCredential)
2904            .expect("No credential present")
2905            .clone();
2906        drop(idms_prox_read);
2907
2908        check_testperson_password(idms, "password", ct).await;
2910
2911        let da = idms_delayed.try_recv().expect("invalid");
2920        assert!(matches!(da, DelayedAction::PwUpgrade(_)));
2922        let r = idms.delayed_action(duration_from_epoch_now(), da).await;
2923        let da = idms_delayed.try_recv().expect("invalid");
2925        assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
2926        assert_eq!(Ok(true), r);
2927
2928        let mut idms_prox_read = idms.proxy_read().await.unwrap();
2929        let person_entry = idms_prox_read
2930            .qs_read
2931            .internal_search_uuid(UUID_TESTPERSON_1)
2932            .expect("Can't access admin entry.");
2933        let cred_after = person_entry
2934            .get_ava_single_credential(Attribute::PrimaryCredential)
2935            .expect("No credential present")
2936            .clone();
2937        drop(idms_prox_read);
2938
2939        assert_eq!(cred_before.uuid, cred_after.uuid);
2940
2941        check_testperson_password(idms, "password", ct).await;
2943        let da = idms_delayed.try_recv().expect("invalid");
2945        assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
2946
2947        idms_delayed.check_is_empty_or_panic();
2949    }
2950
2951    #[idm_test]
2952    async fn test_idm_unix_password_upgrade(idms: &IdmServer, idms_delayed: &mut IdmServerDelayed) {
2953        idms_delayed.check_is_empty_or_panic();
2955        let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2957
2958        let im_pw = "{SSHA512}JwrSUHkI7FTAfHRVR6KoFlSN0E3dmaQWARjZ+/UsShYlENOqDtFVU77HJLLrY2MuSp0jve52+pwtdVl2QUAHukQ0XUf5LDtM";
2959        let pw = Password::try_from(im_pw).expect("failed to parse");
2960        let cred = Credential::new_from_password(pw);
2961        let v_cred = Value::new_credential("unix", cred);
2962
2963        let me_posix = ModifyEvent::new_internal_invalid(
2964            filter!(f_eq(Attribute::Name, PartialValue::new_iname("admin"))),
2965            ModifyList::new_list(vec![
2966                Modify::Present(Attribute::Class, EntryClass::PosixAccount.into()),
2967                Modify::Present(Attribute::GidNumber, Value::new_uint32(2001)),
2968                Modify::Present(Attribute::UnixPassword, v_cred),
2969            ]),
2970        );
2971        assert!(idms_prox_write.qs_write.modify(&me_posix).is_ok());
2972        assert!(idms_prox_write.commit().is_ok());
2973        idms_delayed.check_is_empty_or_panic();
2974        let uuae = UnixUserAuthEvent::new_internal(UUID_ADMIN, "password");
2976        let mut idms_auth = idms.auth().await.unwrap();
2977        let a1 = idms_auth
2978            .auth_unix(&uuae, Duration::from_secs(TEST_CURRENT_TIME))
2979            .await;
2980        match a1 {
2981            Ok(Some(_tok)) => {}
2982            _ => panic!("Oh no"),
2983        };
2984        idms_auth.commit().expect("Must not fail");
2985        let da = idms_delayed.try_recv().expect("invalid");
2988        let _r = idms.delayed_action(duration_from_epoch_now(), da).await;
2989        let mut idms_auth = idms.auth().await.unwrap();
2991        let a2 = idms_auth
2992            .auth_unix(&uuae, Duration::from_secs(TEST_CURRENT_TIME))
2993            .await;
2994        match a2 {
2995            Ok(Some(_tok)) => {}
2996            _ => panic!("Oh no"),
2997        };
2998        idms_auth.commit().expect("Must not fail");
2999        idms_delayed.check_is_empty_or_panic();
3001    }
3002
3003    const TEST_NOT_YET_VALID_TIME: u64 = TEST_CURRENT_TIME - 240;
3007    const TEST_VALID_FROM_TIME: u64 = TEST_CURRENT_TIME - 120;
3008    const TEST_EXPIRE_TIME: u64 = TEST_CURRENT_TIME + 120;
3009    const TEST_AFTER_EXPIRY: u64 = TEST_CURRENT_TIME + 240;
3010
3011    async fn set_testperson_valid_time(idms: &IdmServer) {
3012        let mut idms_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
3013
3014        let v_valid_from = Value::new_datetime_epoch(Duration::from_secs(TEST_VALID_FROM_TIME));
3015        let v_expire = Value::new_datetime_epoch(Duration::from_secs(TEST_EXPIRE_TIME));
3016
3017        let me_inv_m = ModifyEvent::new_internal_invalid(
3019            filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_TESTPERSON_1))),
3020            ModifyList::new_list(vec![
3021                Modify::Present(Attribute::AccountExpire, v_expire),
3022                Modify::Present(Attribute::AccountValidFrom, v_valid_from),
3023            ]),
3024        );
3025        assert!(idms_write.qs_write.modify(&me_inv_m).is_ok());
3027
3028        idms_write.commit().expect("Must not fail");
3029    }
3030
3031    #[idm_test]
3032    async fn test_idm_account_valid_from_expire(
3033        idms: &IdmServer,
3034        _idms_delayed: &mut IdmServerDelayed,
3035    ) {
3036        init_testperson_w_password(idms, TEST_PASSWORD)
3039            .await
3040            .expect("Failed to setup admin account");
3041        set_testperson_valid_time(idms).await;
3044
3045        let time_low = Duration::from_secs(TEST_NOT_YET_VALID_TIME);
3046        let time_high = Duration::from_secs(TEST_AFTER_EXPIRY);
3047
3048        let mut idms_auth = idms.auth().await.unwrap();
3049        let admin_init = AuthEvent::named_init("admin");
3050        let r1 = idms_auth
3051            .auth(&admin_init, time_low, Source::Internal.into())
3052            .await;
3053
3054        let ar = r1.unwrap();
3055        let AuthResult {
3056            sessionid: _,
3057            state,
3058        } = ar;
3059
3060        match state {
3061            AuthState::Denied(_) => {}
3062            _ => {
3063                panic!();
3064            }
3065        };
3066
3067        idms_auth.commit().expect("Must not fail");
3068
3069        let mut idms_auth = idms.auth().await.unwrap();
3071        let admin_init = AuthEvent::named_init("admin");
3072        let r1 = idms_auth
3073            .auth(&admin_init, time_high, Source::Internal.into())
3074            .await;
3075
3076        let ar = r1.unwrap();
3077        let AuthResult {
3078            sessionid: _,
3079            state,
3080        } = ar;
3081
3082        match state {
3083            AuthState::Denied(_) => {}
3084            _ => {
3085                panic!();
3086            }
3087        };
3088
3089        idms_auth.commit().expect("Must not fail");
3090    }
3091
3092    #[idm_test]
3093    async fn test_idm_unix_valid_from_expire(
3094        idms: &IdmServer,
3095        _idms_delayed: &mut IdmServerDelayed,
3096    ) {
3097        init_testperson_w_password(idms, TEST_PASSWORD)
3099            .await
3100            .expect("Failed to setup admin account");
3101        set_testperson_valid_time(idms).await;
3102
3103        let time_low = Duration::from_secs(TEST_NOT_YET_VALID_TIME);
3104        let time_high = Duration::from_secs(TEST_AFTER_EXPIRY);
3105
3106        let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
3108        let me_posix = ModifyEvent::new_internal_invalid(
3109            filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_TESTPERSON_1))),
3110            ModifyList::new_list(vec![
3111                Modify::Present(Attribute::Class, EntryClass::PosixAccount.into()),
3112                Modify::Present(Attribute::GidNumber, Value::new_uint32(2001)),
3113            ]),
3114        );
3115        assert!(idms_prox_write.qs_write.modify(&me_posix).is_ok());
3116
3117        let pce = UnixPasswordChangeEvent::new_internal(UUID_TESTPERSON_1, TEST_PASSWORD);
3118
3119        assert!(idms_prox_write.set_unix_account_password(&pce).is_ok());
3120        assert!(idms_prox_write.commit().is_ok());
3121
3122        let mut idms_auth = idms.auth().await.unwrap();
3124        let uuae_good = UnixUserAuthEvent::new_internal(UUID_TESTPERSON_1, TEST_PASSWORD);
3125
3126        let a1 = idms_auth.auth_unix(&uuae_good, time_low).await;
3127        match a1 {
3130            Ok(None) => {}
3131            _ => panic!("Oh no"),
3132        };
3133
3134        let a2 = idms_auth.auth_unix(&uuae_good, time_high).await;
3135        match a2 {
3136            Ok(None) => {}
3137            _ => panic!("Oh no"),
3138        };
3139
3140        idms_auth.commit().expect("Must not fail");
3141        let mut idms_prox_read = idms.proxy_read().await.unwrap();
3143        let uute = UnixUserTokenEvent::new_internal(UUID_TESTPERSON_1);
3144
3145        let tok_r = idms_prox_read
3146            .get_unixusertoken(&uute, time_low)
3147            .expect("Failed to generate unix user token");
3148
3149        assert_eq!(tok_r.name, "testperson1");
3150        assert!(!tok_r.valid);
3151
3152        let tok_r = idms_prox_read
3153            .get_unixusertoken(&uute, time_high)
3154            .expect("Failed to generate unix user token");
3155
3156        assert_eq!(tok_r.name, "testperson1");
3157        assert!(!tok_r.valid);
3158    }
3159
3160    #[idm_test]
3161    async fn test_idm_radius_valid_from_expire(
3162        idms: &IdmServer,
3163        _idms_delayed: &mut IdmServerDelayed,
3164    ) {
3165        init_testperson_w_password(idms, TEST_PASSWORD)
3168            .await
3169            .expect("Failed to setup admin account");
3170        set_testperson_valid_time(idms).await;
3171
3172        let time_low = Duration::from_secs(TEST_NOT_YET_VALID_TIME);
3173        let time_high = Duration::from_secs(TEST_AFTER_EXPIRY);
3174
3175        let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
3176        let rrse = RegenerateRadiusSecretEvent::new_internal(UUID_TESTPERSON_1);
3177        let _r1 = idms_prox_write
3178            .regenerate_radius_secret(&rrse)
3179            .expect("Failed to reset radius credential 1");
3180        idms_prox_write.commit().expect("failed to commit");
3181
3182        let mut idms_prox_read = idms.proxy_read().await.unwrap();
3183        let admin_entry = idms_prox_read
3184            .qs_read
3185            .internal_search_uuid(UUID_ADMIN)
3186            .expect("Can't access admin entry.");
3187
3188        let rate = RadiusAuthTokenEvent::new_impersonate(admin_entry, UUID_ADMIN);
3189        let tok_r = idms_prox_read.get_radiusauthtoken(&rate, time_low);
3190
3191        if tok_r.is_err() {
3192            } else {
3194            debug_assert!(false);
3195        }
3196
3197        let tok_r = idms_prox_read.get_radiusauthtoken(&rate, time_high);
3198
3199        if tok_r.is_err() {
3200            } else {
3202            debug_assert!(false);
3203        }
3204    }
3205
3206    #[idm_test(audit = 1)]
3207    async fn test_idm_account_softlocking(
3208        idms: &IdmServer,
3209        idms_delayed: &mut IdmServerDelayed,
3210        idms_audit: &mut IdmServerAudit,
3211    ) {
3212        init_testperson_w_password(idms, TEST_PASSWORD)
3213            .await
3214            .expect("Failed to setup admin account");
3215
3216        let sid =
3218            init_authsession_sid(idms, Duration::from_secs(TEST_CURRENT_TIME), "testperson1").await;
3219        let mut idms_auth = idms.auth().await.unwrap();
3220        let anon_step = AuthEvent::cred_step_password(sid, TEST_PASSWORD_INC);
3221
3222        let r2 = idms_auth
3223            .auth(
3224                &anon_step,
3225                Duration::from_secs(TEST_CURRENT_TIME),
3226                Source::Internal.into(),
3227            )
3228            .await;
3229        debug!("r2 ==> {:?}", r2);
3230
3231        match r2 {
3232            Ok(ar) => {
3233                let AuthResult {
3234                    sessionid: _,
3235                    state,
3236                } = ar;
3237                match state {
3238                    AuthState::Denied(reason) => {
3239                        assert!(reason != "Account is temporarily locked");
3240                    }
3241                    _ => {
3242                        error!("A critical error has occurred! We have a non-denied result!");
3243                        panic!();
3244                    }
3245                }
3246            }
3247            Err(e) => {
3248                error!("A critical error has occurred! {:?}", e);
3249                panic!();
3250            }
3251        };
3252
3253        match idms_audit.audit_rx().try_recv() {
3255            Ok(AuditEvent::AuthenticationDenied { .. }) => {}
3256            _ => panic!("Oh no"),
3257        }
3258
3259        idms_auth.commit().expect("Must not fail");
3260
3261        let mut idms_auth = idms.auth().await.unwrap();
3265        let admin_init = AuthEvent::named_init("testperson1");
3266
3267        let r1 = idms_auth
3268            .auth(
3269                &admin_init,
3270                Duration::from_secs(TEST_CURRENT_TIME),
3271                Source::Internal.into(),
3272            )
3273            .await;
3274        let ar = r1.unwrap();
3275        let AuthResult { sessionid, state } = ar;
3276        assert!(matches!(state, AuthState::Choose(_)));
3277
3278        let admin_begin = AuthEvent::begin_mech(sessionid, AuthMech::Password);
3280
3281        let r2 = idms_auth
3282            .auth(
3283                &admin_begin,
3284                Duration::from_secs(TEST_CURRENT_TIME),
3285                Source::Internal.into(),
3286            )
3287            .await;
3288        let ar = r2.unwrap();
3289        let AuthResult {
3290            sessionid: _,
3291            state,
3292        } = ar;
3293
3294        match state {
3295            AuthState::Denied(reason) => {
3296                assert_eq!(reason, "Account is temporarily locked");
3297            }
3298            _ => {
3299                error!("Sessions was not denied (softlock)");
3300                panic!();
3301            }
3302        };
3303
3304        idms_auth.commit().expect("Must not fail");
3305
3306        let sid = init_authsession_sid(
3311            idms,
3312            Duration::from_secs(TEST_CURRENT_TIME + 2),
3313            "testperson1",
3314        )
3315        .await;
3316
3317        let mut idms_auth = idms.auth().await.unwrap();
3318        let anon_step = AuthEvent::cred_step_password(sid, TEST_PASSWORD);
3319
3320        let r2 = idms_auth
3322            .auth(
3323                &anon_step,
3324                Duration::from_secs(TEST_CURRENT_TIME + 2),
3325                Source::Internal.into(),
3326            )
3327            .await;
3328        debug!("r2 ==> {:?}", r2);
3329
3330        match r2 {
3331            Ok(ar) => {
3332                let AuthResult {
3333                    sessionid: _,
3334                    state,
3335                } = ar;
3336                match state {
3337                    AuthState::Success(_uat, AuthIssueSession::Token) => {
3338                        }
3340                    _ => {
3341                        error!("A critical error has occurred! We have a non-success result!");
3342                        panic!();
3343                    }
3344                }
3345            }
3346            Err(e) => {
3347                error!("A critical error has occurred! {:?}", e);
3348                panic!();
3350            }
3351        };
3352
3353        idms_auth.commit().expect("Must not fail");
3354
3355        let da = idms_delayed.try_recv().expect("invalid");
3357        assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
3358        idms_delayed.check_is_empty_or_panic();
3359
3360        }
3367
3368    #[idm_test(audit = 1)]
3369    async fn test_idm_account_softlocking_interleaved(
3370        idms: &IdmServer,
3371        _idms_delayed: &mut IdmServerDelayed,
3372        idms_audit: &mut IdmServerAudit,
3373    ) {
3374        init_testperson_w_password(idms, TEST_PASSWORD)
3375            .await
3376            .expect("Failed to setup admin account");
3377
3378        let sid_early =
3380            init_authsession_sid(idms, Duration::from_secs(TEST_CURRENT_TIME), "testperson1").await;
3381
3382        let sid_later =
3384            init_authsession_sid(idms, Duration::from_secs(TEST_CURRENT_TIME), "testperson1").await;
3385        let mut idms_auth = idms.auth().await.unwrap();
3387        let anon_step = AuthEvent::cred_step_password(sid_later, TEST_PASSWORD_INC);
3388
3389        let r2 = idms_auth
3390            .auth(
3391                &anon_step,
3392                Duration::from_secs(TEST_CURRENT_TIME),
3393                Source::Internal.into(),
3394            )
3395            .await;
3396        debug!("r2 ==> {:?}", r2);
3397
3398        match r2 {
3399            Ok(ar) => {
3400                let AuthResult {
3401                    sessionid: _,
3402                    state,
3403                } = ar;
3404                match state {
3405                    AuthState::Denied(reason) => {
3406                        assert!(reason != "Account is temporarily locked");
3407                    }
3408                    _ => {
3409                        error!("A critical error has occurred! We have a non-denied result!");
3410                        panic!();
3411                    }
3412                }
3413            }
3414            Err(e) => {
3415                error!("A critical error has occurred! {:?}", e);
3416                panic!();
3417            }
3418        };
3419
3420        match idms_audit.audit_rx().try_recv() {
3421            Ok(AuditEvent::AuthenticationDenied { .. }) => {}
3422            _ => panic!("Oh no"),
3423        }
3424
3425        idms_auth.commit().expect("Must not fail");
3426
3427        let mut idms_auth = idms.auth().await.unwrap();
3429        let anon_step = AuthEvent::cred_step_password(sid_early, TEST_PASSWORD);
3430
3431        let r2 = idms_auth
3433            .auth(
3434                &anon_step,
3435                Duration::from_secs(TEST_CURRENT_TIME),
3436                Source::Internal.into(),
3437            )
3438            .await;
3439        debug!("r2 ==> {:?}", r2);
3440        match r2 {
3441            Ok(ar) => {
3442                let AuthResult {
3443                    sessionid: _,
3444                    state,
3445                } = ar;
3446                match state {
3447                    AuthState::Denied(reason) => {
3448                        assert_eq!(reason, "Account is temporarily locked");
3449                    }
3450                    _ => {
3451                        error!("A critical error has occurred! We have a non-denied result!");
3452                        panic!();
3453                    }
3454                }
3455            }
3456            Err(e) => {
3457                error!("A critical error has occurred! {:?}", e);
3458                panic!();
3459            }
3460        };
3461        idms_auth.commit().expect("Must not fail");
3462    }
3463
3464    #[idm_test]
3465    async fn test_idm_account_unix_softlocking(
3466        idms: &IdmServer,
3467        _idms_delayed: &mut IdmServerDelayed,
3468    ) {
3469        init_testperson_w_password(idms, TEST_PASSWORD)
3470            .await
3471            .expect("Failed to setup admin account");
3472        let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
3474        let me_posix = ModifyEvent::new_internal_invalid(
3475            filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_TESTPERSON_1))),
3476            ModifyList::new_list(vec![
3477                Modify::Present(Attribute::Class, EntryClass::PosixAccount.into()),
3478                Modify::Present(Attribute::GidNumber, Value::new_uint32(2001)),
3479            ]),
3480        );
3481        assert!(idms_prox_write.qs_write.modify(&me_posix).is_ok());
3482
3483        let pce = UnixPasswordChangeEvent::new_internal(UUID_TESTPERSON_1, TEST_PASSWORD);
3484        assert!(idms_prox_write.set_unix_account_password(&pce).is_ok());
3485        assert!(idms_prox_write.commit().is_ok());
3486
3487        let mut idms_auth = idms.auth().await.unwrap();
3488        let uuae_good = UnixUserAuthEvent::new_internal(UUID_TESTPERSON_1, TEST_PASSWORD);
3489        let uuae_bad = UnixUserAuthEvent::new_internal(UUID_TESTPERSON_1, TEST_PASSWORD_INC);
3490
3491        let a2 = idms_auth
3492            .auth_unix(&uuae_bad, Duration::from_secs(TEST_CURRENT_TIME))
3493            .await;
3494        match a2 {
3495            Ok(None) => {}
3496            _ => panic!("Oh no"),
3497        };
3498
3499        let a1 = idms_auth
3501            .auth_unix(&uuae_good, Duration::from_secs(TEST_CURRENT_TIME))
3502            .await;
3503        match a1 {
3504            Ok(None) => {}
3505            _ => panic!("Oh no"),
3506        };
3507
3508        let a1 = idms_auth
3510            .auth_unix(&uuae_good, Duration::from_secs(TEST_CURRENT_TIME + 2))
3511            .await;
3512        match a1 {
3513            Ok(Some(_tok)) => {}
3514            _ => panic!("Oh no"),
3515        };
3516
3517        assert!(idms_auth.commit().is_ok());
3518    }
3519
3520    #[idm_test]
3521    async fn test_idm_jwt_uat_expiry(idms: &IdmServer, idms_delayed: &mut IdmServerDelayed) {
3522        let ct = Duration::from_secs(TEST_CURRENT_TIME);
3523        let expiry = ct + Duration::from_secs((DEFAULT_AUTH_SESSION_EXPIRY + 1).into());
3524        init_testperson_w_password(idms, TEST_PASSWORD)
3526            .await
3527            .expect("Failed to setup admin account");
3528        let token = check_testperson_password(idms, TEST_PASSWORD, ct).await;
3529
3530        let da = idms_delayed.try_recv().expect("invalid");
3532        assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
3533        let r = idms.delayed_action(ct, da).await;
3535        assert_eq!(Ok(true), r);
3536        idms_delayed.check_is_empty_or_panic();
3537
3538        let mut idms_prox_read = idms.proxy_read().await.unwrap();
3539
3540        idms_prox_read
3542            .validate_client_auth_info_to_ident(token.clone().into(), ct)
3543            .expect("Failed to validate");
3544
3545        match idms_prox_read.validate_client_auth_info_to_ident(token.into(), expiry) {
3547            Err(OperationError::SessionExpired) => {}
3548            _ => panic!("Oh no"),
3549        }
3550    }
3551
3552    #[idm_test]
3553    async fn test_idm_expired_auth_session_cleanup(
3554        idms: &IdmServer,
3555        _idms_delayed: &mut IdmServerDelayed,
3556    ) {
3557        let ct = Duration::from_secs(TEST_CURRENT_TIME);
3558        let expiry_a = ct + Duration::from_secs((DEFAULT_AUTH_SESSION_EXPIRY + 1).into());
3559        let expiry_b = ct + Duration::from_secs(((DEFAULT_AUTH_SESSION_EXPIRY + 1) * 2).into());
3560
3561        let session_a = Uuid::new_v4();
3562        let session_b = Uuid::new_v4();
3563
3564        let cred_id = init_testperson_w_password(idms, TEST_PASSWORD)
3566            .await
3567            .expect("Failed to setup admin account");
3568
3569        let mut idms_prox_read = idms.proxy_read().await.unwrap();
3571        let admin = idms_prox_read
3572            .qs_read
3573            .internal_search_uuid(UUID_TESTPERSON_1)
3574            .expect("failed");
3575        let sessions = admin.get_ava_as_session_map(Attribute::UserAuthTokenSession);
3576        assert!(sessions.is_none());
3577        drop(idms_prox_read);
3578
3579        let da = DelayedAction::AuthSessionRecord(AuthSessionRecord {
3580            target_uuid: UUID_TESTPERSON_1,
3581            session_id: session_a,
3582            cred_id,
3583            label: "Test Session A".to_string(),
3584            expiry: Some(OffsetDateTime::UNIX_EPOCH + expiry_a),
3585            issued_at: OffsetDateTime::UNIX_EPOCH + ct,
3586            issued_by: IdentityId::User(UUID_ADMIN),
3587            scope: SessionScope::ReadOnly,
3588            type_: AuthType::Passkey,
3589        });
3590        let r = idms.delayed_action(ct, da).await;
3592        assert_eq!(Ok(true), r);
3593
3594        let mut idms_prox_read = idms.proxy_read().await.unwrap();
3596        let admin = idms_prox_read
3597            .qs_read
3598            .internal_search_uuid(UUID_TESTPERSON_1)
3599            .expect("failed");
3600        let sessions = admin
3601            .get_ava_as_session_map(Attribute::UserAuthTokenSession)
3602            .expect("Sessions must be present!");
3603        assert_eq!(sessions.len(), 1);
3604        let session_data_a = sessions.get(&session_a).expect("Session A is missing!");
3605        assert!(matches!(session_data_a.state, SessionState::ExpiresAt(_)));
3606
3607        drop(idms_prox_read);
3608
3609        let da = DelayedAction::AuthSessionRecord(AuthSessionRecord {
3612            target_uuid: UUID_TESTPERSON_1,
3613            session_id: session_b,
3614            cred_id,
3615            label: "Test Session B".to_string(),
3616            expiry: Some(OffsetDateTime::UNIX_EPOCH + expiry_b),
3617            issued_at: OffsetDateTime::UNIX_EPOCH + ct,
3618            issued_by: IdentityId::User(UUID_ADMIN),
3619            scope: SessionScope::ReadOnly,
3620            type_: AuthType::Passkey,
3621        });
3622        let r = idms.delayed_action(expiry_a, da).await;
3624        assert_eq!(Ok(true), r);
3625
3626        let mut idms_prox_read = idms.proxy_read().await.unwrap();
3627        let admin = idms_prox_read
3628            .qs_read
3629            .internal_search_uuid(UUID_TESTPERSON_1)
3630            .expect("failed");
3631        let sessions = admin
3632            .get_ava_as_session_map(Attribute::UserAuthTokenSession)
3633            .expect("Sessions must be present!");
3634        trace!(?sessions);
3635        assert_eq!(sessions.len(), 2);
3636
3637        let session_data_a = sessions.get(&session_a).expect("Session A is missing!");
3638        assert!(matches!(session_data_a.state, SessionState::RevokedAt(_)));
3639
3640        let session_data_b = sessions.get(&session_b).expect("Session B is missing!");
3641        assert!(matches!(session_data_b.state, SessionState::ExpiresAt(_)));
3642        }
3644
3645    #[idm_test]
3646    async fn test_idm_account_session_validation(
3647        idms: &IdmServer,
3648        idms_delayed: &mut IdmServerDelayed,
3649    ) {
3650        use kanidm_proto::internal::UserAuthToken;
3651
3652        let ct = duration_from_epoch_now();
3653
3654        let post_grace = ct + AUTH_TOKEN_GRACE_WINDOW + Duration::from_secs(1);
3655        let expiry = ct + Duration::from_secs(DEFAULT_AUTH_SESSION_EXPIRY as u64 + 1);
3656
3657        assert!(post_grace < expiry);
3660
3661        init_testperson_w_password(idms, TEST_PASSWORD)
3663            .await
3664            .expect("Failed to setup admin account");
3665        let uat_unverified = check_testperson_password(idms, TEST_PASSWORD, ct).await;
3666
3667        let da = idms_delayed.try_recv().expect("invalid");
3669        assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
3670        let r = idms.delayed_action(ct, da).await;
3671        assert_eq!(Ok(true), r);
3672
3673        let mut idms_prox_read = idms.proxy_read().await.unwrap();
3674
3675        let token_kid = uat_unverified.kid().expect("no key id present");
3676
3677        let uat_jwk = idms_prox_read
3678            .qs_read
3679            .get_key_providers()
3680            .get_key_object(UUID_DOMAIN_INFO)
3681            .and_then(|object| {
3682                object
3683                    .jws_public_jwk(token_kid)
3684                    .expect("Unable to access uat jwk")
3685            })
3686            .expect("No jwk by this kid");
3687
3688        let jws_validator = JwsEs256Verifier::try_from(&uat_jwk).unwrap();
3689
3690        let uat_inner: UserAuthToken = jws_validator
3691            .verify(&uat_unverified)
3692            .unwrap()
3693            .from_json()
3694            .unwrap();
3695
3696        idms_prox_read
3698            .validate_client_auth_info_to_ident(uat_unverified.clone().into(), ct)
3699            .expect("Failed to validate");
3700
3701        idms_prox_read
3703            .validate_client_auth_info_to_ident(uat_unverified.clone().into(), post_grace)
3704            .expect("Failed to validate");
3705
3706        drop(idms_prox_read);
3707
3708        let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
3710        let dte = DestroySessionTokenEvent::new_internal(uat_inner.uuid, uat_inner.session_id);
3711        assert!(idms_prox_write.account_destroy_session_token(&dte).is_ok());
3712        assert!(idms_prox_write.commit().is_ok());
3713
3714        let mut idms_prox_read = idms.proxy_read().await.unwrap();
3716
3717        match idms_prox_read
3720            .validate_client_auth_info_to_ident(uat_unverified.clone().into(), post_grace)
3721        {
3722            Err(OperationError::SessionExpired) => {}
3723            _ => panic!("Oh no"),
3724        }
3725        drop(idms_prox_read);
3726
3727        let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
3729        let filt = filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(uat_inner.uuid)));
3730        let mut work_set = idms_prox_write
3731            .qs_write
3732            .internal_search_writeable(&filt)
3733            .expect("Failed to perform internal search writeable");
3734        for (_, entry) in work_set.iter_mut() {
3735            let _ = entry.force_trim_ava(Attribute::UserAuthTokenSession);
3736        }
3737        assert!(idms_prox_write
3738            .qs_write
3739            .internal_apply_writable(work_set)
3740            .is_ok());
3741
3742        assert!(idms_prox_write.commit().is_ok());
3743
3744        let mut idms_prox_read = idms.proxy_read().await.unwrap();
3745        idms_prox_read
3746            .validate_client_auth_info_to_ident(uat_unverified.clone().into(), ct)
3747            .expect("Failed to validate");
3748
3749        match idms_prox_read
3751            .validate_client_auth_info_to_ident(uat_unverified.clone().into(), post_grace)
3752        {
3753            Err(OperationError::SessionExpired) => {}
3754            _ => panic!("Oh no"),
3755        }
3756    }
3757
3758    #[idm_test]
3759    async fn test_idm_account_session_expiry(
3760        idms: &IdmServer,
3761        _idms_delayed: &mut IdmServerDelayed,
3762    ) {
3763        let ct = Duration::from_secs(TEST_CURRENT_TIME);
3764
3765        let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
3767
3768        let new_authsession_expiry = 1000;
3769
3770        let modlist = ModifyList::new_purge_and_set(
3771            Attribute::AuthSessionExpiry,
3772            Value::Uint32(new_authsession_expiry),
3773        );
3774        idms_prox_write
3775            .qs_write
3776            .internal_modify_uuid(UUID_IDM_ALL_ACCOUNTS, &modlist)
3777            .expect("Unable to change default session exp");
3778
3779        assert!(idms_prox_write.commit().is_ok());
3780
3781        let mut idms_auth = idms.auth().await.unwrap();
3783        let anon_init = AuthEvent::anonymous_init();
3785        let r1 = idms_auth
3787            .auth(&anon_init, ct, Source::Internal.into())
3788            .await;
3789        let sid = match r1 {
3792            Ok(ar) => {
3793                let AuthResult { sessionid, state } = ar;
3794                match state {
3795                    AuthState::Choose(mut conts) => {
3796                        assert_eq!(conts.len(), 1);
3798                        let m = conts.pop().expect("Should not fail");
3800                        assert_eq!(m, AuthMech::Anonymous);
3801                    }
3802                    _ => {
3803                        error!("A critical error has occurred! We have a non-continue result!");
3804                        panic!();
3805                    }
3806                };
3807                sessionid
3809            }
3810            Err(e) => {
3811                error!("A critical error has occurred! {:?}", e);
3813                panic!();
3814            }
3815        };
3816
3817        idms_auth.commit().expect("Must not fail");
3818
3819        let mut idms_auth = idms.auth().await.unwrap();
3820        let anon_begin = AuthEvent::begin_mech(sid, AuthMech::Anonymous);
3821
3822        let r2 = idms_auth
3823            .auth(&anon_begin, ct, Source::Internal.into())
3824            .await;
3825
3826        match r2 {
3827            Ok(ar) => {
3828                let AuthResult {
3829                    sessionid: _,
3830                    state,
3831                } = ar;
3832
3833                match state {
3834                    AuthState::Continue(allowed) => {
3835                        assert_eq!(allowed.len(), 1);
3837                        assert_eq!(allowed.first(), Some(&AuthAllowed::Anonymous));
3838                    }
3839                    _ => {
3840                        error!("A critical error has occurred! We have a non-continue result!");
3841                        panic!();
3842                    }
3843                }
3844            }
3845            Err(e) => {
3846                error!("A critical error has occurred! {:?}", e);
3847                panic!();
3849            }
3850        };
3851
3852        idms_auth.commit().expect("Must not fail");
3853
3854        let mut idms_auth = idms.auth().await.unwrap();
3855        let anon_step = AuthEvent::cred_step_anonymous(sid);
3857
3858        let r2 = idms_auth
3860            .auth(&anon_step, ct, Source::Internal.into())
3861            .await;
3862
3863        let token = match r2 {
3864            Ok(ar) => {
3865                let AuthResult {
3866                    sessionid: _,
3867                    state,
3868                } = ar;
3869
3870                match state {
3871                    AuthState::Success(uat, AuthIssueSession::Token) => uat,
3872                    _ => {
3873                        error!("A critical error has occurred! We have a non-success result!");
3874                        panic!();
3875                    }
3876                }
3877            }
3878            Err(e) => {
3879                error!("A critical error has occurred! {:?}", e);
3880                panic!("A critical error has occurred! {e:?}");
3882            }
3883        };
3884
3885        idms_auth.commit().expect("Must not fail");
3886
3887        let Token::UserAuthToken(uat) = idms
3890            .proxy_read()
3891            .await
3892            .unwrap()
3893            .validate_and_parse_token_to_identity_token(&token, ct)
3894            .expect("Must not fail")
3895        else {
3896            panic!("Unexpected auth token type for anonymous auth");
3897        };
3898
3899        debug!(?uat);
3900
3901        assert!(
3902            matches!(uat.expiry, Some(exp) if exp == OffsetDateTime::UNIX_EPOCH + ct + Duration::from_secs(new_authsession_expiry as u64))
3903        );
3904    }
3905
3906    #[idm_test]
3907    async fn test_idm_uat_claim_insertion(idms: &IdmServer, _idms_delayed: &mut IdmServerDelayed) {
3908        let ct = Duration::from_secs(TEST_CURRENT_TIME);
3909        let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
3910
3911        let account = idms_prox_write
3913            .target_to_account(UUID_ADMIN)
3914            .expect("account must exist");
3915
3916        let session_id = uuid::Uuid::new_v4();
3918
3919        let uat = account
3923            .to_userauthtoken(
3924                session_id,
3925                SessionScope::ReadWrite,
3926                ct,
3927                &ResolvedAccountPolicy::test_policy(),
3928            )
3929            .expect("Unable to create uat");
3930        let ident = idms_prox_write
3931            .process_uat_to_identity(&uat, ct, Source::Internal)
3932            .expect("Unable to process uat");
3933
3934        assert!(!ident.has_claim("authtype_anonymous"));
3935        assert!(!ident.has_claim("authlevel_strong"));
3937        assert!(!ident.has_claim("authclass_single"));
3938        assert!(!ident.has_claim("authclass_mfa"));
3939
3940        let uat = account
3942            .to_userauthtoken(
3943                session_id,
3944                SessionScope::ReadWrite,
3945                ct,
3946                &ResolvedAccountPolicy::test_policy(),
3947            )
3948            .expect("Unable to create uat");
3949        let ident = idms_prox_write
3950            .process_uat_to_identity(&uat, ct, Source::Internal)
3951            .expect("Unable to process uat");
3952
3953        assert!(!ident.has_claim("authtype_unixpassword"));
3954        assert!(!ident.has_claim("authclass_single"));
3955        assert!(!ident.has_claim("authlevel_strong"));
3957        assert!(!ident.has_claim("authclass_mfa"));
3958
3959        let uat = account
3961            .to_userauthtoken(
3962                session_id,
3963                SessionScope::ReadWrite,
3964                ct,
3965                &ResolvedAccountPolicy::test_policy(),
3966            )
3967            .expect("Unable to create uat");
3968        let ident = idms_prox_write
3969            .process_uat_to_identity(&uat, ct, Source::Internal)
3970            .expect("Unable to process uat");
3971
3972        assert!(!ident.has_claim("authtype_password"));
3973        assert!(!ident.has_claim("authclass_single"));
3974        assert!(!ident.has_claim("authlevel_strong"));
3976        assert!(!ident.has_claim("authclass_mfa"));
3977
3978        let uat = account
3980            .to_userauthtoken(
3981                session_id,
3982                SessionScope::ReadWrite,
3983                ct,
3984                &ResolvedAccountPolicy::test_policy(),
3985            )
3986            .expect("Unable to create uat");
3987        let ident = idms_prox_write
3988            .process_uat_to_identity(&uat, ct, Source::Internal)
3989            .expect("Unable to process uat");
3990
3991        assert!(!ident.has_claim("authtype_generatedpassword"));
3992        assert!(!ident.has_claim("authclass_single"));
3993        assert!(!ident.has_claim("authlevel_strong"));
3994        assert!(!ident.has_claim("authclass_mfa"));
3996
3997        let uat = account
3999            .to_userauthtoken(
4000                session_id,
4001                SessionScope::ReadWrite,
4002                ct,
4003                &ResolvedAccountPolicy::test_policy(),
4004            )
4005            .expect("Unable to create uat");
4006        let ident = idms_prox_write
4007            .process_uat_to_identity(&uat, ct, Source::Internal)
4008            .expect("Unable to process uat");
4009
4010        assert!(!ident.has_claim("authtype_webauthn"));
4011        assert!(!ident.has_claim("authclass_single"));
4012        assert!(!ident.has_claim("authlevel_strong"));
4013        assert!(!ident.has_claim("authclass_mfa"));
4015
4016        let uat = account
4018            .to_userauthtoken(
4019                session_id,
4020                SessionScope::ReadWrite,
4021                ct,
4022                &ResolvedAccountPolicy::test_policy(),
4023            )
4024            .expect("Unable to create uat");
4025        let ident = idms_prox_write
4026            .process_uat_to_identity(&uat, ct, Source::Internal)
4027            .expect("Unable to process uat");
4028
4029        assert!(!ident.has_claim("authtype_passwordmfa"));
4030        assert!(!ident.has_claim("authlevel_strong"));
4031        assert!(!ident.has_claim("authclass_mfa"));
4032        assert!(!ident.has_claim("authclass_single"));
4034    }
4035
4036    #[idm_test]
4037    async fn test_idm_uat_limits_account_policy(
4038        idms: &IdmServer,
4039        _idms_delayed: &mut IdmServerDelayed,
4040    ) {
4041        let ct = Duration::from_secs(TEST_CURRENT_TIME);
4042        let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
4043
4044        idms_prox_write
4045            .qs_write
4046            .internal_create(vec![E_TESTPERSON_1.clone()])
4047            .expect("Failed to create test person");
4048
4049        let account = idms_prox_write
4051            .target_to_account(UUID_TESTPERSON_1)
4052            .expect("account must exist");
4053
4054        let session_id = uuid::Uuid::new_v4();
4056
4057        let uat = account
4058            .to_userauthtoken(
4059                session_id,
4060                SessionScope::ReadWrite,
4061                ct,
4062                &ResolvedAccountPolicy::test_policy(),
4063            )
4064            .expect("Unable to create uat");
4065
4066        let ident = idms_prox_write
4067            .process_uat_to_identity(&uat, ct, Source::Internal)
4068            .expect("Unable to process uat");
4069
4070        assert_eq!(
4071            ident.limits().search_max_results,
4072            DEFAULT_LIMIT_SEARCH_MAX_RESULTS as usize
4073        );
4074        assert_eq!(
4075            ident.limits().search_max_filter_test,
4076            DEFAULT_LIMIT_SEARCH_MAX_FILTER_TEST as usize
4077        );
4078    }
4079
4080    #[idm_test]
4081    async fn test_idm_jwt_uat_token_key_reload(
4082        idms: &IdmServer,
4083        idms_delayed: &mut IdmServerDelayed,
4084    ) {
4085        let ct = duration_from_epoch_now();
4086
4087        init_testperson_w_password(idms, TEST_PASSWORD)
4088            .await
4089            .expect("Failed to setup admin account");
4090        let token = check_testperson_password(idms, TEST_PASSWORD, ct).await;
4091
4092        let da = idms_delayed.try_recv().expect("invalid");
4094        assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
4095        idms_delayed.check_is_empty_or_panic();
4096
4097        let mut idms_prox_read = idms.proxy_read().await.unwrap();
4098
4099        idms_prox_read
4101            .validate_client_auth_info_to_ident(token.clone().into(), ct)
4102            .expect("Failed to validate");
4103
4104        drop(idms_prox_read);
4105
4106        let revoke_kid = token.kid().expect("token does not contain a key id");
4108
4109        let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
4111        let me_reset_tokens = ModifyEvent::new_internal_invalid(
4112            filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_DOMAIN_INFO))),
4113            ModifyList::new_append(
4114                Attribute::KeyActionRevoke,
4115                Value::HexString(revoke_kid.to_string()),
4116            ),
4117        );
4118        assert!(idms_prox_write.qs_write.modify(&me_reset_tokens).is_ok());
4119        assert!(idms_prox_write.commit().is_ok());
4120
4121        let new_token = check_testperson_password(idms, TEST_PASSWORD, ct).await;
4122
4123        let da = idms_delayed.try_recv().expect("invalid");
4125        assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
4126        idms_delayed.check_is_empty_or_panic();
4127
4128        let mut idms_prox_read = idms.proxy_read().await.unwrap();
4129
4130        assert!(idms_prox_read
4132            .validate_client_auth_info_to_ident(token.into(), ct)
4133            .is_err());
4134
4135        idms_prox_read
4137            .validate_client_auth_info_to_ident(new_token.into(), ct)
4138            .expect("Failed to validate");
4139    }
4140
4141    #[idm_test]
4142    async fn test_idm_service_account_to_person(
4143        idms: &IdmServer,
4144        _idms_delayed: &mut IdmServerDelayed,
4145    ) {
4146        let ct = Duration::from_secs(TEST_CURRENT_TIME);
4147        let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
4148
4149        let ident = Identity::from_internal();
4150        let target_uuid = Uuid::new_v4();
4151
4152        let e = entry_init!(
4154            (Attribute::Class, EntryClass::Object.to_value()),
4155            (Attribute::Class, EntryClass::Account.to_value()),
4156            (Attribute::Class, EntryClass::ServiceAccount.to_value()),
4157            (Attribute::Name, Value::new_iname("testaccount")),
4158            (Attribute::Uuid, Value::Uuid(target_uuid)),
4159            (Attribute::Description, Value::new_utf8s("testaccount")),
4160            (Attribute::DisplayName, Value::new_utf8s("Test Account"))
4161        );
4162
4163        let ce = CreateEvent::new_internal(vec![e]);
4164        let cr = idms_prox_write.qs_write.create(&ce);
4165        assert!(cr.is_ok());
4166
4167        assert!(idms_prox_write
4169            .service_account_into_person(&ident, target_uuid)
4170            .is_ok());
4171
4172        }
4174
4175    async fn idm_fallback_auth_fixture(
4176        idms: &IdmServer,
4177        _idms_delayed: &mut IdmServerDelayed,
4178        has_posix_password: bool,
4179        allow_primary_cred_fallback: Option<bool>,
4180        expected: Option<()>,
4181    ) {
4182        let ct = Duration::from_secs(TEST_CURRENT_TIME);
4183        let target_uuid = Uuid::new_v4();
4184        let p = CryptoPolicy::minimum();
4185
4186        {
4187            let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
4188
4189            if let Some(allow_primary_cred_fallback) = allow_primary_cred_fallback {
4190                idms_prox_write
4191                    .qs_write
4192                    .internal_modify_uuid(
4193                        UUID_IDM_ALL_ACCOUNTS,
4194                        &ModifyList::new_purge_and_set(
4195                            Attribute::AllowPrimaryCredFallback,
4196                            Value::new_bool(allow_primary_cred_fallback),
4197                        ),
4198                    )
4199                    .expect("Unable to change default session exp");
4200            }
4201
4202            let mut e = entry_init!(
4203                (Attribute::Class, EntryClass::Object.to_value()),
4204                (Attribute::Class, EntryClass::Account.to_value()),
4205                (Attribute::Class, EntryClass::Person.to_value()),
4206                (Attribute::Uuid, Value::Uuid(target_uuid)),
4207                (Attribute::Name, Value::new_iname("kevin")),
4208                (Attribute::DisplayName, Value::new_utf8s("Kevin")),
4209                (Attribute::Class, EntryClass::PosixAccount.to_value()),
4210                (
4211                    Attribute::PrimaryCredential,
4212                    Value::Cred(
4213                        "primary".to_string(),
4214                        Credential::new_password_only(&p, "banana").unwrap()
4215                    )
4216                )
4217            );
4218
4219            if has_posix_password {
4220                e.add_ava(
4221                    Attribute::UnixPassword,
4222                    Value::Cred(
4223                        "unix".to_string(),
4224                        Credential::new_password_only(&p, "kampai").unwrap(),
4225                    ),
4226                );
4227            }
4228
4229            let ce = CreateEvent::new_internal(vec![e]);
4230            let cr = idms_prox_write.qs_write.create(&ce);
4231            assert!(cr.is_ok());
4232            idms_prox_write.commit().expect("Must not fail");
4233        }
4234
4235        let result = idms
4236            .auth()
4237            .await
4238            .unwrap()
4239            .auth_ldap(
4240                &LdapAuthEvent {
4241                    target: target_uuid,
4242                    cleartext: if has_posix_password {
4243                        "kampai".to_string()
4244                    } else {
4245                        "banana".to_string()
4246                    },
4247                },
4248                ct,
4249            )
4250            .await;
4251
4252        assert!(result.is_ok());
4253        if expected.is_some() {
4254            assert!(result.unwrap().is_some());
4255        } else {
4256            assert!(result.unwrap().is_none());
4257        }
4258    }
4259
4260    #[idm_test]
4261    async fn test_idm_fallback_auth_no_pass_none_fallback(
4262        idms: &IdmServer,
4263        _idms_delayed: &mut IdmServerDelayed,
4264    ) {
4265        idm_fallback_auth_fixture(idms, _idms_delayed, false, None, None).await;
4266    }
4267    #[idm_test]
4268    async fn test_idm_fallback_auth_pass_none_fallback(
4269        idms: &IdmServer,
4270        _idms_delayed: &mut IdmServerDelayed,
4271    ) {
4272        idm_fallback_auth_fixture(idms, _idms_delayed, true, None, Some(())).await;
4273    }
4274    #[idm_test]
4275    async fn test_idm_fallback_auth_no_pass_true_fallback(
4276        idms: &IdmServer,
4277        _idms_delayed: &mut IdmServerDelayed,
4278    ) {
4279        idm_fallback_auth_fixture(idms, _idms_delayed, false, Some(true), Some(())).await;
4280    }
4281    #[idm_test]
4282    async fn test_idm_fallback_auth_pass_true_fallback(
4283        idms: &IdmServer,
4284        _idms_delayed: &mut IdmServerDelayed,
4285    ) {
4286        idm_fallback_auth_fixture(idms, _idms_delayed, true, Some(true), Some(())).await;
4287    }
4288    #[idm_test]
4289    async fn test_idm_fallback_auth_no_pass_false_fallback(
4290        idms: &IdmServer,
4291        _idms_delayed: &mut IdmServerDelayed,
4292    ) {
4293        idm_fallback_auth_fixture(idms, _idms_delayed, false, Some(false), None).await;
4294    }
4295    #[idm_test]
4296    async fn test_idm_fallback_auth_pass_false_fallback(
4297        idms: &IdmServer,
4298        _idms_delayed: &mut IdmServerDelayed,
4299    ) {
4300        idm_fallback_auth_fixture(idms, _idms_delayed, true, Some(false), Some(())).await;
4301    }
4302}