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