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