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