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