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