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