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