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