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