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