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 = rand::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 = rand::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>() {
551 if let Some(expiry) = apit.expiry {
552 if time::OffsetDateTime::UNIX_EPOCH + ct >= expiry {
553 security_info!("Session expired");
554 return Err(OperationError::SessionExpired);
555 }
556 }
557
558 let entry = self
559 .get_qs_txn()
560 .internal_search_uuid(apit.account_id)
561 .map_err(|err| {
562 security_info!(?err, "Account associated with api token no longer exists.");
563 OperationError::NotAuthenticated
564 })?;
565
566 return Ok(Token::ApiToken(apit, entry));
567 };
568
569 if let Ok(session_id) = Uuid::from_slice(jws_inner.payload()) {
571 let filter = filter!(f_eq(
574 Attribute::ApiTokenSession,
575 PartialValue::Refer(session_id)
576 ));
577
578 let mut entry = self.get_qs_txn().internal_search(filter).map_err(|err| {
579 security_info!(
580 ?err,
581 "Account or session associated with session token no longer exists."
582 );
583 OperationError::NotAuthenticated
584 })?;
585
586 let entry = entry.pop().ok_or_else(|| {
587 security_info!("Search result failed to return a valid entry.");
588 OperationError::NotAuthenticated
589 })?;
590
591 let api_token_map = entry.get_ava_as_apitoken_map(Attribute::ApiTokenSession)
592 .ok_or_else(|| {
593 security_info!(entry_id = %entry.get_display_id(), "Account does not contain any valid api token sessions.");
594 OperationError::NotAuthenticated
595 })?;
596
597 let api_token_internal = api_token_map.get(&session_id)
598 .ok_or_else(|| {
599 security_info!(entry_id = %entry.get_display_id(), "Account does not contain a valid api token for the session.");
600 OperationError::NotAuthenticated
601 })?;
602
603 let purpose = api_token_internal.scope.try_into().map_err(|_| {
604 security_info!(entry_id = %entry.get_display_id(), "Account scope is not valid.");
605 OperationError::NotAuthenticated
606 })?;
607
608 let apit = kanidm_proto::internal::ApiToken {
609 account_id: entry.get_uuid(),
610 token_id: session_id,
611 label: api_token_internal.label.clone(),
612 expiry: api_token_internal.expiry,
613 issued_at: api_token_internal.issued_at,
614 purpose,
615 };
616
617 if let Some(expiry) = apit.expiry {
618 if time::OffsetDateTime::UNIX_EPOCH + ct >= expiry {
619 security_info!(entry_id = %entry.get_display_id(), "Session expired");
620 return Err(OperationError::SessionExpired);
621 }
622 }
623
624 return Ok(Token::ApiToken(apit, entry));
625 }
626
627 security_info!("Unable to verify token, invalid inner JSON");
628 Err(OperationError::NotAuthenticated)
629 }
630
631 fn check_oauth2_account_uuid_valid(
632 &mut self,
633 uuid: Uuid,
634 session_id: Uuid,
635 parent_session_id: Option<Uuid>,
636 iat: i64,
637 ct: Duration,
638 ) -> Result<Option<Arc<Entry<EntrySealed, EntryCommitted>>>, OperationError> {
639 let entry = self.get_qs_txn().internal_search_uuid(uuid).map_err(|e| {
640 admin_error!(?e, "check_oauth2_account_uuid_valid failed");
641 e
642 })?;
643
644 let within_valid_window = Account::check_within_valid_time(
645 ct,
646 entry
647 .get_ava_single_datetime(Attribute::AccountValidFrom)
648 .as_ref(),
649 entry
650 .get_ava_single_datetime(Attribute::AccountExpire)
651 .as_ref(),
652 );
653
654 if !within_valid_window {
655 security_info!("Account has expired or is not yet valid, not allowing to proceed");
656 return Ok(None);
657 }
658
659 let grace_valid = ct < (Duration::from_secs(iat as u64) + AUTH_TOKEN_GRACE_WINDOW);
664
665 let oauth2_session = entry
666 .get_ava_as_oauth2session_map(Attribute::OAuth2Session)
667 .and_then(|sessions| sessions.get(&session_id));
668
669 if let Some(oauth2_session) = oauth2_session {
670 let oauth2_session_valid = !matches!(oauth2_session.state, SessionState::RevokedAt(_));
672
673 if !oauth2_session_valid {
674 security_info!("The oauth2 session associated to this token is revoked.");
675 return Ok(None);
676 }
677
678 if let Some(parent_session_id) = parent_session_id {
680 let uat_session = entry
681 .get_ava_as_session_map(Attribute::UserAuthTokenSession)
682 .and_then(|sessions| sessions.get(&parent_session_id));
683
684 if let Some(uat_session) = uat_session {
685 let parent_session_valid =
686 !matches!(uat_session.state, SessionState::RevokedAt(_));
687 if parent_session_valid {
688 security_info!(
689 "A valid parent and oauth2 session value exists for this token"
690 );
691 } else {
692 security_info!(
693 "The parent oauth2 session associated to this token is revoked."
694 );
695 return Ok(None);
696 }
697 } else {
698 let api_session = entry
699 .get_ava_as_apitoken_map(Attribute::ApiTokenSession)
700 .and_then(|sessions| sessions.get(&parent_session_id));
701 if api_session.is_some() {
702 security_info!("A valid api token session value exists for this token");
703 } else if grace_valid {
704 security_info!(
705 "The token grace window is in effect. Assuming parent session valid."
706 );
707 } else {
708 security_info!("The token grace window has passed and no entry parent sessions exist. Assuming invalid.");
709 return Ok(None);
710 }
711 }
712 }
713 } else if grace_valid {
715 security_info!("The token grace window is in effect. Assuming valid.");
716 } else {
717 security_info!(
718 "The token grace window has passed and no entry sessions exist. Assuming invalid."
719 );
720 return Ok(None);
721 }
722
723 Ok(Some(entry))
724 }
725
726 #[instrument(level = "debug", skip_all)]
739 fn process_uat_to_identity(
740 &mut self,
741 uat: &UserAuthToken,
742 ct: Duration,
743 source: Source,
744 ) -> Result<Identity, OperationError> {
745 let entry = self
747 .get_qs_txn()
748 .internal_search_uuid(uat.uuid)
749 .map_err(|e| {
750 admin_error!(?e, "from_ro_uat failed");
751 e
752 })?;
753
754 let valid = Account::check_user_auth_token_valid(ct, uat, &entry);
755
756 if !valid {
757 return Err(OperationError::SessionExpired);
758 }
759
760 let scope = match uat.purpose {
763 UatPurpose::ReadOnly => AccessScope::ReadOnly,
764 UatPurpose::ReadWrite { expiry: None } => AccessScope::ReadOnly,
765 UatPurpose::ReadWrite {
766 expiry: Some(expiry),
767 } => {
768 let cot = time::OffsetDateTime::UNIX_EPOCH + ct;
769 if cot < expiry {
770 AccessScope::ReadWrite
771 } else {
772 AccessScope::ReadOnly
773 }
774 }
775 };
776
777 let mut limits = Limits::default();
778 if let Some(lim) = uat.limit_search_max_results.and_then(|v| v.try_into().ok()) {
780 limits.search_max_results = lim;
781 }
782 if let Some(lim) = uat
783 .limit_search_max_filter_test
784 .and_then(|v| v.try_into().ok())
785 {
786 limits.search_max_filter_test = lim;
787 }
788
789 Ok(Identity::new(
805 IdentType::User(IdentUser { entry }),
806 source,
807 uat.session_id,
808 scope,
809 limits,
810 ))
811 }
812
813 #[instrument(level = "debug", skip_all)]
814 fn process_apit_to_identity(
815 &mut self,
816 apit: &ApiToken,
817 source: Source,
818 entry: Arc<EntrySealedCommitted>,
819 ct: Duration,
820 ) -> Result<Identity, OperationError> {
821 let valid = ServiceAccount::check_api_token_valid(ct, apit, &entry);
822
823 if !valid {
824 return Err(OperationError::SessionExpired);
826 }
827
828 let scope = (&apit.purpose).into();
829
830 let limits = Limits::api_token();
831 Ok(Identity::new(
832 IdentType::User(IdentUser { entry }),
833 source,
834 apit.token_id,
835 scope,
836 limits,
837 ))
838 }
839
840 fn client_cert_info_entry(
841 &mut self,
842 client_cert_info: &ClientCertInfo,
843 ) -> Result<Arc<EntrySealedCommitted>, OperationError> {
844 let pks256 = hex::encode(client_cert_info.public_key_s256);
845 let mut maybe_cert_entries = self.get_qs_txn().internal_search(filter!(f_eq(
847 Attribute::Certificate,
848 PartialValue::HexString(pks256.clone())
849 )))?;
850
851 let maybe_cert_entry = maybe_cert_entries.pop();
852
853 if let Some(cert_entry) = maybe_cert_entry {
854 if maybe_cert_entries.is_empty() {
855 Ok(cert_entry)
856 } else {
857 debug!(?pks256, "Multiple certificates matched, unable to proceed.");
858 Err(OperationError::NotAuthenticated)
859 }
860 } else {
861 debug!(?pks256, "No certificates were able to be mapped.");
862 Err(OperationError::NotAuthenticated)
863 }
864 }
865
866 #[instrument(level = "debug", skip_all)]
875 fn client_certificate_to_identity(
876 &mut self,
877 client_cert_info: &ClientCertInfo,
878 ct: Duration,
879 source: Source,
880 ) -> Result<Identity, OperationError> {
881 let cert_entry = self.client_cert_info_entry(client_cert_info)?;
882
883 let refers_uuid = cert_entry
885 .get_ava_single_refer(Attribute::Refers)
886 .ok_or_else(|| {
887 warn!("Invalid certificate entry, missing refers");
888 OperationError::InvalidState
889 })?;
890
891 let entry = self.get_qs_txn().internal_search_uuid(refers_uuid)?;
893
894 let (account, account_policy) =
895 Account::try_from_entry_with_policy(entry.as_ref(), self.get_qs_txn())?;
896
897 if !account.is_within_valid_time(ct) {
899 return Err(OperationError::SessionExpired);
901 };
902
903 let scope = AccessScope::ReadOnly;
905
906 let mut limits = Limits::default();
907 if let Some(lim) = account_policy
909 .limit_search_max_results()
910 .and_then(|v| v.try_into().ok())
911 {
912 limits.search_max_results = lim;
913 }
914 if let Some(lim) = account_policy
915 .limit_search_max_filter_test()
916 .and_then(|v| v.try_into().ok())
917 {
918 limits.search_max_filter_test = lim;
919 }
920
921 let certificate_uuid = cert_entry.get_uuid();
922
923 Ok(Identity::new(
924 IdentType::User(IdentUser { entry }),
925 source,
926 certificate_uuid,
928 scope,
929 limits,
930 ))
931 }
932
933 #[instrument(level = "debug", skip_all)]
934 fn client_certificate_to_user_auth_token(
935 &mut self,
936 client_cert_info: &ClientCertInfo,
937 ct: Duration,
938 ) -> Result<UserAuthToken, OperationError> {
939 let cert_entry = self.client_cert_info_entry(client_cert_info)?;
940
941 let refers_uuid = cert_entry
943 .get_ava_single_refer(Attribute::Refers)
944 .ok_or_else(|| {
945 warn!("Invalid certificate entry, missing refers");
946 OperationError::InvalidState
947 })?;
948
949 let entry = self.get_qs_txn().internal_search_uuid(refers_uuid)?;
951
952 let (account, account_policy) =
953 Account::try_from_entry_with_policy(entry.as_ref(), self.get_qs_txn())?;
954
955 if !account.is_within_valid_time(ct) {
957 return Err(OperationError::SessionExpired);
959 };
960
961 let certificate_uuid = cert_entry.get_uuid();
962 let session_is_rw = false;
963
964 account
965 .client_cert_info_to_userauthtoken(certificate_uuid, session_is_rw, ct, &account_policy)
966 .ok_or(OperationError::InvalidState)
967 }
968
969 fn process_ldap_uuid_to_identity(
970 &mut self,
971 uuid: &Uuid,
972 ct: Duration,
973 source: Source,
974 ) -> Result<Identity, OperationError> {
975 let entry = self
976 .get_qs_txn()
977 .internal_search_uuid(*uuid)
978 .map_err(|err| {
979 error!(?err, ?uuid, "Failed to search user by uuid");
980 err
981 })?;
982
983 let (account, account_policy) =
984 Account::try_from_entry_with_policy(entry.as_ref(), self.get_qs_txn())?;
985
986 if !account.is_within_valid_time(ct) {
987 info!("Account is expired or not yet valid.");
988 return Err(OperationError::SessionExpired);
989 }
990
991 let anon_entry = if *uuid == UUID_ANONYMOUS {
993 entry
995 } else {
996 self.get_qs_txn()
998 .internal_search_uuid(UUID_ANONYMOUS)
999 .map_err(|err| {
1000 error!(
1001 ?err,
1002 "Unable to search anonymous user for privilege bounding."
1003 );
1004 err
1005 })?
1006 };
1007
1008 let mut limits = Limits::default();
1009 let session_id = Uuid::new_v4();
1010
1011 if let Some(max_results) = account_policy.limit_search_max_results() {
1013 limits.search_max_results = max_results as usize;
1014 }
1015 if let Some(max_filter) = account_policy.limit_search_max_filter_test() {
1016 limits.search_max_filter_test = max_filter as usize;
1017 }
1018
1019 Ok(Identity::new(
1022 IdentType::User(IdentUser { entry: anon_entry }),
1023 source,
1024 session_id,
1025 AccessScope::ReadOnly,
1026 limits,
1027 ))
1028 }
1029
1030 #[instrument(level = "debug", skip_all)]
1031 fn validate_ldap_session(
1032 &mut self,
1033 session: &LdapSession,
1034 source: Source,
1035 ct: Duration,
1036 ) -> Result<Identity, OperationError> {
1037 match session {
1038 LdapSession::UnixBind(uuid) | LdapSession::ApplicationPasswordBind(_, uuid) => {
1039 self.process_ldap_uuid_to_identity(uuid, ct, source)
1040 }
1041 LdapSession::UserAuthToken(uat) => self.process_uat_to_identity(uat, ct, source),
1042 LdapSession::ApiToken(apit) => {
1043 let entry = self
1044 .get_qs_txn()
1045 .internal_search_uuid(apit.account_id)
1046 .map_err(|e| {
1047 admin_error!("Failed to validate ldap session -> {:?}", e);
1048 e
1049 })?;
1050
1051 self.process_apit_to_identity(apit, source, entry, ct)
1052 }
1053 }
1054 }
1055
1056 #[instrument(level = "info", skip_all)]
1057 fn validate_sync_client_auth_info_to_ident(
1058 &mut self,
1059 client_auth_info: ClientAuthInfo,
1060 ct: Duration,
1061 ) -> Result<Identity, OperationError> {
1062 let jwsu = client_auth_info.bearer_token.ok_or_else(|| {
1065 security_info!("No token provided");
1066 OperationError::NotAuthenticated
1067 })?;
1068
1069 let jws_inner = self
1070 .get_qs_txn()
1071 .get_domain_key_object_handle()?
1072 .jws_verify(&jwsu)
1073 .map_err(|err| {
1074 security_info!(?err, "Unable to verify token");
1075 OperationError::NotAuthenticated
1076 })?;
1077
1078 let sync_token = jws_inner.from_json::<ScimSyncToken>().map_err(|err| {
1079 error!(?err, "Unable to deserialise JWS");
1080 OperationError::SerdeJsonError
1081 })?;
1082
1083 let entry = self
1084 .get_qs_txn()
1085 .internal_search(filter!(f_eq(
1086 Attribute::SyncTokenSession,
1087 PartialValue::Refer(sync_token.token_id)
1088 )))
1089 .and_then(|mut vs| match vs.pop() {
1090 Some(entry) if vs.is_empty() => Ok(entry),
1091 _ => {
1092 admin_error!(
1093 token_id = ?sync_token.token_id,
1094 "entries was empty, or matched multiple results for token id"
1095 );
1096 Err(OperationError::NotAuthenticated)
1097 }
1098 })?;
1099
1100 let valid = SyncAccount::check_sync_token_valid(ct, &sync_token, &entry);
1101
1102 if !valid {
1103 security_info!("Unable to proceed with invalid sync token");
1104 return Err(OperationError::NotAuthenticated);
1105 }
1106
1107 let scope = (&sync_token.purpose).into();
1109
1110 let limits = Limits::unlimited();
1111 Ok(Identity::new(
1112 IdentType::Synch(entry.get_uuid()),
1113 client_auth_info.source,
1114 sync_token.token_id,
1115 scope,
1116 limits,
1117 ))
1118 }
1119}
1120
1121impl<'a> IdmServerTransaction<'a> for IdmServerAuthTransaction<'a> {
1122 type QsTransactionType = QueryServerReadTransaction<'a>;
1123
1124 fn get_qs_txn(&mut self) -> &mut Self::QsTransactionType {
1125 &mut self.qs_read
1126 }
1127}
1128
1129impl IdmServerAuthTransaction<'_> {
1130 #[cfg(test)]
1131 pub fn is_sessionid_present(&self, sessionid: Uuid) -> bool {
1132 let session_read = self.sessions.read();
1133 session_read.contains_key(&sessionid)
1134 }
1135
1136 pub fn get_origin(&self) -> &Url {
1137 #[allow(clippy::unwrap_used)]
1138 self.webauthn.get_allowed_origins().first().unwrap()
1139 }
1140
1141 #[instrument(level = "trace", skip(self))]
1142 pub async fn expire_auth_sessions(&mut self, ct: Duration) {
1143 let expire = ct - Duration::from_secs(AUTH_SESSION_TIMEOUT);
1145 let split_at = uuid_from_duration(expire, self.sid);
1146 let _session_ticket = self.session_ticket.acquire().await;
1148 let mut session_write = self.sessions.write();
1149 session_write.split_off_lt(&split_at);
1150 session_write.commit();
1152 }
1153
1154 pub async fn auth(
1155 &mut self,
1156 ae: &AuthEvent,
1157 ct: Duration,
1158 client_auth_info: ClientAuthInfo,
1159 ) -> Result<AuthResult, OperationError> {
1160 match &ae.step {
1162 AuthEventStep::Init(init) => {
1163 let sessionid = uuid_from_duration(ct, self.sid);
1166
1167 let euuid = self.qs_read.name_to_uuid(init.username.as_str())?;
1183
1184 let entry = self.qs_read.internal_search_uuid(euuid)?;
1186
1187 security_info!(
1188 username = %init.username,
1189 issue = ?init.issue,
1190 privileged = ?init.privileged,
1191 uuid = %euuid,
1192 "Initiating Authentication Session",
1193 );
1194
1195 let (account, account_policy) =
1200 Account::try_from_entry_with_policy(entry.as_ref(), &mut self.qs_read)?;
1201
1202 trace!(?account.primary);
1203
1204 let _session_ticket = self.session_ticket.acquire().await;
1206
1207 let _maybe_slock_ref =
1213 account
1214 .primary_cred_uuid_and_policy()
1215 .map(|(cred_uuid, policy)| {
1216 let mut softlock_write = self.softlocks.write();
1221 let slock_ref: CredSoftLockMutex =
1222 if let Some(slock_ref) = softlock_write.get(&cred_uuid) {
1223 slock_ref.clone()
1224 } else {
1225 let slock = Arc::new(Mutex::new(CredSoftLock::new(policy)));
1227 softlock_write.insert(cred_uuid, slock.clone());
1228 slock
1229 };
1230 softlock_write.commit();
1231 slock_ref
1232 });
1233
1234 let oauth2_client_provider =
1236 account.oauth2_client_provider().and_then(|trust_provider| {
1237 debug!(?trust_provider);
1238 self.oauth2_client_providers.get(&trust_provider.provider)
1240 });
1241
1242 debug!(?oauth2_client_provider);
1243
1244 let asd: AuthSessionData = AuthSessionData {
1245 account,
1246 account_policy,
1247 issue: init.issue,
1248 webauthn: self.webauthn,
1249 ct,
1250 client_auth_info,
1251 oauth2_client_provider,
1252 };
1253
1254 let domain_keys = self.qs_read.get_domain_key_object_handle()?;
1255
1256 let (auth_session, state) = AuthSession::new(asd, init.privileged, domain_keys);
1257
1258 match auth_session {
1259 Some(auth_session) => {
1260 let mut session_write = self.sessions.write();
1261 if session_write.contains_key(&sessionid) {
1262 Err(OperationError::InvalidSessionState)
1265 } else {
1266 session_write.insert(sessionid, Arc::new(Mutex::new(auth_session)));
1267 debug_assert!(session_write.get(&sessionid).is_some());
1269 Ok(())
1270 }?;
1271 session_write.commit();
1272 }
1273 None => {
1274 security_info!("Authentication Session Unable to begin");
1275 }
1276 };
1277
1278 Ok(AuthResult { sessionid, state })
1279 } AuthEventStep::Begin(mech) => {
1281 let session_read = self.sessions.read();
1282 let auth_session_ref = session_read
1284 .get(&mech.sessionid)
1286 .cloned()
1287 .ok_or_else(|| {
1288 admin_error!("Invalid Session State (no present session uuid)");
1289 OperationError::InvalidSessionState
1290 })?;
1291
1292 let mut auth_session = auth_session_ref.lock().await;
1293
1294 let auth_result = auth_session.start_session(&mech.mech);
1296
1297 let is_valid = match auth_session.get_credential_uuid()? {
1298 Some(cred_uuid) => {
1299 let softlock_read = self.softlocks.read();
1302 if let Some(slock_ref) = softlock_read.get(&cred_uuid) {
1303 let mut slock = slock_ref.lock().await;
1304
1305 let softlock_expire_odt = auth_session.account().softlock_expire();
1306
1307 let softlock_expire = softlock_expire_odt
1308 .map(|odt| odt.unix_timestamp() as u64)
1309 .map(Duration::from_secs);
1310
1311 slock.apply_time_step(ct, softlock_expire);
1313 slock.is_valid()
1315 } else {
1316 trace!("slock not found");
1317 false
1318 }
1319 }
1320 None => true,
1321 };
1322
1323 if is_valid {
1324 auth_result
1325 } else {
1326 trace!("lock step begin");
1328 auth_session.end_session("Account is temporarily locked")
1329 }
1330 .map(|aus| AuthResult {
1331 sessionid: mech.sessionid,
1332 state: aus,
1333 })
1334 } AuthEventStep::Cred(creds) => {
1336 let session_read = self.sessions.read();
1340 let auth_session_ref = session_read
1342 .get(&creds.sessionid)
1344 .cloned()
1345 .ok_or_else(|| {
1346 admin_error!("Invalid Session State (no present session uuid)");
1347 OperationError::InvalidSessionState
1348 })?;
1349
1350 let mut auth_session = auth_session_ref.lock().await;
1351
1352 let maybe_slock_ref = match auth_session.get_credential_uuid()? {
1353 Some(cred_uuid) => {
1354 let softlock_read = self.softlocks.read();
1355 softlock_read.get(&cred_uuid).cloned()
1356 }
1357 None => None,
1358 };
1359
1360 let mut maybe_slock = if let Some(s) = maybe_slock_ref.as_ref() {
1363 Some(s.lock().await)
1364 } else {
1365 None
1366 };
1367
1368 let is_valid = if let Some(ref mut slock) = maybe_slock {
1369 slock.apply_time_step(ct, None);
1371 slock.is_valid()
1373 } else {
1374 true
1376 };
1377
1378 if is_valid {
1379 auth_session
1383 .validate_creds(
1384 &creds.cred,
1385 ct,
1386 &self.async_tx,
1387 &self.audit_tx,
1388 self.webauthn,
1389 self.qs_read.pw_badlist(),
1390 )
1391 .inspect(|aus| {
1392 if let AuthState::Denied(_) = aus {
1395 if let Some(ref mut slock) = maybe_slock {
1397 slock.record_failure(ct);
1398 }
1399 };
1400 })
1401 } else {
1402 trace!("lock step cred");
1404 auth_session.end_session("Account is temporarily locked")
1405 }
1406 .map(|aus| AuthResult {
1407 sessionid: creds.sessionid,
1408 state: aus,
1409 })
1410 } }
1412 }
1413
1414 async fn auth_with_unix_pass(
1415 &mut self,
1416 id: Uuid,
1417 cleartext: &str,
1418 ct: Duration,
1419 ) -> Result<Option<Account>, OperationError> {
1420 let entry = match self.qs_read.internal_search_uuid(id) {
1421 Ok(entry) => entry,
1422 Err(e) => {
1423 admin_error!("Failed to start auth unix -> {:?}", e);
1424 return Err(e);
1425 }
1426 };
1427
1428 let (account, acp) =
1429 Account::try_from_entry_with_policy(entry.as_ref(), &mut self.qs_read)?;
1430
1431 if !account.is_within_valid_time(ct) {
1432 security_info!("Account is expired or not yet valid.");
1433 return Ok(None);
1434 }
1435
1436 let softlock_expire_odt = account.softlock_expire();
1437
1438 let softlock_expire = softlock_expire_odt
1439 .map(|odt| odt.unix_timestamp() as u64)
1440 .map(Duration::from_secs);
1441
1442 let cred = if acp.allow_primary_cred_fallback() == Some(true) {
1443 account
1444 .unix_extn()
1445 .and_then(|extn| extn.ucred())
1446 .or_else(|| account.primary())
1447 } else {
1448 account.unix_extn().and_then(|extn| extn.ucred())
1449 };
1450
1451 let (cred, cred_id, cred_slock_policy) = match cred {
1452 None => {
1453 if acp.allow_primary_cred_fallback() == Some(true) {
1454 security_info!("Account does not have a POSIX or primary password configured.");
1455 } else {
1456 security_info!("Account does not have a POSIX password configured.");
1457 }
1458 return Ok(None);
1459 }
1460 Some(cred) => (cred, cred.uuid, cred.softlock_policy()),
1461 };
1462
1463 let Ok(password) = cred.password_ref() else {
1465 error!("User's UNIX or primary credential is not a password, can't authenticate!");
1466 return Err(OperationError::InvalidState);
1467 };
1468
1469 let slock_ref = {
1470 let softlock_read = self.softlocks.read();
1471 if let Some(slock_ref) = softlock_read.get(&cred_id) {
1472 slock_ref.clone()
1473 } else {
1474 let _session_ticket = self.session_ticket.acquire().await;
1475 let mut softlock_write = self.softlocks.write();
1476 let slock = Arc::new(Mutex::new(CredSoftLock::new(cred_slock_policy)));
1477 softlock_write.insert(cred_id, slock.clone());
1478 softlock_write.commit();
1479 slock
1480 }
1481 };
1482
1483 let mut slock = slock_ref.lock().await;
1484
1485 slock.apply_time_step(ct, softlock_expire);
1486
1487 if !slock.is_valid() {
1488 security_info!("Account is softlocked.");
1489 return Ok(None);
1490 }
1491
1492 let valid = password.verify(cleartext).map_err(|e| {
1494 error!(crypto_err = ?e);
1495 OperationError::CryptographyError
1496 })?;
1497
1498 if !valid {
1499 slock.record_failure(ct);
1501
1502 return Ok(None);
1503 }
1504
1505 security_info!("Successfully authenticated with unix (or primary) password");
1506 if password.requires_upgrade() {
1507 self.async_tx
1508 .send(DelayedAction::UnixPwUpgrade(UnixPasswordUpgrade {
1509 target_uuid: id,
1510 existing_password: cleartext.to_string(),
1511 }))
1512 .map_err(|_| {
1513 admin_error!("failed to queue delayed action - unix password upgrade");
1514 OperationError::InvalidState
1515 })?;
1516 }
1517
1518 Ok(Some(account))
1519 }
1520
1521 pub async fn auth_unix(
1522 &mut self,
1523 uae: &UnixUserAuthEvent,
1524 ct: Duration,
1525 ) -> Result<Option<UnixUserToken>, OperationError> {
1526 Ok(self
1527 .auth_with_unix_pass(uae.target, &uae.cleartext, ct)
1528 .await?
1529 .and_then(|acc| acc.to_unixusertoken(ct).ok()))
1530 }
1531
1532 pub async fn auth_ldap(
1533 &mut self,
1534 lae: &LdapAuthEvent,
1535 ct: Duration,
1536 ) -> Result<Option<LdapBoundToken>, OperationError> {
1537 if lae.target == UUID_ANONYMOUS {
1538 let account_entry = self.qs_read.internal_search_uuid(lae.target).map_err(|e| {
1539 admin_error!("Failed to start auth ldap -> {:?}", e);
1540 e
1541 })?;
1542
1543 let account = Account::try_from_entry_ro(account_entry.as_ref(), &mut self.qs_read)?;
1544
1545 if !account.is_within_valid_time(ct) {
1547 security_info!("Account is not within valid time period");
1548 return Ok(None);
1549 }
1550
1551 let session_id = Uuid::new_v4();
1552 security_info!(
1553 "Starting session {} for {} {}",
1554 session_id,
1555 account.spn(),
1556 account.uuid
1557 );
1558
1559 Ok(Some(LdapBoundToken {
1561 session_id,
1562 spn: account.spn().into(),
1563 effective_session: LdapSession::UnixBind(UUID_ANONYMOUS),
1564 }))
1565 } else {
1566 if !self.qs_read.d_info.d_ldap_allow_unix_pw_bind {
1567 security_info!("Bind not allowed through Unix passwords.");
1568 return Ok(None);
1569 }
1570
1571 let auth = self
1572 .auth_with_unix_pass(lae.target, &lae.cleartext, ct)
1573 .await?;
1574
1575 match auth {
1576 Some(account) => {
1577 let session_id = Uuid::new_v4();
1578 security_info!(
1579 "Starting session {} for {} {}",
1580 session_id,
1581 account.spn(),
1582 account.uuid
1583 );
1584
1585 Ok(Some(LdapBoundToken {
1586 spn: account.spn().into(),
1587 session_id,
1588 effective_session: LdapSession::UnixBind(account.uuid),
1589 }))
1590 }
1591 None => Ok(None),
1592 }
1593 }
1594 }
1595
1596 pub async fn token_auth_ldap(
1597 &mut self,
1598 lae: &LdapTokenAuthEvent,
1599 ct: Duration,
1600 ) -> Result<Option<LdapBoundToken>, OperationError> {
1601 match self.validate_and_parse_token_to_identity_token(&lae.token, ct)? {
1602 Token::UserAuthToken(uat) => {
1603 let spn = uat.spn.clone();
1604 Ok(Some(LdapBoundToken {
1605 session_id: uat.session_id,
1606 spn,
1607 effective_session: LdapSession::UserAuthToken(uat),
1608 }))
1609 }
1610 Token::ApiToken(apit, entry) => {
1611 let spn = entry
1612 .get_ava_single_proto_string(Attribute::Spn)
1613 .ok_or_else(|| OperationError::MissingAttribute(Attribute::Spn))?;
1614
1615 Ok(Some(LdapBoundToken {
1616 session_id: apit.token_id,
1617 spn,
1618 effective_session: LdapSession::ApiToken(apit),
1619 }))
1620 }
1621 }
1622 }
1623
1624 pub fn commit(self) -> Result<(), OperationError> {
1625 Ok(())
1626 }
1627}
1628
1629impl<'a> IdmServerTransaction<'a> for IdmServerProxyReadTransaction<'a> {
1630 type QsTransactionType = QueryServerReadTransaction<'a>;
1631
1632 fn get_qs_txn(&mut self) -> &mut Self::QsTransactionType {
1633 &mut self.qs_read
1634 }
1635}
1636
1637fn gen_password_mod(
1638 cleartext: &str,
1639 crypto_policy: &CryptoPolicy,
1640) -> Result<ModifyList<ModifyInvalid>, OperationError> {
1641 let new_cred = Credential::new_password_only(crypto_policy, cleartext)?;
1642 let cred_value = Value::new_credential("unix", new_cred);
1643 Ok(ModifyList::new_purge_and_set(
1644 Attribute::UnixPassword,
1645 cred_value,
1646 ))
1647}
1648
1649fn gen_password_upgrade_mod(
1650 unix_cred: &Credential,
1651 cleartext: &str,
1652 crypto_policy: &CryptoPolicy,
1653) -> Result<Option<ModifyList<ModifyInvalid>>, OperationError> {
1654 if let Some(new_cred) = unix_cred.upgrade_password(crypto_policy, cleartext)? {
1655 let cred_value = Value::new_credential("primary", new_cred);
1656 Ok(Some(ModifyList::new_purge_and_set(
1657 Attribute::UnixPassword,
1658 cred_value,
1659 )))
1660 } else {
1661 Ok(None)
1663 }
1664}
1665
1666impl IdmServerProxyReadTransaction<'_> {
1667 pub fn jws_public_jwk(&mut self, key_id: &str) -> Result<Jwk, OperationError> {
1668 self.qs_read
1669 .get_key_providers()
1670 .get_key_object_handle(UUID_DOMAIN_INFO)
1671 .ok_or(OperationError::NoMatchingEntries)
1673 .and_then(|key_object| key_object.jws_public_jwk(key_id))
1674 .and_then(|maybe_key: Option<Jwk>| maybe_key.ok_or(OperationError::NoMatchingEntries))
1675 }
1676
1677 pub fn get_radiusauthtoken(
1678 &mut self,
1679 rate: &RadiusAuthTokenEvent,
1680 ct: Duration,
1681 ) -> Result<RadiusAuthToken, OperationError> {
1682 let account = self
1683 .qs_read
1684 .impersonate_search_ext_uuid(rate.target, &rate.ident)
1685 .and_then(|account_entry| {
1686 RadiusAccount::try_from_entry_reduced(&account_entry, &mut self.qs_read)
1687 })
1688 .map_err(|e| {
1689 admin_error!("Failed to start radius auth token {:?}", e);
1690 e
1691 })?;
1692
1693 account.to_radiusauthtoken(ct)
1694 }
1695
1696 pub fn get_unixusertoken(
1697 &mut self,
1698 uute: &UnixUserTokenEvent,
1699 ct: Duration,
1700 ) -> Result<UnixUserToken, OperationError> {
1701 let account = self
1702 .qs_read
1703 .impersonate_search_uuid(uute.target, &uute.ident)
1704 .and_then(|account_entry| Account::try_from_entry_ro(&account_entry, &mut self.qs_read))
1705 .map_err(|e| {
1706 admin_error!("Failed to start unix user token -> {:?}", e);
1707 e
1708 })?;
1709
1710 account.to_unixusertoken(ct)
1711 }
1712
1713 pub fn get_unixgrouptoken(
1714 &mut self,
1715 uute: &UnixGroupTokenEvent,
1716 ) -> Result<UnixGroupToken, OperationError> {
1717 let group = self
1718 .qs_read
1719 .impersonate_search_ext_uuid(uute.target, &uute.ident)
1720 .and_then(|e| Group::<Unix>::try_from_entry(&e))
1721 .map_err(|e| {
1722 admin_error!("Failed to start unix group token {:?}", e);
1723 e
1724 })?;
1725 Ok(group.to_unixgrouptoken())
1726 }
1727
1728 pub fn get_credentialstatus(
1729 &mut self,
1730 cse: &CredentialStatusEvent,
1731 ) -> Result<CredentialStatus, OperationError> {
1732 let account = self
1733 .qs_read
1734 .impersonate_search_ext_uuid(cse.target, &cse.ident)
1735 .and_then(|account_entry| {
1736 Account::try_from_entry_reduced(&account_entry, &mut self.qs_read)
1737 })
1738 .map_err(|e| {
1739 admin_error!("Failed to search account {:?}", e);
1740 e
1741 })?;
1742
1743 account.to_credentialstatus()
1744 }
1745}
1746
1747impl<'a> IdmServerTransaction<'a> for IdmServerProxyWriteTransaction<'a> {
1748 type QsTransactionType = QueryServerWriteTransaction<'a>;
1749
1750 fn get_qs_txn(&mut self) -> &mut Self::QsTransactionType {
1751 &mut self.qs_write
1752 }
1753}
1754
1755impl IdmServerProxyWriteTransaction<'_> {
1756 pub(crate) fn crypto_policy(&self) -> &CryptoPolicy {
1757 self.crypto_policy
1758 }
1759
1760 pub fn get_origin(&self) -> &Url {
1761 #[allow(clippy::unwrap_used)]
1762 self.webauthn.get_allowed_origins().first().unwrap()
1763 }
1764
1765 fn check_password_quality(
1766 &mut self,
1767 cleartext: &str,
1768 related_inputs: &[&str],
1769 ) -> Result<(), OperationError> {
1770 if cleartext.len() < PW_MIN_LENGTH as usize {
1776 return Err(OperationError::PasswordQuality(vec![
1777 PasswordFeedback::TooShort(PW_MIN_LENGTH),
1778 ]));
1779 }
1780
1781 let entropy = zxcvbn(cleartext, related_inputs);
1784
1785 if entropy.score() < Score::Four {
1787 let feedback: zxcvbn::feedback::Feedback = entropy
1790 .feedback()
1791 .ok_or(OperationError::InvalidState)
1792 .cloned()
1793 .inspect_err(|err| {
1794 security_info!(?err, "zxcvbn returned no feedback when score < 3");
1795 })?;
1796
1797 security_info!(?feedback, "pw quality feedback");
1798
1799 return Err(OperationError::PasswordQuality(vec![
1802 PasswordFeedback::BadListed,
1803 ]));
1804 }
1805
1806 if self
1810 .qs_write
1811 .pw_badlist()
1812 .contains(&cleartext.to_lowercase())
1813 {
1814 security_info!("Password found in badlist, rejecting");
1815 Err(OperationError::PasswordQuality(vec![
1816 PasswordFeedback::BadListed,
1817 ]))
1818 } else {
1819 Ok(())
1820 }
1821 }
1822
1823 pub(crate) fn target_to_account(&mut self, target: Uuid) -> Result<Account, OperationError> {
1824 let account = self
1826 .qs_write
1827 .internal_search_uuid(target)
1828 .and_then(|account_entry| {
1829 Account::try_from_entry_rw(&account_entry, &mut self.qs_write)
1830 })
1831 .map_err(|e| {
1832 admin_error!("Failed to search account {:?}", e);
1833 e
1834 })?;
1835 if account.is_anonymous() {
1839 admin_warn!("Unable to convert anonymous to account during write txn");
1840 Err(OperationError::SystemProtectedObject)
1841 } else {
1842 Ok(account)
1843 }
1844 }
1845
1846 #[cfg(test)]
1847 pub(crate) fn set_account_password(
1848 &mut self,
1849 pce: &PasswordChangeEvent,
1850 ) -> Result<(), OperationError> {
1851 let account = self.target_to_account(pce.target)?;
1852
1853 let modlist = account
1855 .gen_password_mod(pce.cleartext.as_str(), self.crypto_policy)
1856 .map_err(|e| {
1857 admin_error!("Failed to generate password mod {:?}", e);
1858 e
1859 })?;
1860 trace!(?modlist, "processing change");
1861
1862 let me = self
1865 .qs_write
1866 .impersonate_modify_gen_event(
1867 &filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(pce.target))),
1869 &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(pce.target))),
1871 &modlist,
1872 &pce.ident,
1873 )
1874 .map_err(|e| {
1875 request_error!(error = ?e);
1876 e
1877 })?;
1878
1879 let mp = self
1880 .qs_write
1881 .modify_pre_apply(&me)
1882 .and_then(|opt_mp| opt_mp.ok_or(OperationError::NoMatchingEntries))
1883 .map_err(|e| {
1884 request_error!(error = ?e);
1885 e
1886 })?;
1887
1888 self.qs_write.modify_apply(mp).map_err(|e| {
1893 request_error!(error = ?e);
1894 e
1895 })?;
1896
1897 Ok(())
1898 }
1899
1900 pub fn set_unix_account_password(
1901 &mut self,
1902 pce: &UnixPasswordChangeEvent,
1903 ) -> Result<(), OperationError> {
1904 let account = self
1906 .qs_write
1907 .internal_search_uuid(pce.target)
1908 .and_then(|account_entry| {
1909 Account::try_from_entry_rw(&account_entry, &mut self.qs_write)
1911 })
1912 .map_err(|e| {
1913 admin_error!("Failed to start set unix account password {:?}", e);
1914 e
1915 })?;
1916
1917 if account.unix_extn().is_none() {
1919 return Err(OperationError::MissingClass(
1920 ENTRYCLASS_POSIX_ACCOUNT.into(),
1921 ));
1922 }
1923
1924 if account.is_anonymous() {
1926 trace!("Unable to use anonymous to change UNIX account password");
1927 return Err(OperationError::SystemProtectedObject);
1928 }
1929
1930 let modlist =
1931 gen_password_mod(pce.cleartext.as_str(), self.crypto_policy).map_err(|e| {
1932 admin_error!(?e, "Unable to generate password change modlist");
1933 e
1934 })?;
1935 trace!(?modlist, "processing change");
1936
1937 let me = self
1940 .qs_write
1941 .impersonate_modify_gen_event(
1942 &filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(pce.target))),
1944 &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(pce.target))),
1946 &modlist,
1947 &pce.ident,
1948 )
1949 .map_err(|e| {
1950 request_error!(error = ?e);
1951 e
1952 })?;
1953
1954 let mp = self
1955 .qs_write
1956 .modify_pre_apply(&me)
1957 .and_then(|opt_mp| opt_mp.ok_or(OperationError::NoMatchingEntries))
1958 .map_err(|e| {
1959 request_error!(error = ?e);
1960 e
1961 })?;
1962
1963 self.check_password_quality(pce.cleartext.as_str(), account.related_inputs().as_slice())
1967 .map_err(|e| {
1968 admin_error!(?e, "Failed to checked password quality");
1969 e
1970 })?;
1971
1972 self.qs_write.modify_apply(mp).map_err(|e| {
1974 request_error!(error = ?e);
1975 e
1976 })?;
1977
1978 Ok(())
1979 }
1980
1981 #[instrument(level = "debug", skip_all)]
1982 pub fn recover_account(
1983 &mut self,
1984 name: &str,
1985 cleartext: Option<&str>,
1986 ) -> Result<String, OperationError> {
1987 let target = self.qs_write.name_to_uuid(name).inspect_err(|err| {
1989 error!(?err, "name to uuid failed");
1990 })?;
1991
1992 let cleartext = cleartext
1993 .map(|s| s.to_string())
1994 .unwrap_or_else(password_from_random);
1995
1996 let ncred = Credential::new_generatedpassword_only(self.crypto_policy, &cleartext)
1997 .inspect_err(|err| {
1998 error!(?err, "unable to generate password modification");
1999 })?;
2000 let vcred = Value::new_credential("primary", ncred);
2001 let v_valid_from = Value::new_datetime_epoch(self.qs_write.get_curtime());
2002
2003 let modlist = ModifyList::new_list(vec![
2004 m_purge(Attribute::AccountExpire),
2006 m_purge(Attribute::AccountValidFrom),
2007 Modify::Present(Attribute::AccountValidFrom, v_valid_from),
2008 m_purge(Attribute::PassKeys),
2010 m_purge(Attribute::PrimaryCredential),
2011 Modify::Present(Attribute::PrimaryCredential, vcred),
2012 ]);
2013
2014 trace!(?modlist, "processing change");
2015
2016 self.qs_write
2017 .internal_modify(
2018 &filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(target))),
2020 &modlist,
2021 )
2022 .inspect_err(|err| {
2023 error!(?err);
2024 })?;
2025
2026 Ok(cleartext)
2027 }
2028
2029 #[instrument(level = "debug", skip(self))]
2030 pub fn disable_account(&mut self, name: &str) -> Result<(), OperationError> {
2031 let target = self.qs_write.name_to_uuid(name).inspect_err(|err| {
2033 error!(?err, "name to uuid failed");
2034 })?;
2035
2036 let v_expire = Value::new_datetime_epoch(self.qs_write.get_curtime());
2037
2038 let modlist = ModifyList::new_list(vec![
2039 m_purge(Attribute::AccountValidFrom),
2041 m_purge(Attribute::AccountExpire),
2042 Modify::Present(Attribute::AccountExpire, v_expire),
2043 ]);
2044
2045 trace!(?modlist, "processing change");
2046
2047 self.qs_write
2048 .internal_modify(
2049 &filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(target))),
2051 &modlist,
2052 )
2053 .inspect_err(|err| {
2054 error!(?err);
2055 })?;
2056
2057 Ok(())
2058 }
2059
2060 #[instrument(level = "debug", skip_all)]
2061 pub fn regenerate_radius_secret(
2062 &mut self,
2063 rrse: &RegenerateRadiusSecretEvent,
2064 ) -> Result<String, OperationError> {
2065 let account = self.target_to_account(rrse.target)?;
2066
2067 let cleartext = readable_password_from_random();
2070
2071 let modlist = account
2073 .regenerate_radius_secret_mod(cleartext.as_str())
2074 .map_err(|e| {
2075 admin_error!("Unable to generate radius secret mod {:?}", e);
2076 e
2077 })?;
2078 trace!(?modlist, "processing change");
2079
2080 self.qs_write
2082 .impersonate_modify(
2083 &filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(rrse.target))),
2085 &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(rrse.target))),
2087 &modlist,
2088 &rrse.ident,
2090 )
2091 .map_err(|e| {
2092 request_error!(error = ?e);
2093 e
2094 })
2095 .map(|_| cleartext)
2096 }
2097
2098 #[instrument(level = "debug", skip_all)]
2100 fn process_pwupgrade(&mut self, pwu: &PasswordUpgrade) -> Result<(), OperationError> {
2101 let account = self.target_to_account(pwu.target_uuid)?;
2103
2104 info!(session_id = %pwu.target_uuid, "Processing password hash upgrade");
2105
2106 let maybe_modlist = account
2107 .gen_password_upgrade_mod(pwu.existing_password.as_str(), self.crypto_policy)
2108 .map_err(|e| {
2109 admin_error!("Unable to generate password mod {:?}", e);
2110 e
2111 })?;
2112
2113 if let Some(modlist) = maybe_modlist {
2114 self.qs_write.internal_modify(
2115 &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(pwu.target_uuid))),
2116 &modlist,
2117 )
2118 } else {
2119 Ok(())
2121 }
2122 }
2123
2124 #[instrument(level = "debug", skip_all)]
2125 fn process_unixpwupgrade(&mut self, pwu: &UnixPasswordUpgrade) -> Result<(), OperationError> {
2126 info!(session_id = %pwu.target_uuid, "Processing unix password hash upgrade");
2127
2128 let account = self
2129 .qs_write
2130 .internal_search_uuid(pwu.target_uuid)
2131 .and_then(|account_entry| {
2132 Account::try_from_entry_rw(&account_entry, &mut self.qs_write)
2133 })
2134 .map_err(|e| {
2135 admin_error!("Failed to start unix pw upgrade -> {:?}", e);
2136 e
2137 })?;
2138
2139 let cred = match account.unix_extn() {
2140 Some(ue) => ue.ucred(),
2141 None => {
2142 return Err(OperationError::MissingClass(
2143 ENTRYCLASS_POSIX_ACCOUNT.into(),
2144 ));
2145 }
2146 };
2147
2148 let Some(cred) = cred else {
2150 return Ok(());
2151 };
2152
2153 let maybe_modlist =
2154 gen_password_upgrade_mod(cred, pwu.existing_password.as_str(), self.crypto_policy)?;
2155
2156 match maybe_modlist {
2157 Some(modlist) => self.qs_write.internal_modify(
2158 &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(pwu.target_uuid))),
2159 &modlist,
2160 ),
2161 None => Ok(()),
2162 }
2163 }
2164
2165 #[instrument(level = "debug", skip_all)]
2166 pub(crate) fn process_webauthncounterinc(
2167 &mut self,
2168 wci: &WebauthnCounterIncrement,
2169 ) -> Result<(), OperationError> {
2170 info!(session_id = %wci.target_uuid, "Processing webauthn counter increment");
2171
2172 let mut account = self.target_to_account(wci.target_uuid)?;
2173
2174 let opt_modlist = account
2176 .gen_webauthn_counter_mod(&wci.auth_result)
2177 .map_err(|e| {
2178 admin_error!("Unable to generate webauthn counter mod {:?}", e);
2179 e
2180 })?;
2181
2182 if let Some(modlist) = opt_modlist {
2183 self.qs_write.internal_modify(
2184 &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(wci.target_uuid))),
2185 &modlist,
2186 )
2187 } else {
2188 trace!("No modification required");
2190 Ok(())
2191 }
2192 }
2193
2194 #[instrument(level = "debug", skip_all)]
2195 pub(crate) fn process_backupcoderemoval(
2196 &mut self,
2197 bcr: &BackupCodeRemoval,
2198 ) -> Result<(), OperationError> {
2199 info!(session_id = %bcr.target_uuid, "Processing backup code removal");
2200
2201 let account = self.target_to_account(bcr.target_uuid)?;
2202 let modlist = account
2204 .invalidate_backup_code_mod(&bcr.code_to_remove)
2205 .map_err(|e| {
2206 admin_error!("Unable to generate backup code mod {:?}", e);
2207 e
2208 })?;
2209
2210 self.qs_write.internal_modify(
2211 &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(bcr.target_uuid))),
2212 &modlist,
2213 )
2214 }
2215
2216 #[instrument(level = "debug", skip_all)]
2217 pub(crate) fn process_authsessionrecord(
2218 &mut self,
2219 asr: &AuthSessionRecord,
2220 ) -> Result<(), OperationError> {
2221 let state = match asr.expiry {
2223 Some(e) => SessionState::ExpiresAt(e),
2224 None => SessionState::NeverExpires,
2225 };
2226
2227 let session = Value::Session(
2228 asr.session_id,
2229 Session {
2230 label: asr.label.clone(),
2231 state,
2232 issued_at: asr.issued_at,
2235 issued_by: asr.issued_by.clone(),
2237 cred_id: asr.cred_id,
2239 scope: asr.scope,
2242 type_: asr.type_,
2243 ext_metadata: Default::default(),
2244 },
2245 );
2246
2247 info!(session_id = %asr.session_id, "Persisting auth session");
2248
2249 let modlist = ModifyList::new_append(Attribute::UserAuthTokenSession, session);
2251
2252 self.qs_write
2253 .internal_modify(
2254 &filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(asr.target_uuid))),
2255 &modlist,
2256 )
2257 .map_err(|e| {
2258 admin_error!("Failed to persist user auth token {:?}", e);
2259 e
2260 })
2261 }
2263
2264 #[instrument(level = "debug", skip_all)]
2265 pub fn process_delayedaction(
2266 &mut self,
2267 da: &DelayedAction,
2268 _ct: Duration,
2269 ) -> Result<(), OperationError> {
2270 match da {
2271 DelayedAction::PwUpgrade(pwu) => self.process_pwupgrade(pwu),
2272 DelayedAction::UnixPwUpgrade(upwu) => self.process_unixpwupgrade(upwu),
2273 DelayedAction::WebauthnCounterIncrement(wci) => self.process_webauthncounterinc(wci),
2274 DelayedAction::BackupCodeRemoval(bcr) => self.process_backupcoderemoval(bcr),
2275 DelayedAction::AuthSessionRecord(asr) => self.process_authsessionrecord(asr),
2276 }
2277 }
2278
2279 fn reload_applications(&mut self) -> Result<(), OperationError> {
2280 self.qs_write
2281 .get_applications_set()
2282 .and_then(|application_set| self.applications.reload(application_set))
2283 }
2284
2285 fn reload_oauth2(&mut self) -> Result<(), OperationError> {
2286 let domain_level = self.qs_write.get_domain_version();
2287 self.qs_write.get_oauth2rs_set().and_then(|oauth2rs_set| {
2288 let key_providers = self.qs_write.get_key_providers();
2289 self.oauth2rs
2290 .reload(oauth2rs_set, key_providers, domain_level)
2291 })?;
2292 self.qs_write.clear_changed_oauth2();
2294 Ok(())
2295 }
2296
2297 #[instrument(level = "debug", skip_all)]
2298 pub fn commit(mut self) -> Result<(), OperationError> {
2299 self.qs_write.reload()?;
2302
2303 if self.qs_write.get_changed_app() {
2305 self.reload_applications()?;
2306 }
2307
2308 if self.qs_write.get_changed_oauth2() {
2309 self.reload_oauth2()?;
2310 }
2311
2312 if self.qs_write.get_changed_oauth2_client() {
2313 self.reload_oauth2_client_providers()?;
2314 }
2315
2316 self.applications.commit();
2318 self.oauth2rs.commit();
2319 self.cred_update_sessions.commit();
2320 self.oauth2_client_providers.commit();
2321
2322 trace!("cred_update_session.commit");
2323 self.qs_write.commit()
2324 }
2325}
2326
2327#[cfg(test)]
2330mod tests {
2331 use std::convert::TryFrom;
2332 use std::time::Duration;
2333
2334 use crate::credential::{Credential, Password};
2335 use crate::idm::account::DestroySessionTokenEvent;
2336 use crate::idm::accountpolicy::ResolvedAccountPolicy;
2337 use crate::idm::audit::AuditEvent;
2338 use crate::idm::authentication::AuthState;
2339 use crate::idm::delayed::{AuthSessionRecord, DelayedAction};
2340 use crate::idm::event::{AuthEvent, AuthResult};
2341 use crate::idm::event::{
2342 LdapAuthEvent, PasswordChangeEvent, RadiusAuthTokenEvent, RegenerateRadiusSecretEvent,
2343 UnixGroupTokenEvent, UnixPasswordChangeEvent, UnixUserAuthEvent, UnixUserTokenEvent,
2344 };
2345 use crate::idm::server::{IdmServer, IdmServerTransaction, Token};
2346 use crate::modify::{Modify, ModifyList};
2347 use crate::prelude::*;
2348 use crate::server::keys::KeyProvidersTransaction;
2349 use crate::value::{AuthType, SessionState};
2350 use compact_jwt::{traits::JwsVerifiable, JwsCompact, JwsEs256Verifier, JwsVerifier};
2351 use kanidm_lib_crypto::CryptoPolicy;
2352 use kanidm_proto::v1::{AuthAllowed, AuthIssueSession, AuthMech};
2353 use time::OffsetDateTime;
2354 use uuid::Uuid;
2355
2356 const TEST_PASSWORD: &str = "ntaoeuntnaoeuhraohuercahu😍";
2357 const TEST_PASSWORD_INC: &str = "ntaoentu nkrcgaeunhibwmwmqj;k wqjbkx ";
2358 const TEST_CURRENT_TIME: u64 = 6000;
2359
2360 #[idm_test]
2361 async fn test_idm_anonymous_auth(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2362 let mut idms_auth = idms.auth().await.unwrap();
2364 let anon_init = AuthEvent::anonymous_init();
2366 let r1 = idms_auth
2368 .auth(
2369 &anon_init,
2370 Duration::from_secs(TEST_CURRENT_TIME),
2371 Source::Internal.into(),
2372 )
2373 .await;
2374 let sid = match r1 {
2377 Ok(ar) => {
2378 let AuthResult { sessionid, state } = ar;
2379 match state {
2380 AuthState::Choose(mut conts) => {
2381 assert_eq!(conts.len(), 1);
2383 let m = conts.pop().expect("Should not fail");
2385 assert_eq!(m, AuthMech::Anonymous);
2386 }
2387 _ => {
2388 error!("A critical error has occurred! We have a non-continue result!");
2389 panic!();
2390 }
2391 };
2392 sessionid
2394 }
2395 Err(e) => {
2396 error!("A critical error has occurred! {:?}", e);
2398 panic!();
2399 }
2400 };
2401
2402 debug!("sessionid is ==> {:?}", sid);
2403
2404 idms_auth.commit().expect("Must not fail");
2405
2406 let mut idms_auth = idms.auth().await.unwrap();
2407 let anon_begin = AuthEvent::begin_mech(sid, AuthMech::Anonymous);
2408
2409 let r2 = idms_auth
2410 .auth(
2411 &anon_begin,
2412 Duration::from_secs(TEST_CURRENT_TIME),
2413 Source::Internal.into(),
2414 )
2415 .await;
2416 debug!("r2 ==> {:?}", r2);
2417
2418 match r2 {
2419 Ok(ar) => {
2420 let AuthResult {
2421 sessionid: _,
2422 state,
2423 } = ar;
2424
2425 match state {
2426 AuthState::Continue(allowed) => {
2427 assert_eq!(allowed.len(), 1);
2429 assert_eq!(allowed.first(), Some(&AuthAllowed::Anonymous));
2430 }
2431 _ => {
2432 error!("A critical error has occurred! We have a non-continue result!");
2433 panic!();
2434 }
2435 }
2436 }
2437 Err(e) => {
2438 error!("A critical error has occurred! {:?}", e);
2439 panic!();
2441 }
2442 };
2443
2444 idms_auth.commit().expect("Must not fail");
2445
2446 let mut idms_auth = idms.auth().await.unwrap();
2447 let anon_step = AuthEvent::cred_step_anonymous(sid);
2449
2450 let r2 = idms_auth
2452 .auth(
2453 &anon_step,
2454 Duration::from_secs(TEST_CURRENT_TIME),
2455 Source::Internal.into(),
2456 )
2457 .await;
2458 debug!("r2 ==> {:?}", r2);
2459
2460 match r2 {
2461 Ok(ar) => {
2462 let AuthResult {
2463 sessionid: _,
2464 state,
2465 } = ar;
2466
2467 match state {
2468 AuthState::Success(_uat, AuthIssueSession::Token) => {
2469 }
2471 _ => {
2472 error!("A critical error has occurred! We have a non-success result!");
2473 panic!();
2474 }
2475 }
2476 }
2477 Err(e) => {
2478 error!("A critical error has occurred! {:?}", e);
2479 panic!();
2481 }
2482 };
2483
2484 idms_auth.commit().expect("Must not fail");
2485 }
2486
2487 #[idm_test]
2489 async fn test_idm_anonymous_auth_invalid_states(
2490 idms: &IdmServer,
2491 _idms_delayed: &IdmServerDelayed,
2492 ) {
2493 {
2494 let mut idms_auth = idms.auth().await.unwrap();
2495 let sid = Uuid::new_v4();
2496 let anon_step = AuthEvent::cred_step_anonymous(sid);
2497
2498 let r2 = idms_auth
2500 .auth(
2501 &anon_step,
2502 Duration::from_secs(TEST_CURRENT_TIME),
2503 Source::Internal.into(),
2504 )
2505 .await;
2506 debug!("r2 ==> {:?}", r2);
2507
2508 match r2 {
2509 Ok(_) => {
2510 error!("Auth state machine not correctly enforced!");
2511 panic!();
2512 }
2513 Err(e) => match e {
2514 OperationError::InvalidSessionState => {}
2515 _ => panic!(),
2516 },
2517 };
2518 }
2519 }
2520
2521 async fn init_testperson_w_password(
2522 idms: &IdmServer,
2523 pw: &str,
2524 ) -> Result<Uuid, OperationError> {
2525 let p = CryptoPolicy::minimum();
2526 let cred = Credential::new_password_only(&p, pw)?;
2527 let cred_id = cred.uuid;
2528 let v_cred = Value::new_credential("primary", cred);
2529 let mut idms_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2530
2531 idms_write
2532 .qs_write
2533 .internal_create(vec![E_TESTPERSON_1.clone()])
2534 .expect("Failed to create test person");
2535
2536 let me_inv_m = ModifyEvent::new_internal_invalid(
2538 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_TESTPERSON_1))),
2539 ModifyList::new_list(vec![Modify::Present(Attribute::PrimaryCredential, v_cred)]),
2540 );
2541 assert!(idms_write.qs_write.modify(&me_inv_m).is_ok());
2543
2544 idms_write.commit().map(|()| cred_id)
2545 }
2546
2547 async fn init_authsession_sid(idms: &IdmServer, ct: Duration, name: &str) -> Uuid {
2548 let mut idms_auth = idms.auth().await.unwrap();
2549 let admin_init = AuthEvent::named_init(name);
2550
2551 let r1 = idms_auth
2552 .auth(&admin_init, ct, Source::Internal.into())
2553 .await;
2554 let ar = r1.unwrap();
2555 let AuthResult { sessionid, state } = ar;
2556
2557 assert!(matches!(state, AuthState::Choose(_)));
2558
2559 let admin_begin = AuthEvent::begin_mech(sessionid, AuthMech::Password);
2561
2562 let r2 = idms_auth
2563 .auth(&admin_begin, ct, Source::Internal.into())
2564 .await;
2565 let ar = r2.unwrap();
2566 let AuthResult { sessionid, state } = ar;
2567
2568 match state {
2569 AuthState::Continue(_) => {}
2570 s => {
2571 error!(?s, "Sessions was not initialised");
2572 panic!();
2573 }
2574 };
2575
2576 idms_auth.commit().expect("Must not fail");
2577
2578 sessionid
2579 }
2580
2581 async fn check_testperson_password(idms: &IdmServer, pw: &str, ct: Duration) -> JwsCompact {
2582 let sid = init_authsession_sid(idms, ct, "testperson1").await;
2583
2584 let mut idms_auth = idms.auth().await.unwrap();
2585 let anon_step = AuthEvent::cred_step_password(sid, pw);
2586
2587 let r2 = idms_auth
2589 .auth(&anon_step, ct, Source::Internal.into())
2590 .await;
2591 debug!("r2 ==> {:?}", r2);
2592
2593 let token = match r2 {
2594 Ok(ar) => {
2595 let AuthResult {
2596 sessionid: _,
2597 state,
2598 } = ar;
2599
2600 match state {
2601 AuthState::Success(token, AuthIssueSession::Token) => {
2602 token
2604 }
2605 _ => {
2606 error!("A critical error has occurred! We have a non-success result!");
2607 panic!();
2608 }
2609 }
2610 }
2611 Err(e) => {
2612 error!("A critical error has occurred! {:?}", e);
2613 panic!();
2615 }
2616 };
2617
2618 idms_auth.commit().expect("Must not fail");
2619
2620 *token
2621 }
2622
2623 #[idm_test]
2624 async fn test_idm_simple_password_auth(idms: &IdmServer, idms_delayed: &mut IdmServerDelayed) {
2625 let ct = duration_from_epoch_now();
2626 init_testperson_w_password(idms, TEST_PASSWORD)
2627 .await
2628 .expect("Failed to setup admin account");
2629 check_testperson_password(idms, TEST_PASSWORD, ct).await;
2630
2631 let da = idms_delayed.try_recv().expect("invalid");
2633 assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
2634 idms_delayed.check_is_empty_or_panic();
2635 }
2636
2637 #[idm_test]
2638 async fn test_idm_simple_password_spn_auth(
2639 idms: &IdmServer,
2640 idms_delayed: &mut IdmServerDelayed,
2641 ) {
2642 init_testperson_w_password(idms, TEST_PASSWORD)
2643 .await
2644 .expect("Failed to setup admin account");
2645
2646 let sid = init_authsession_sid(
2647 idms,
2648 Duration::from_secs(TEST_CURRENT_TIME),
2649 "testperson1@example.com",
2650 )
2651 .await;
2652
2653 let mut idms_auth = idms.auth().await.unwrap();
2654 let anon_step = AuthEvent::cred_step_password(sid, TEST_PASSWORD);
2655
2656 let r2 = idms_auth
2658 .auth(
2659 &anon_step,
2660 Duration::from_secs(TEST_CURRENT_TIME),
2661 Source::Internal.into(),
2662 )
2663 .await;
2664 debug!("r2 ==> {:?}", r2);
2665
2666 match r2 {
2667 Ok(ar) => {
2668 let AuthResult {
2669 sessionid: _,
2670 state,
2671 } = ar;
2672 match state {
2673 AuthState::Success(_uat, AuthIssueSession::Token) => {
2674 }
2676 _ => {
2677 error!("A critical error has occurred! We have a non-success result!");
2678 panic!();
2679 }
2680 }
2681 }
2682 Err(e) => {
2683 error!("A critical error has occurred! {:?}", e);
2684 panic!();
2686 }
2687 };
2688
2689 let da = idms_delayed.try_recv().expect("invalid");
2691 assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
2692 idms_delayed.check_is_empty_or_panic();
2693
2694 idms_auth.commit().expect("Must not fail");
2695 }
2696
2697 #[idm_test(audit = 1)]
2698 async fn test_idm_simple_password_invalid(
2699 idms: &IdmServer,
2700 _idms_delayed: &IdmServerDelayed,
2701 idms_audit: &mut IdmServerAudit,
2702 ) {
2703 init_testperson_w_password(idms, TEST_PASSWORD)
2704 .await
2705 .expect("Failed to setup admin account");
2706 let sid =
2707 init_authsession_sid(idms, Duration::from_secs(TEST_CURRENT_TIME), "testperson1").await;
2708 let mut idms_auth = idms.auth().await.unwrap();
2709 let anon_step = AuthEvent::cred_step_password(sid, TEST_PASSWORD_INC);
2710
2711 let r2 = idms_auth
2713 .auth(
2714 &anon_step,
2715 Duration::from_secs(TEST_CURRENT_TIME),
2716 Source::Internal.into(),
2717 )
2718 .await;
2719 debug!("r2 ==> {:?}", r2);
2720
2721 match r2 {
2722 Ok(ar) => {
2723 let AuthResult {
2724 sessionid: _,
2725 state,
2726 } = ar;
2727 match state {
2728 AuthState::Denied(_reason) => {
2729 }
2731 _ => {
2732 error!("A critical error has occurred! We have a non-denied result!");
2733 panic!();
2734 }
2735 }
2736 }
2737 Err(e) => {
2738 error!("A critical error has occurred! {:?}", e);
2739 panic!();
2741 }
2742 };
2743
2744 match idms_audit.audit_rx().try_recv() {
2746 Ok(AuditEvent::AuthenticationDenied { .. }) => {}
2747 _ => panic!("Oh no"),
2748 }
2749
2750 idms_auth.commit().expect("Must not fail");
2751 }
2752
2753 #[idm_test]
2754 async fn test_idm_simple_password_reset(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2755 let pce = PasswordChangeEvent::new_internal(UUID_ADMIN, TEST_PASSWORD);
2756
2757 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2758 assert!(idms_prox_write.set_account_password(&pce).is_ok());
2759 assert!(idms_prox_write.set_account_password(&pce).is_ok());
2760 assert!(idms_prox_write.commit().is_ok());
2761 }
2762
2763 #[idm_test]
2764 async fn test_idm_anonymous_set_password_denied(
2765 idms: &IdmServer,
2766 _idms_delayed: &IdmServerDelayed,
2767 ) {
2768 let pce = PasswordChangeEvent::new_internal(UUID_ANONYMOUS, TEST_PASSWORD);
2769
2770 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2771 assert!(idms_prox_write.set_account_password(&pce).is_err());
2772 assert!(idms_prox_write.commit().is_ok());
2773 }
2774
2775 #[idm_test]
2776 async fn test_idm_regenerate_radius_secret(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2777 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2778
2779 idms_prox_write
2780 .qs_write
2781 .internal_create(vec![E_TESTPERSON_1.clone()])
2782 .expect("unable to create test person");
2783
2784 let rrse = RegenerateRadiusSecretEvent::new_internal(UUID_TESTPERSON_1);
2785
2786 let r1 = idms_prox_write
2788 .regenerate_radius_secret(&rrse)
2789 .expect("Failed to reset radius credential 1");
2790 let r2 = idms_prox_write
2792 .regenerate_radius_secret(&rrse)
2793 .expect("Failed to reset radius credential 2");
2794 assert!(r1 != r2);
2795 }
2796
2797 #[idm_test]
2798 async fn test_idm_radiusauthtoken(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2799 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2800
2801 idms_prox_write
2802 .qs_write
2803 .internal_create(vec![E_TESTPERSON_1.clone()])
2804 .expect("unable to create test person");
2805
2806 let rrse = RegenerateRadiusSecretEvent::new_internal(UUID_TESTPERSON_1);
2807 let r1 = idms_prox_write
2808 .regenerate_radius_secret(&rrse)
2809 .expect("Failed to reset radius credential 1");
2810 idms_prox_write.commit().expect("failed to commit");
2811
2812 let mut idms_prox_read = idms.proxy_read().await.unwrap();
2813 let person_entry = idms_prox_read
2814 .qs_read
2815 .internal_search_uuid(UUID_TESTPERSON_1)
2816 .expect("Can't access admin entry.");
2817
2818 let rate = RadiusAuthTokenEvent::new_impersonate(person_entry, UUID_TESTPERSON_1);
2819 let tok_r = idms_prox_read
2820 .get_radiusauthtoken(&rate, duration_from_epoch_now())
2821 .expect("Failed to generate radius auth token");
2822
2823 assert_eq!(r1, tok_r.secret);
2825 }
2826
2827 #[idm_test]
2828 async fn test_idm_unixusertoken(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2829 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2830 let me_posix = ModifyEvent::new_internal_invalid(
2832 filter!(f_eq(Attribute::Name, PartialValue::new_iname("admin"))),
2833 ModifyList::new_list(vec![
2834 Modify::Present(Attribute::Class, EntryClass::PosixAccount.into()),
2835 Modify::Present(Attribute::GidNumber, Value::new_uint32(2001)),
2836 ]),
2837 );
2838 assert!(idms_prox_write.qs_write.modify(&me_posix).is_ok());
2839 let e: Entry<EntryInit, EntryNew> = entry_init!(
2841 (Attribute::Class, EntryClass::Object.to_value()),
2842 (Attribute::Class, EntryClass::Group.to_value()),
2843 (Attribute::Class, EntryClass::PosixGroup.to_value()),
2844 (Attribute::Name, Value::new_iname("testgroup")),
2845 (
2846 Attribute::Uuid,
2847 Value::Uuid(uuid::uuid!("01609135-a1c4-43d5-966b-a28227644445"))
2848 ),
2849 (Attribute::Description, Value::new_utf8s("testgroup")),
2850 (
2851 Attribute::Member,
2852 Value::Refer(uuid::uuid!("00000000-0000-0000-0000-000000000000"))
2853 )
2854 );
2855
2856 let ce = CreateEvent::new_internal(vec![e]);
2857
2858 assert!(idms_prox_write.qs_write.create(&ce).is_ok());
2859
2860 idms_prox_write.commit().expect("failed to commit");
2861
2862 let mut idms_prox_read = idms.proxy_read().await.unwrap();
2863
2864 let idm_admin_entry = idms_prox_read
2866 .qs_read
2867 .internal_search_uuid(UUID_IDM_ADMIN)
2868 .expect("Can't access admin entry.");
2869
2870 let ugte = UnixGroupTokenEvent::new_impersonate(
2871 idm_admin_entry.clone(),
2872 uuid!("01609135-a1c4-43d5-966b-a28227644445"),
2873 );
2874 let tok_g = idms_prox_read
2875 .get_unixgrouptoken(&ugte)
2876 .expect("Failed to generate unix group token");
2877
2878 assert_eq!(tok_g.name, "testgroup");
2879 assert_eq!(tok_g.spn, "testgroup@example.com");
2880
2881 let uute = UnixUserTokenEvent::new_internal(UUID_ADMIN);
2882 let tok_r = idms_prox_read
2883 .get_unixusertoken(&uute, duration_from_epoch_now())
2884 .expect("Failed to generate unix user token");
2885
2886 assert_eq!(tok_r.name, "admin");
2887 assert_eq!(tok_r.spn, "admin@example.com");
2888 assert_eq!(tok_r.groups.len(), 2);
2889 assert_eq!(tok_r.groups[0].name, "admin");
2890 assert_eq!(tok_r.groups[1].name, "testgroup");
2891 assert!(tok_r.valid);
2892
2893 let ugte = UnixGroupTokenEvent::new_impersonate(
2895 idm_admin_entry,
2896 uuid!("00000000-0000-0000-0000-000000000000"),
2897 );
2898 let tok_g = idms_prox_read
2899 .get_unixgrouptoken(&ugte)
2900 .expect("Failed to generate unix group token");
2901
2902 assert_eq!(tok_g.name, "admin");
2903 assert_eq!(tok_g.spn, "admin@example.com");
2904 }
2905
2906 #[idm_test]
2907 async fn test_idm_simple_unix_password_reset(
2908 idms: &IdmServer,
2909 _idms_delayed: &IdmServerDelayed,
2910 ) {
2911 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2912 let me_posix = ModifyEvent::new_internal_invalid(
2914 filter!(f_eq(Attribute::Name, PartialValue::new_iname("admin"))),
2915 ModifyList::new_list(vec![
2916 Modify::Present(Attribute::Class, EntryClass::PosixAccount.into()),
2917 Modify::Present(Attribute::GidNumber, Value::new_uint32(2001)),
2918 ]),
2919 );
2920 assert!(idms_prox_write.qs_write.modify(&me_posix).is_ok());
2921
2922 let pce = UnixPasswordChangeEvent::new_internal(UUID_ADMIN, TEST_PASSWORD);
2923
2924 assert!(idms_prox_write.set_unix_account_password(&pce).is_ok());
2925 assert!(idms_prox_write.commit().is_ok());
2926
2927 let mut idms_auth = idms.auth().await.unwrap();
2928 let uuae_good = UnixUserAuthEvent::new_internal(UUID_ADMIN, TEST_PASSWORD);
2931 let a1 = idms_auth
2932 .auth_unix(&uuae_good, Duration::from_secs(TEST_CURRENT_TIME))
2933 .await;
2934 match a1 {
2935 Ok(Some(_tok)) => {}
2936 _ => panic!("Oh no"),
2937 };
2938 let uuae_bad = UnixUserAuthEvent::new_internal(UUID_ADMIN, TEST_PASSWORD_INC);
2940 let a2 = idms_auth
2941 .auth_unix(&uuae_bad, Duration::from_secs(TEST_CURRENT_TIME))
2942 .await;
2943 match a2 {
2944 Ok(None) => {}
2945 _ => panic!("Oh no"),
2946 };
2947 assert!(idms_auth.commit().is_ok());
2948
2949 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2951 let me_purge_up = ModifyEvent::new_internal_invalid(
2952 filter!(f_eq(Attribute::Name, PartialValue::new_iname("admin"))),
2953 ModifyList::new_list(vec![Modify::Purged(Attribute::UnixPassword)]),
2954 );
2955 assert!(idms_prox_write.qs_write.modify(&me_purge_up).is_ok());
2956 assert!(idms_prox_write.commit().is_ok());
2957
2958 let mut idms_auth = idms.auth().await.unwrap();
2961 let a3 = idms_auth
2962 .auth_unix(&uuae_good, Duration::from_secs(TEST_CURRENT_TIME))
2963 .await;
2964 match a3 {
2965 Ok(None) => {}
2966 _ => panic!("Oh no"),
2967 };
2968 assert!(idms_auth.commit().is_ok());
2969 }
2970
2971 #[idm_test]
2972 async fn test_idm_simple_password_upgrade(
2973 idms: &IdmServer,
2974 idms_delayed: &mut IdmServerDelayed,
2975 ) {
2976 let ct = duration_from_epoch_now();
2977 idms_delayed.check_is_empty_or_panic();
2979 {
2981 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
2982 idms_prox_write
2985 .qs_write
2986 .internal_create(vec![E_TESTPERSON_1.clone()])
2987 .expect("Failed to create test person");
2988
2989 let me_inv_m =
2990 ModifyEvent::new_internal_invalid(
2991 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_TESTPERSON_1))),
2992 ModifyList::new_list(vec![Modify::Present(
2993 Attribute::PasswordImport,
2994 Value::from("{SSHA512}JwrSUHkI7FTAfHRVR6KoFlSN0E3dmaQWARjZ+/UsShYlENOqDtFVU77HJLLrY2MuSp0jve52+pwtdVl2QUAHukQ0XUf5LDtM")
2995 )]),
2996 );
2997 assert!(idms_prox_write.qs_write.modify(&me_inv_m).is_ok());
2999 assert!(idms_prox_write.commit().is_ok());
3000 }
3001 idms_delayed.check_is_empty_or_panic();
3003
3004 let mut idms_prox_read = idms.proxy_read().await.unwrap();
3005 let person_entry = idms_prox_read
3006 .qs_read
3007 .internal_search_uuid(UUID_TESTPERSON_1)
3008 .expect("Can't access admin entry.");
3009 let cred_before = person_entry
3010 .get_ava_single_credential(Attribute::PrimaryCredential)
3011 .expect("No credential present")
3012 .clone();
3013 drop(idms_prox_read);
3014
3015 check_testperson_password(idms, "password", ct).await;
3017
3018 let da = idms_delayed.try_recv().expect("invalid");
3027 assert!(matches!(da, DelayedAction::PwUpgrade(_)));
3029 let r = idms.delayed_action(duration_from_epoch_now(), da).await;
3030 let da = idms_delayed.try_recv().expect("invalid");
3032 assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
3033 assert_eq!(Ok(true), r);
3034
3035 let mut idms_prox_read = idms.proxy_read().await.unwrap();
3036 let person_entry = idms_prox_read
3037 .qs_read
3038 .internal_search_uuid(UUID_TESTPERSON_1)
3039 .expect("Can't access admin entry.");
3040 let cred_after = person_entry
3041 .get_ava_single_credential(Attribute::PrimaryCredential)
3042 .expect("No credential present")
3043 .clone();
3044 drop(idms_prox_read);
3045
3046 assert_eq!(cred_before.uuid, cred_after.uuid);
3047
3048 check_testperson_password(idms, "password", ct).await;
3050 let da = idms_delayed.try_recv().expect("invalid");
3052 assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
3053
3054 idms_delayed.check_is_empty_or_panic();
3056 }
3057
3058 #[idm_test]
3059 async fn test_idm_unix_password_upgrade(idms: &IdmServer, idms_delayed: &mut IdmServerDelayed) {
3060 idms_delayed.check_is_empty_or_panic();
3062 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
3064
3065 let im_pw = "{SSHA512}JwrSUHkI7FTAfHRVR6KoFlSN0E3dmaQWARjZ+/UsShYlENOqDtFVU77HJLLrY2MuSp0jve52+pwtdVl2QUAHukQ0XUf5LDtM";
3066 let pw = Password::try_from(im_pw).expect("failed to parse");
3067 let cred = Credential::new_from_password(pw);
3068 let v_cred = Value::new_credential("unix", cred);
3069
3070 let me_posix = ModifyEvent::new_internal_invalid(
3071 filter!(f_eq(Attribute::Name, PartialValue::new_iname("admin"))),
3072 ModifyList::new_list(vec![
3073 Modify::Present(Attribute::Class, EntryClass::PosixAccount.into()),
3074 Modify::Present(Attribute::GidNumber, Value::new_uint32(2001)),
3075 Modify::Present(Attribute::UnixPassword, v_cred),
3076 ]),
3077 );
3078 assert!(idms_prox_write.qs_write.modify(&me_posix).is_ok());
3079 assert!(idms_prox_write.commit().is_ok());
3080 idms_delayed.check_is_empty_or_panic();
3081 let uuae = UnixUserAuthEvent::new_internal(UUID_ADMIN, "password");
3083 let mut idms_auth = idms.auth().await.unwrap();
3084 let a1 = idms_auth
3085 .auth_unix(&uuae, Duration::from_secs(TEST_CURRENT_TIME))
3086 .await;
3087 match a1 {
3088 Ok(Some(_tok)) => {}
3089 _ => panic!("Oh no"),
3090 };
3091 idms_auth.commit().expect("Must not fail");
3092 let da = idms_delayed.try_recv().expect("invalid");
3095 let _r = idms.delayed_action(duration_from_epoch_now(), da).await;
3096 let mut idms_auth = idms.auth().await.unwrap();
3098 let a2 = idms_auth
3099 .auth_unix(&uuae, Duration::from_secs(TEST_CURRENT_TIME))
3100 .await;
3101 match a2 {
3102 Ok(Some(_tok)) => {}
3103 _ => panic!("Oh no"),
3104 };
3105 idms_auth.commit().expect("Must not fail");
3106 idms_delayed.check_is_empty_or_panic();
3108 }
3109
3110 const TEST_NOT_YET_VALID_TIME: u64 = TEST_CURRENT_TIME - 240;
3114 const TEST_VALID_FROM_TIME: u64 = TEST_CURRENT_TIME - 120;
3115 const TEST_EXPIRE_TIME: u64 = TEST_CURRENT_TIME + 120;
3116 const TEST_AFTER_EXPIRY: u64 = TEST_CURRENT_TIME + 240;
3117
3118 async fn set_testperson_valid_time(idms: &IdmServer) {
3119 let mut idms_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
3120
3121 let v_valid_from = Value::new_datetime_epoch(Duration::from_secs(TEST_VALID_FROM_TIME));
3122 let v_expire = Value::new_datetime_epoch(Duration::from_secs(TEST_EXPIRE_TIME));
3123
3124 let me_inv_m = ModifyEvent::new_internal_invalid(
3126 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_TESTPERSON_1))),
3127 ModifyList::new_list(vec![
3128 Modify::Present(Attribute::AccountExpire, v_expire),
3129 Modify::Present(Attribute::AccountValidFrom, v_valid_from),
3130 ]),
3131 );
3132 assert!(idms_write.qs_write.modify(&me_inv_m).is_ok());
3134
3135 idms_write.commit().expect("Must not fail");
3136 }
3137
3138 #[idm_test]
3139 async fn test_idm_account_valid_from_expire(
3140 idms: &IdmServer,
3141 _idms_delayed: &mut IdmServerDelayed,
3142 ) {
3143 init_testperson_w_password(idms, TEST_PASSWORD)
3146 .await
3147 .expect("Failed to setup admin account");
3148 set_testperson_valid_time(idms).await;
3151
3152 let time_low = Duration::from_secs(TEST_NOT_YET_VALID_TIME);
3153 let time_high = Duration::from_secs(TEST_AFTER_EXPIRY);
3154
3155 let mut idms_auth = idms.auth().await.unwrap();
3156 let admin_init = AuthEvent::named_init("admin");
3157 let r1 = idms_auth
3158 .auth(&admin_init, time_low, Source::Internal.into())
3159 .await;
3160
3161 let ar = r1.unwrap();
3162 let AuthResult {
3163 sessionid: _,
3164 state,
3165 } = ar;
3166
3167 match state {
3168 AuthState::Denied(_) => {}
3169 _ => {
3170 panic!();
3171 }
3172 };
3173
3174 idms_auth.commit().expect("Must not fail");
3175
3176 let mut idms_auth = idms.auth().await.unwrap();
3178 let admin_init = AuthEvent::named_init("admin");
3179 let r1 = idms_auth
3180 .auth(&admin_init, time_high, Source::Internal.into())
3181 .await;
3182
3183 let ar = r1.unwrap();
3184 let AuthResult {
3185 sessionid: _,
3186 state,
3187 } = ar;
3188
3189 match state {
3190 AuthState::Denied(_) => {}
3191 _ => {
3192 panic!();
3193 }
3194 };
3195
3196 idms_auth.commit().expect("Must not fail");
3197 }
3198
3199 #[idm_test]
3200 async fn test_idm_unix_valid_from_expire(
3201 idms: &IdmServer,
3202 _idms_delayed: &mut IdmServerDelayed,
3203 ) {
3204 init_testperson_w_password(idms, TEST_PASSWORD)
3206 .await
3207 .expect("Failed to setup admin account");
3208 set_testperson_valid_time(idms).await;
3209
3210 let time_low = Duration::from_secs(TEST_NOT_YET_VALID_TIME);
3211 let time_high = Duration::from_secs(TEST_AFTER_EXPIRY);
3212
3213 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
3215 let me_posix = ModifyEvent::new_internal_invalid(
3216 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_TESTPERSON_1))),
3217 ModifyList::new_list(vec![
3218 Modify::Present(Attribute::Class, EntryClass::PosixAccount.into()),
3219 Modify::Present(Attribute::GidNumber, Value::new_uint32(2001)),
3220 ]),
3221 );
3222 assert!(idms_prox_write.qs_write.modify(&me_posix).is_ok());
3223
3224 let pce = UnixPasswordChangeEvent::new_internal(UUID_TESTPERSON_1, TEST_PASSWORD);
3225
3226 assert!(idms_prox_write.set_unix_account_password(&pce).is_ok());
3227 assert!(idms_prox_write.commit().is_ok());
3228
3229 let mut idms_auth = idms.auth().await.unwrap();
3231 let uuae_good = UnixUserAuthEvent::new_internal(UUID_TESTPERSON_1, TEST_PASSWORD);
3232
3233 let a1 = idms_auth.auth_unix(&uuae_good, time_low).await;
3234 match a1 {
3237 Ok(None) => {}
3238 _ => panic!("Oh no"),
3239 };
3240
3241 let a2 = idms_auth.auth_unix(&uuae_good, time_high).await;
3242 match a2 {
3243 Ok(None) => {}
3244 _ => panic!("Oh no"),
3245 };
3246
3247 idms_auth.commit().expect("Must not fail");
3248 let mut idms_prox_read = idms.proxy_read().await.unwrap();
3250 let uute = UnixUserTokenEvent::new_internal(UUID_TESTPERSON_1);
3251
3252 let tok_r = idms_prox_read
3253 .get_unixusertoken(&uute, time_low)
3254 .expect("Failed to generate unix user token");
3255
3256 assert_eq!(tok_r.name, "testperson1");
3257 assert!(!tok_r.valid);
3258
3259 let tok_r = idms_prox_read
3260 .get_unixusertoken(&uute, time_high)
3261 .expect("Failed to generate unix user token");
3262
3263 assert_eq!(tok_r.name, "testperson1");
3264 assert!(!tok_r.valid);
3265 }
3266
3267 #[idm_test]
3268 async fn test_idm_radius_valid_from_expire(
3269 idms: &IdmServer,
3270 _idms_delayed: &mut IdmServerDelayed,
3271 ) {
3272 init_testperson_w_password(idms, TEST_PASSWORD)
3275 .await
3276 .expect("Failed to setup admin account");
3277 set_testperson_valid_time(idms).await;
3278
3279 let time_low = Duration::from_secs(TEST_NOT_YET_VALID_TIME);
3280 let time_high = Duration::from_secs(TEST_AFTER_EXPIRY);
3281
3282 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
3283 let rrse = RegenerateRadiusSecretEvent::new_internal(UUID_TESTPERSON_1);
3284 let _r1 = idms_prox_write
3285 .regenerate_radius_secret(&rrse)
3286 .expect("Failed to reset radius credential 1");
3287 idms_prox_write.commit().expect("failed to commit");
3288
3289 let mut idms_prox_read = idms.proxy_read().await.unwrap();
3290 let admin_entry = idms_prox_read
3291 .qs_read
3292 .internal_search_uuid(UUID_ADMIN)
3293 .expect("Can't access admin entry.");
3294
3295 let rate = RadiusAuthTokenEvent::new_impersonate(admin_entry, UUID_ADMIN);
3296 let tok_r = idms_prox_read.get_radiusauthtoken(&rate, time_low);
3297
3298 if tok_r.is_err() {
3299 } else {
3301 debug_assert!(false);
3302 }
3303
3304 let tok_r = idms_prox_read.get_radiusauthtoken(&rate, time_high);
3305
3306 if tok_r.is_err() {
3307 } else {
3309 debug_assert!(false);
3310 }
3311 }
3312
3313 #[idm_test(audit = 1)]
3314 async fn test_idm_account_softlocking(
3315 idms: &IdmServer,
3316 idms_delayed: &mut IdmServerDelayed,
3317 idms_audit: &mut IdmServerAudit,
3318 ) {
3319 init_testperson_w_password(idms, TEST_PASSWORD)
3320 .await
3321 .expect("Failed to setup admin account");
3322
3323 let sid =
3325 init_authsession_sid(idms, Duration::from_secs(TEST_CURRENT_TIME), "testperson1").await;
3326 let mut idms_auth = idms.auth().await.unwrap();
3327 let anon_step = AuthEvent::cred_step_password(sid, TEST_PASSWORD_INC);
3328
3329 let r2 = idms_auth
3330 .auth(
3331 &anon_step,
3332 Duration::from_secs(TEST_CURRENT_TIME),
3333 Source::Internal.into(),
3334 )
3335 .await;
3336 debug!("r2 ==> {:?}", r2);
3337
3338 match r2 {
3339 Ok(ar) => {
3340 let AuthResult {
3341 sessionid: _,
3342 state,
3343 } = ar;
3344 match state {
3345 AuthState::Denied(reason) => {
3346 assert!(reason != "Account is temporarily locked");
3347 }
3348 _ => {
3349 error!("A critical error has occurred! We have a non-denied result!");
3350 panic!();
3351 }
3352 }
3353 }
3354 Err(e) => {
3355 error!("A critical error has occurred! {:?}", e);
3356 panic!();
3357 }
3358 };
3359
3360 match idms_audit.audit_rx().try_recv() {
3362 Ok(AuditEvent::AuthenticationDenied { .. }) => {}
3363 _ => panic!("Oh no"),
3364 }
3365
3366 idms_auth.commit().expect("Must not fail");
3367
3368 let mut idms_auth = idms.auth().await.unwrap();
3372 let admin_init = AuthEvent::named_init("testperson1");
3373
3374 let r1 = idms_auth
3375 .auth(
3376 &admin_init,
3377 Duration::from_secs(TEST_CURRENT_TIME),
3378 Source::Internal.into(),
3379 )
3380 .await;
3381 let ar = r1.unwrap();
3382 let AuthResult { sessionid, state } = ar;
3383 assert!(matches!(state, AuthState::Choose(_)));
3384
3385 let admin_begin = AuthEvent::begin_mech(sessionid, AuthMech::Password);
3387
3388 let r2 = idms_auth
3389 .auth(
3390 &admin_begin,
3391 Duration::from_secs(TEST_CURRENT_TIME),
3392 Source::Internal.into(),
3393 )
3394 .await;
3395 let ar = r2.unwrap();
3396 let AuthResult {
3397 sessionid: _,
3398 state,
3399 } = ar;
3400
3401 match state {
3402 AuthState::Denied(reason) => {
3403 assert_eq!(reason, "Account is temporarily locked");
3404 }
3405 _ => {
3406 error!("Sessions was not denied (softlock)");
3407 panic!();
3408 }
3409 };
3410
3411 idms_auth.commit().expect("Must not fail");
3412
3413 let sid = init_authsession_sid(
3418 idms,
3419 Duration::from_secs(TEST_CURRENT_TIME + 2),
3420 "testperson1",
3421 )
3422 .await;
3423
3424 let mut idms_auth = idms.auth().await.unwrap();
3425 let anon_step = AuthEvent::cred_step_password(sid, TEST_PASSWORD);
3426
3427 let r2 = idms_auth
3429 .auth(
3430 &anon_step,
3431 Duration::from_secs(TEST_CURRENT_TIME + 2),
3432 Source::Internal.into(),
3433 )
3434 .await;
3435 debug!("r2 ==> {:?}", r2);
3436
3437 match r2 {
3438 Ok(ar) => {
3439 let AuthResult {
3440 sessionid: _,
3441 state,
3442 } = ar;
3443 match state {
3444 AuthState::Success(_uat, AuthIssueSession::Token) => {
3445 }
3447 _ => {
3448 error!("A critical error has occurred! We have a non-success result!");
3449 panic!();
3450 }
3451 }
3452 }
3453 Err(e) => {
3454 error!("A critical error has occurred! {:?}", e);
3455 panic!();
3457 }
3458 };
3459
3460 idms_auth.commit().expect("Must not fail");
3461
3462 let da = idms_delayed.try_recv().expect("invalid");
3464 assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
3465 idms_delayed.check_is_empty_or_panic();
3466
3467 }
3474
3475 #[idm_test(audit = 1)]
3476 async fn test_idm_account_softlocking_interleaved(
3477 idms: &IdmServer,
3478 _idms_delayed: &mut IdmServerDelayed,
3479 idms_audit: &mut IdmServerAudit,
3480 ) {
3481 init_testperson_w_password(idms, TEST_PASSWORD)
3482 .await
3483 .expect("Failed to setup admin account");
3484
3485 let sid_early =
3487 init_authsession_sid(idms, Duration::from_secs(TEST_CURRENT_TIME), "testperson1").await;
3488
3489 let sid_later =
3491 init_authsession_sid(idms, Duration::from_secs(TEST_CURRENT_TIME), "testperson1").await;
3492 let mut idms_auth = idms.auth().await.unwrap();
3494 let anon_step = AuthEvent::cred_step_password(sid_later, TEST_PASSWORD_INC);
3495
3496 let r2 = idms_auth
3497 .auth(
3498 &anon_step,
3499 Duration::from_secs(TEST_CURRENT_TIME),
3500 Source::Internal.into(),
3501 )
3502 .await;
3503 debug!("r2 ==> {:?}", r2);
3504
3505 match r2 {
3506 Ok(ar) => {
3507 let AuthResult {
3508 sessionid: _,
3509 state,
3510 } = ar;
3511 match state {
3512 AuthState::Denied(reason) => {
3513 assert!(reason != "Account is temporarily locked");
3514 }
3515 _ => {
3516 error!("A critical error has occurred! We have a non-denied result!");
3517 panic!();
3518 }
3519 }
3520 }
3521 Err(e) => {
3522 error!("A critical error has occurred! {:?}", e);
3523 panic!();
3524 }
3525 };
3526
3527 match idms_audit.audit_rx().try_recv() {
3528 Ok(AuditEvent::AuthenticationDenied { .. }) => {}
3529 _ => panic!("Oh no"),
3530 }
3531
3532 idms_auth.commit().expect("Must not fail");
3533
3534 let mut idms_auth = idms.auth().await.unwrap();
3536 let anon_step = AuthEvent::cred_step_password(sid_early, TEST_PASSWORD);
3537
3538 let r2 = idms_auth
3540 .auth(
3541 &anon_step,
3542 Duration::from_secs(TEST_CURRENT_TIME),
3543 Source::Internal.into(),
3544 )
3545 .await;
3546 debug!("r2 ==> {:?}", r2);
3547 match r2 {
3548 Ok(ar) => {
3549 let AuthResult {
3550 sessionid: _,
3551 state,
3552 } = ar;
3553 match state {
3554 AuthState::Denied(reason) => {
3555 assert_eq!(reason, "Account is temporarily locked");
3556 }
3557 _ => {
3558 error!("A critical error has occurred! We have a non-denied result!");
3559 panic!();
3560 }
3561 }
3562 }
3563 Err(e) => {
3564 error!("A critical error has occurred! {:?}", e);
3565 panic!();
3566 }
3567 };
3568 idms_auth.commit().expect("Must not fail");
3569 }
3570
3571 #[idm_test]
3572 async fn test_idm_account_unix_softlocking(
3573 idms: &IdmServer,
3574 _idms_delayed: &mut IdmServerDelayed,
3575 ) {
3576 init_testperson_w_password(idms, TEST_PASSWORD)
3577 .await
3578 .expect("Failed to setup admin account");
3579 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
3581 let me_posix = ModifyEvent::new_internal_invalid(
3582 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_TESTPERSON_1))),
3583 ModifyList::new_list(vec![
3584 Modify::Present(Attribute::Class, EntryClass::PosixAccount.into()),
3585 Modify::Present(Attribute::GidNumber, Value::new_uint32(2001)),
3586 ]),
3587 );
3588 assert!(idms_prox_write.qs_write.modify(&me_posix).is_ok());
3589
3590 let pce = UnixPasswordChangeEvent::new_internal(UUID_TESTPERSON_1, TEST_PASSWORD);
3591 assert!(idms_prox_write.set_unix_account_password(&pce).is_ok());
3592 assert!(idms_prox_write.commit().is_ok());
3593
3594 let mut idms_auth = idms.auth().await.unwrap();
3595 let uuae_good = UnixUserAuthEvent::new_internal(UUID_TESTPERSON_1, TEST_PASSWORD);
3596 let uuae_bad = UnixUserAuthEvent::new_internal(UUID_TESTPERSON_1, TEST_PASSWORD_INC);
3597
3598 let a2 = idms_auth
3599 .auth_unix(&uuae_bad, Duration::from_secs(TEST_CURRENT_TIME))
3600 .await;
3601 match a2 {
3602 Ok(None) => {}
3603 _ => panic!("Oh no"),
3604 };
3605
3606 let a1 = idms_auth
3608 .auth_unix(&uuae_good, Duration::from_secs(TEST_CURRENT_TIME))
3609 .await;
3610 match a1 {
3611 Ok(None) => {}
3612 _ => panic!("Oh no"),
3613 };
3614
3615 let a1 = idms_auth
3617 .auth_unix(&uuae_good, Duration::from_secs(TEST_CURRENT_TIME + 2))
3618 .await;
3619 match a1 {
3620 Ok(Some(_tok)) => {}
3621 _ => panic!("Oh no"),
3622 };
3623
3624 assert!(idms_auth.commit().is_ok());
3625 }
3626
3627 #[idm_test]
3628 async fn test_idm_jwt_uat_expiry(idms: &IdmServer, idms_delayed: &mut IdmServerDelayed) {
3629 let ct = Duration::from_secs(TEST_CURRENT_TIME);
3630 let expiry = ct + Duration::from_secs((DEFAULT_AUTH_SESSION_EXPIRY + 1).into());
3631 init_testperson_w_password(idms, TEST_PASSWORD)
3633 .await
3634 .expect("Failed to setup admin account");
3635 let token = check_testperson_password(idms, TEST_PASSWORD, ct).await;
3636
3637 let da = idms_delayed.try_recv().expect("invalid");
3639 assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
3640 let r = idms.delayed_action(ct, da).await;
3642 assert_eq!(Ok(true), r);
3643 idms_delayed.check_is_empty_or_panic();
3644
3645 let mut idms_prox_read = idms.proxy_read().await.unwrap();
3646
3647 idms_prox_read
3649 .validate_client_auth_info_to_ident(token.clone().into(), ct)
3650 .expect("Failed to validate");
3651
3652 match idms_prox_read.validate_client_auth_info_to_ident(token.into(), expiry) {
3654 Err(OperationError::SessionExpired) => {}
3655 _ => panic!("Oh no"),
3656 }
3657 }
3658
3659 #[idm_test]
3660 async fn test_idm_expired_auth_session_cleanup(
3661 idms: &IdmServer,
3662 _idms_delayed: &mut IdmServerDelayed,
3663 ) {
3664 let ct = Duration::from_secs(TEST_CURRENT_TIME);
3665 let expiry_a = ct + Duration::from_secs((DEFAULT_AUTH_SESSION_EXPIRY + 1).into());
3666 let expiry_b = ct + Duration::from_secs(((DEFAULT_AUTH_SESSION_EXPIRY + 1) * 2).into());
3667
3668 let session_a = Uuid::new_v4();
3669 let session_b = Uuid::new_v4();
3670
3671 let cred_id = init_testperson_w_password(idms, TEST_PASSWORD)
3673 .await
3674 .expect("Failed to setup admin account");
3675
3676 let mut idms_prox_read = idms.proxy_read().await.unwrap();
3678 let admin = idms_prox_read
3679 .qs_read
3680 .internal_search_uuid(UUID_TESTPERSON_1)
3681 .expect("failed");
3682 let sessions = admin.get_ava_as_session_map(Attribute::UserAuthTokenSession);
3683 assert!(sessions.is_none());
3684 drop(idms_prox_read);
3685
3686 let da = DelayedAction::AuthSessionRecord(AuthSessionRecord {
3687 target_uuid: UUID_TESTPERSON_1,
3688 session_id: session_a,
3689 cred_id,
3690 label: "Test Session A".to_string(),
3691 expiry: Some(OffsetDateTime::UNIX_EPOCH + expiry_a),
3692 issued_at: OffsetDateTime::UNIX_EPOCH + ct,
3693 issued_by: IdentityId::User(UUID_ADMIN),
3694 scope: SessionScope::ReadOnly,
3695 type_: AuthType::Passkey,
3696 ext_metadata: Default::default(),
3697 });
3698 let r = idms.delayed_action(ct, da).await;
3700 assert_eq!(Ok(true), r);
3701
3702 let mut idms_prox_read = idms.proxy_read().await.unwrap();
3704 let admin = idms_prox_read
3705 .qs_read
3706 .internal_search_uuid(UUID_TESTPERSON_1)
3707 .expect("failed");
3708 let sessions = admin
3709 .get_ava_as_session_map(Attribute::UserAuthTokenSession)
3710 .expect("Sessions must be present!");
3711 assert_eq!(sessions.len(), 1);
3712 let session_data_a = sessions.get(&session_a).expect("Session A is missing!");
3713 assert!(matches!(session_data_a.state, SessionState::ExpiresAt(_)));
3714
3715 drop(idms_prox_read);
3716
3717 let da = DelayedAction::AuthSessionRecord(AuthSessionRecord {
3720 target_uuid: UUID_TESTPERSON_1,
3721 session_id: session_b,
3722 cred_id,
3723 label: "Test Session B".to_string(),
3724 expiry: Some(OffsetDateTime::UNIX_EPOCH + expiry_b),
3725 issued_at: OffsetDateTime::UNIX_EPOCH + ct,
3726 issued_by: IdentityId::User(UUID_ADMIN),
3727 scope: SessionScope::ReadOnly,
3728 type_: AuthType::Passkey,
3729 ext_metadata: Default::default(),
3730 });
3731 let r = idms.delayed_action(expiry_a, da).await;
3733 assert_eq!(Ok(true), r);
3734
3735 let mut idms_prox_read = idms.proxy_read().await.unwrap();
3736 let admin = idms_prox_read
3737 .qs_read
3738 .internal_search_uuid(UUID_TESTPERSON_1)
3739 .expect("failed");
3740 let sessions = admin
3741 .get_ava_as_session_map(Attribute::UserAuthTokenSession)
3742 .expect("Sessions must be present!");
3743 trace!(?sessions);
3744 assert_eq!(sessions.len(), 2);
3745
3746 let session_data_a = sessions.get(&session_a).expect("Session A is missing!");
3747 assert!(matches!(session_data_a.state, SessionState::RevokedAt(_)));
3748
3749 let session_data_b = sessions.get(&session_b).expect("Session B is missing!");
3750 assert!(matches!(session_data_b.state, SessionState::ExpiresAt(_)));
3751 }
3753
3754 #[idm_test]
3755 async fn test_idm_account_session_validation(
3756 idms: &IdmServer,
3757 idms_delayed: &mut IdmServerDelayed,
3758 ) {
3759 use kanidm_proto::internal::UserAuthToken;
3760
3761 let ct = duration_from_epoch_now();
3762
3763 let post_grace = ct + AUTH_TOKEN_GRACE_WINDOW + Duration::from_secs(1);
3764 let expiry = ct + Duration::from_secs(DEFAULT_AUTH_SESSION_EXPIRY as u64 + 1);
3765
3766 assert!(post_grace < expiry);
3769
3770 init_testperson_w_password(idms, TEST_PASSWORD)
3772 .await
3773 .expect("Failed to setup admin account");
3774 let uat_unverified = check_testperson_password(idms, TEST_PASSWORD, ct).await;
3775
3776 let da = idms_delayed.try_recv().expect("invalid");
3778 assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
3779 let r = idms.delayed_action(ct, da).await;
3780 assert_eq!(Ok(true), r);
3781
3782 let mut idms_prox_read = idms.proxy_read().await.unwrap();
3783
3784 let token_kid = uat_unverified.kid().expect("no key id present");
3785
3786 let uat_jwk = idms_prox_read
3787 .qs_read
3788 .get_key_providers()
3789 .get_key_object(UUID_DOMAIN_INFO)
3790 .and_then(|object| {
3791 object
3792 .jws_public_jwk(token_kid)
3793 .expect("Unable to access uat jwk")
3794 })
3795 .expect("No jwk by this kid");
3796
3797 let jws_validator = JwsEs256Verifier::try_from(&uat_jwk).unwrap();
3798
3799 let uat_inner: UserAuthToken = jws_validator
3800 .verify(&uat_unverified)
3801 .unwrap()
3802 .from_json()
3803 .unwrap();
3804
3805 idms_prox_read
3807 .validate_client_auth_info_to_ident(uat_unverified.clone().into(), ct)
3808 .expect("Failed to validate");
3809
3810 idms_prox_read
3812 .validate_client_auth_info_to_ident(uat_unverified.clone().into(), post_grace)
3813 .expect("Failed to validate");
3814
3815 drop(idms_prox_read);
3816
3817 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
3819 let dte = DestroySessionTokenEvent::new_internal(uat_inner.uuid, uat_inner.session_id);
3820 assert!(idms_prox_write.account_destroy_session_token(&dte).is_ok());
3821 assert!(idms_prox_write.commit().is_ok());
3822
3823 let mut idms_prox_read = idms.proxy_read().await.unwrap();
3825
3826 match idms_prox_read
3829 .validate_client_auth_info_to_ident(uat_unverified.clone().into(), post_grace)
3830 {
3831 Err(OperationError::SessionExpired) => {}
3832 _ => panic!("Oh no"),
3833 }
3834 drop(idms_prox_read);
3835
3836 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
3838 let filt = filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(uat_inner.uuid)));
3839 let mut work_set = idms_prox_write
3840 .qs_write
3841 .internal_search_writeable(&filt)
3842 .expect("Failed to perform internal search writeable");
3843 for (_, entry) in work_set.iter_mut() {
3844 let _ = entry.force_trim_ava(Attribute::UserAuthTokenSession);
3845 }
3846 assert!(idms_prox_write
3847 .qs_write
3848 .internal_apply_writable(work_set)
3849 .is_ok());
3850
3851 assert!(idms_prox_write.commit().is_ok());
3852
3853 let mut idms_prox_read = idms.proxy_read().await.unwrap();
3854 idms_prox_read
3855 .validate_client_auth_info_to_ident(uat_unverified.clone().into(), ct)
3856 .expect("Failed to validate");
3857
3858 match idms_prox_read
3860 .validate_client_auth_info_to_ident(uat_unverified.clone().into(), post_grace)
3861 {
3862 Err(OperationError::SessionExpired) => {}
3863 _ => panic!("Oh no"),
3864 }
3865 }
3866
3867 #[idm_test]
3868 async fn test_idm_account_session_expiry(
3869 idms: &IdmServer,
3870 _idms_delayed: &mut IdmServerDelayed,
3871 ) {
3872 let ct = Duration::from_secs(TEST_CURRENT_TIME);
3873
3874 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
3876
3877 let new_authsession_expiry = 1000;
3878
3879 let modlist = ModifyList::new_purge_and_set(
3880 Attribute::AuthSessionExpiry,
3881 Value::Uint32(new_authsession_expiry),
3882 );
3883 idms_prox_write
3884 .qs_write
3885 .internal_modify_uuid(UUID_IDM_ALL_ACCOUNTS, &modlist)
3886 .expect("Unable to change default session exp");
3887
3888 assert!(idms_prox_write.commit().is_ok());
3889
3890 let mut idms_auth = idms.auth().await.unwrap();
3892 let anon_init = AuthEvent::anonymous_init();
3894 let r1 = idms_auth
3896 .auth(&anon_init, ct, Source::Internal.into())
3897 .await;
3898 let sid = match r1 {
3901 Ok(ar) => {
3902 let AuthResult { sessionid, state } = ar;
3903 match state {
3904 AuthState::Choose(mut conts) => {
3905 assert_eq!(conts.len(), 1);
3907 let m = conts.pop().expect("Should not fail");
3909 assert_eq!(m, AuthMech::Anonymous);
3910 }
3911 _ => {
3912 error!("A critical error has occurred! We have a non-continue result!");
3913 panic!();
3914 }
3915 };
3916 sessionid
3918 }
3919 Err(e) => {
3920 error!("A critical error has occurred! {:?}", e);
3922 panic!();
3923 }
3924 };
3925
3926 idms_auth.commit().expect("Must not fail");
3927
3928 let mut idms_auth = idms.auth().await.unwrap();
3929 let anon_begin = AuthEvent::begin_mech(sid, AuthMech::Anonymous);
3930
3931 let r2 = idms_auth
3932 .auth(&anon_begin, ct, Source::Internal.into())
3933 .await;
3934
3935 match r2 {
3936 Ok(ar) => {
3937 let AuthResult {
3938 sessionid: _,
3939 state,
3940 } = ar;
3941
3942 match state {
3943 AuthState::Continue(allowed) => {
3944 assert_eq!(allowed.len(), 1);
3946 assert_eq!(allowed.first(), Some(&AuthAllowed::Anonymous));
3947 }
3948 _ => {
3949 error!("A critical error has occurred! We have a non-continue result!");
3950 panic!();
3951 }
3952 }
3953 }
3954 Err(e) => {
3955 error!("A critical error has occurred! {:?}", e);
3956 panic!();
3958 }
3959 };
3960
3961 idms_auth.commit().expect("Must not fail");
3962
3963 let mut idms_auth = idms.auth().await.unwrap();
3964 let anon_step = AuthEvent::cred_step_anonymous(sid);
3966
3967 let r2 = idms_auth
3969 .auth(&anon_step, ct, Source::Internal.into())
3970 .await;
3971
3972 let token = match r2 {
3973 Ok(ar) => {
3974 let AuthResult {
3975 sessionid: _,
3976 state,
3977 } = ar;
3978
3979 match state {
3980 AuthState::Success(uat, AuthIssueSession::Token) => uat,
3981 _ => {
3982 error!("A critical error has occurred! We have a non-success result!");
3983 panic!();
3984 }
3985 }
3986 }
3987 Err(e) => {
3988 error!("A critical error has occurred! {:?}", e);
3989 panic!("A critical error has occurred! {e:?}");
3991 }
3992 };
3993
3994 idms_auth.commit().expect("Must not fail");
3995
3996 let Token::UserAuthToken(uat) = idms
3999 .proxy_read()
4000 .await
4001 .unwrap()
4002 .validate_and_parse_token_to_identity_token(&token, ct)
4003 .expect("Must not fail")
4004 else {
4005 panic!("Unexpected auth token type for anonymous auth");
4006 };
4007
4008 debug!(?uat);
4009
4010 assert!(
4011 matches!(uat.expiry, Some(exp) if exp == OffsetDateTime::UNIX_EPOCH + ct + Duration::from_secs(new_authsession_expiry as u64))
4012 );
4013 }
4014
4015 #[idm_test]
4016 async fn test_idm_uat_claim_insertion(idms: &IdmServer, _idms_delayed: &mut IdmServerDelayed) {
4017 let ct = Duration::from_secs(TEST_CURRENT_TIME);
4018 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
4019
4020 let account = idms_prox_write
4022 .target_to_account(UUID_ADMIN)
4023 .expect("account must exist");
4024
4025 let session_id = uuid::Uuid::new_v4();
4027
4028 let uat = account
4032 .to_userauthtoken(
4033 session_id,
4034 SessionScope::ReadWrite,
4035 ct,
4036 &ResolvedAccountPolicy::test_policy(),
4037 )
4038 .expect("Unable to create uat");
4039 let ident = idms_prox_write
4040 .process_uat_to_identity(&uat, ct, Source::Internal)
4041 .expect("Unable to process uat");
4042
4043 assert!(!ident.has_claim("authtype_anonymous"));
4044 assert!(!ident.has_claim("authlevel_strong"));
4046 assert!(!ident.has_claim("authclass_single"));
4047 assert!(!ident.has_claim("authclass_mfa"));
4048
4049 let uat = account
4051 .to_userauthtoken(
4052 session_id,
4053 SessionScope::ReadWrite,
4054 ct,
4055 &ResolvedAccountPolicy::test_policy(),
4056 )
4057 .expect("Unable to create uat");
4058 let ident = idms_prox_write
4059 .process_uat_to_identity(&uat, ct, Source::Internal)
4060 .expect("Unable to process uat");
4061
4062 assert!(!ident.has_claim("authtype_unixpassword"));
4063 assert!(!ident.has_claim("authclass_single"));
4064 assert!(!ident.has_claim("authlevel_strong"));
4066 assert!(!ident.has_claim("authclass_mfa"));
4067
4068 let uat = account
4070 .to_userauthtoken(
4071 session_id,
4072 SessionScope::ReadWrite,
4073 ct,
4074 &ResolvedAccountPolicy::test_policy(),
4075 )
4076 .expect("Unable to create uat");
4077 let ident = idms_prox_write
4078 .process_uat_to_identity(&uat, ct, Source::Internal)
4079 .expect("Unable to process uat");
4080
4081 assert!(!ident.has_claim("authtype_password"));
4082 assert!(!ident.has_claim("authclass_single"));
4083 assert!(!ident.has_claim("authlevel_strong"));
4085 assert!(!ident.has_claim("authclass_mfa"));
4086
4087 let uat = account
4089 .to_userauthtoken(
4090 session_id,
4091 SessionScope::ReadWrite,
4092 ct,
4093 &ResolvedAccountPolicy::test_policy(),
4094 )
4095 .expect("Unable to create uat");
4096 let ident = idms_prox_write
4097 .process_uat_to_identity(&uat, ct, Source::Internal)
4098 .expect("Unable to process uat");
4099
4100 assert!(!ident.has_claim("authtype_generatedpassword"));
4101 assert!(!ident.has_claim("authclass_single"));
4102 assert!(!ident.has_claim("authlevel_strong"));
4103 assert!(!ident.has_claim("authclass_mfa"));
4105
4106 let uat = account
4108 .to_userauthtoken(
4109 session_id,
4110 SessionScope::ReadWrite,
4111 ct,
4112 &ResolvedAccountPolicy::test_policy(),
4113 )
4114 .expect("Unable to create uat");
4115 let ident = idms_prox_write
4116 .process_uat_to_identity(&uat, ct, Source::Internal)
4117 .expect("Unable to process uat");
4118
4119 assert!(!ident.has_claim("authtype_webauthn"));
4120 assert!(!ident.has_claim("authclass_single"));
4121 assert!(!ident.has_claim("authlevel_strong"));
4122 assert!(!ident.has_claim("authclass_mfa"));
4124
4125 let uat = account
4127 .to_userauthtoken(
4128 session_id,
4129 SessionScope::ReadWrite,
4130 ct,
4131 &ResolvedAccountPolicy::test_policy(),
4132 )
4133 .expect("Unable to create uat");
4134 let ident = idms_prox_write
4135 .process_uat_to_identity(&uat, ct, Source::Internal)
4136 .expect("Unable to process uat");
4137
4138 assert!(!ident.has_claim("authtype_passwordmfa"));
4139 assert!(!ident.has_claim("authlevel_strong"));
4140 assert!(!ident.has_claim("authclass_mfa"));
4141 assert!(!ident.has_claim("authclass_single"));
4143 }
4144
4145 #[idm_test]
4146 async fn test_idm_uat_limits_account_policy(
4147 idms: &IdmServer,
4148 _idms_delayed: &mut IdmServerDelayed,
4149 ) {
4150 let ct = Duration::from_secs(TEST_CURRENT_TIME);
4151 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
4152
4153 idms_prox_write
4154 .qs_write
4155 .internal_create(vec![E_TESTPERSON_1.clone()])
4156 .expect("Failed to create test person");
4157
4158 let account = idms_prox_write
4160 .target_to_account(UUID_TESTPERSON_1)
4161 .expect("account must exist");
4162
4163 let session_id = uuid::Uuid::new_v4();
4165
4166 let uat = account
4167 .to_userauthtoken(
4168 session_id,
4169 SessionScope::ReadWrite,
4170 ct,
4171 &ResolvedAccountPolicy::test_policy(),
4172 )
4173 .expect("Unable to create uat");
4174
4175 let ident = idms_prox_write
4176 .process_uat_to_identity(&uat, ct, Source::Internal)
4177 .expect("Unable to process uat");
4178
4179 assert_eq!(
4180 ident.limits().search_max_results,
4181 DEFAULT_LIMIT_SEARCH_MAX_RESULTS as usize
4182 );
4183 assert_eq!(
4184 ident.limits().search_max_filter_test,
4185 DEFAULT_LIMIT_SEARCH_MAX_FILTER_TEST as usize
4186 );
4187 }
4188
4189 #[idm_test]
4190 async fn test_idm_jwt_uat_token_key_reload(
4191 idms: &IdmServer,
4192 idms_delayed: &mut IdmServerDelayed,
4193 ) {
4194 let ct = duration_from_epoch_now();
4195
4196 init_testperson_w_password(idms, TEST_PASSWORD)
4197 .await
4198 .expect("Failed to setup admin account");
4199 let token = check_testperson_password(idms, TEST_PASSWORD, ct).await;
4200
4201 let da = idms_delayed.try_recv().expect("invalid");
4203 assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
4204 idms_delayed.check_is_empty_or_panic();
4205
4206 let mut idms_prox_read = idms.proxy_read().await.unwrap();
4207
4208 idms_prox_read
4210 .validate_client_auth_info_to_ident(token.clone().into(), ct)
4211 .expect("Failed to validate");
4212
4213 drop(idms_prox_read);
4214
4215 let revoke_kid = token.kid().expect("token does not contain a key id");
4217
4218 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
4220 let me_reset_tokens = ModifyEvent::new_internal_invalid(
4221 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_DOMAIN_INFO))),
4222 ModifyList::new_append(
4223 Attribute::KeyActionRevoke,
4224 Value::HexString(revoke_kid.to_string()),
4225 ),
4226 );
4227 assert!(idms_prox_write.qs_write.modify(&me_reset_tokens).is_ok());
4228 assert!(idms_prox_write.commit().is_ok());
4229
4230 let new_token = check_testperson_password(idms, TEST_PASSWORD, ct).await;
4231
4232 let da = idms_delayed.try_recv().expect("invalid");
4234 assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
4235 idms_delayed.check_is_empty_or_panic();
4236
4237 let mut idms_prox_read = idms.proxy_read().await.unwrap();
4238
4239 assert!(idms_prox_read
4241 .validate_client_auth_info_to_ident(token.into(), ct)
4242 .is_err());
4243
4244 idms_prox_read
4246 .validate_client_auth_info_to_ident(new_token.into(), ct)
4247 .expect("Failed to validate");
4248 }
4249
4250 #[idm_test]
4251 async fn test_idm_service_account_to_person(
4252 idms: &IdmServer,
4253 _idms_delayed: &mut IdmServerDelayed,
4254 ) {
4255 let ct = Duration::from_secs(TEST_CURRENT_TIME);
4256 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
4257
4258 let ident = Identity::from_internal();
4259 let target_uuid = Uuid::new_v4();
4260
4261 let e = entry_init!(
4263 (Attribute::Class, EntryClass::Object.to_value()),
4264 (Attribute::Class, EntryClass::Account.to_value()),
4265 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
4266 (Attribute::Name, Value::new_iname("testaccount")),
4267 (Attribute::Uuid, Value::Uuid(target_uuid)),
4268 (Attribute::Description, Value::new_utf8s("testaccount")),
4269 (Attribute::DisplayName, Value::new_utf8s("Test Account"))
4270 );
4271
4272 let ce = CreateEvent::new_internal(vec![e]);
4273 let cr = idms_prox_write.qs_write.create(&ce);
4274 assert!(cr.is_ok());
4275
4276 assert!(idms_prox_write
4278 .service_account_into_person(&ident, target_uuid)
4279 .is_ok());
4280
4281 }
4283
4284 async fn idm_fallback_auth_fixture(
4285 idms: &IdmServer,
4286 _idms_delayed: &mut IdmServerDelayed,
4287 has_posix_password: bool,
4288 allow_primary_cred_fallback: Option<bool>,
4289 expected: Option<()>,
4290 ) {
4291 let ct = Duration::from_secs(TEST_CURRENT_TIME);
4292 let target_uuid = Uuid::new_v4();
4293 let p = CryptoPolicy::minimum();
4294
4295 {
4296 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
4297
4298 if let Some(allow_primary_cred_fallback) = allow_primary_cred_fallback {
4299 idms_prox_write
4300 .qs_write
4301 .internal_modify_uuid(
4302 UUID_IDM_ALL_ACCOUNTS,
4303 &ModifyList::new_purge_and_set(
4304 Attribute::AllowPrimaryCredFallback,
4305 Value::new_bool(allow_primary_cred_fallback),
4306 ),
4307 )
4308 .expect("Unable to change default session exp");
4309 }
4310
4311 let mut e = entry_init!(
4312 (Attribute::Class, EntryClass::Object.to_value()),
4313 (Attribute::Class, EntryClass::Account.to_value()),
4314 (Attribute::Class, EntryClass::Person.to_value()),
4315 (Attribute::Uuid, Value::Uuid(target_uuid)),
4316 (Attribute::Name, Value::new_iname("kevin")),
4317 (Attribute::DisplayName, Value::new_utf8s("Kevin")),
4318 (Attribute::Class, EntryClass::PosixAccount.to_value()),
4319 (
4320 Attribute::PrimaryCredential,
4321 Value::Cred(
4322 "primary".to_string(),
4323 Credential::new_password_only(&p, "banana").unwrap()
4324 )
4325 )
4326 );
4327
4328 if has_posix_password {
4329 e.add_ava(
4330 Attribute::UnixPassword,
4331 Value::Cred(
4332 "unix".to_string(),
4333 Credential::new_password_only(&p, "kampai").unwrap(),
4334 ),
4335 );
4336 }
4337
4338 let ce = CreateEvent::new_internal(vec![e]);
4339 let cr = idms_prox_write.qs_write.create(&ce);
4340 assert!(cr.is_ok());
4341 idms_prox_write.commit().expect("Must not fail");
4342 }
4343
4344 let result = idms
4345 .auth()
4346 .await
4347 .unwrap()
4348 .auth_ldap(
4349 &LdapAuthEvent {
4350 target: target_uuid,
4351 cleartext: if has_posix_password {
4352 "kampai".to_string()
4353 } else {
4354 "banana".to_string()
4355 },
4356 },
4357 ct,
4358 )
4359 .await;
4360
4361 assert!(result.is_ok());
4362 if expected.is_some() {
4363 assert!(result.unwrap().is_some());
4364 } else {
4365 assert!(result.unwrap().is_none());
4366 }
4367 }
4368
4369 #[idm_test]
4370 async fn test_idm_fallback_auth_no_pass_none_fallback(
4371 idms: &IdmServer,
4372 _idms_delayed: &mut IdmServerDelayed,
4373 ) {
4374 idm_fallback_auth_fixture(idms, _idms_delayed, false, None, None).await;
4375 }
4376 #[idm_test]
4377 async fn test_idm_fallback_auth_pass_none_fallback(
4378 idms: &IdmServer,
4379 _idms_delayed: &mut IdmServerDelayed,
4380 ) {
4381 idm_fallback_auth_fixture(idms, _idms_delayed, true, None, Some(())).await;
4382 }
4383 #[idm_test]
4384 async fn test_idm_fallback_auth_no_pass_true_fallback(
4385 idms: &IdmServer,
4386 _idms_delayed: &mut IdmServerDelayed,
4387 ) {
4388 idm_fallback_auth_fixture(idms, _idms_delayed, false, Some(true), Some(())).await;
4389 }
4390 #[idm_test]
4391 async fn test_idm_fallback_auth_pass_true_fallback(
4392 idms: &IdmServer,
4393 _idms_delayed: &mut IdmServerDelayed,
4394 ) {
4395 idm_fallback_auth_fixture(idms, _idms_delayed, true, Some(true), Some(())).await;
4396 }
4397 #[idm_test]
4398 async fn test_idm_fallback_auth_no_pass_false_fallback(
4399 idms: &IdmServer,
4400 _idms_delayed: &mut IdmServerDelayed,
4401 ) {
4402 idm_fallback_auth_fixture(idms, _idms_delayed, false, Some(false), None).await;
4403 }
4404 #[idm_test]
4405 async fn test_idm_fallback_auth_pass_false_fallback(
4406 idms: &IdmServer,
4407 _idms_delayed: &mut IdmServerDelayed,
4408 ) {
4409 idm_fallback_auth_fixture(idms, _idms_delayed, true, Some(false), Some(())).await;
4410 }
4411}