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::prelude::*;
31use crate::server::keys::KeyProvidersTransaction;
32use crate::server::DomainInfo;
33use crate::utils::{password_from_random, readable_password_from_random, uuid_from_duration, Sid};
34use crate::value::{Session, SessionState};
35use compact_jwt::{Jwk, JwsCompact};
36use concread::bptree::{BptreeMap, BptreeMapReadTxn, BptreeMapWriteTxn};
37use concread::cowcell::CowCellReadTxn;
38use concread::hashmap::HashMap;
39use kanidm_lib_crypto::CryptoPolicy;
40use kanidm_proto::internal::{
41 ApiToken, CredentialStatus, PasswordFeedback, RadiusAuthToken, ScimSyncToken, UatPurpose,
42 UserAuthToken,
43};
44use kanidm_proto::v1::{UnixGroupToken, UnixUserToken};
45use rand::prelude::*;
46use std::convert::TryFrom;
47use std::sync::Arc;
48use std::time::Duration;
49use tokio::sync::mpsc::{
50 unbounded_channel as unbounded, UnboundedReceiver as Receiver, UnboundedSender as Sender,
51};
52use tokio::sync::{Mutex, Semaphore};
53use tracing::trace;
54use url::Url;
55use webauthn_rs::prelude::{Webauthn, WebauthnBuilder};
56use zxcvbn::{zxcvbn, Score};
57
58#[cfg(test)]
59use crate::idm::event::PasswordChangeEvent;
60
61pub(crate) type AuthSessionMutex = Arc<Mutex<AuthSession>>;
62pub(crate) type CredSoftLockMutex = Arc<Mutex<CredSoftLock>>;
63
64pub type DomainInfoRead = CowCellReadTxn<DomainInfo>;
65
66pub struct IdmServer {
67 session_ticket: Semaphore,
72 sessions: BptreeMap<Uuid, AuthSessionMutex>,
73 softlocks: HashMap<Uuid, CredSoftLockMutex>,
74 cred_update_sessions: BptreeMap<Uuid, CredentialUpdateSessionMutex>,
76 qs: QueryServer,
78 crypto_policy: CryptoPolicy,
80 async_tx: Sender<DelayedAction>,
81 audit_tx: Sender<AuditEvent>,
82 webauthn: Webauthn,
84 oauth2rs: Arc<Oauth2ResourceServers>,
85 applications: Arc<LdapApplications>,
86}
87
88pub struct IdmServerAuthTransaction<'a> {
90 pub(crate) session_ticket: &'a Semaphore,
91 pub(crate) sessions: &'a BptreeMap<Uuid, AuthSessionMutex>,
92 pub(crate) softlocks: &'a HashMap<Uuid, CredSoftLockMutex>,
93
94 pub qs_read: QueryServerReadTransaction<'a>,
95 pub(crate) sid: Sid,
97 pub(crate) async_tx: Sender<DelayedAction>,
99 pub(crate) audit_tx: Sender<AuditEvent>,
100 pub(crate) webauthn: &'a Webauthn,
101 pub(crate) applications: LdapApplicationsReadTransaction,
102}
103
104pub struct IdmServerCredUpdateTransaction<'a> {
105 pub(crate) qs_read: QueryServerReadTransaction<'a>,
106 pub(crate) webauthn: &'a Webauthn,
108 pub(crate) cred_update_sessions: BptreeMapReadTxn<'a, Uuid, CredentialUpdateSessionMutex>,
109 pub(crate) crypto_policy: &'a CryptoPolicy,
110}
111
112pub struct IdmServerProxyReadTransaction<'a> {
114 pub qs_read: QueryServerReadTransaction<'a>,
115 pub(crate) oauth2rs: Oauth2ResourceServersReadTransaction,
116}
117
118pub struct IdmServerProxyWriteTransaction<'a> {
119 pub qs_write: QueryServerWriteTransaction<'a>,
122 pub(crate) cred_update_sessions: BptreeMapWriteTxn<'a, Uuid, CredentialUpdateSessionMutex>,
124 pub(crate) sid: Sid,
125 crypto_policy: &'a CryptoPolicy,
126 webauthn: &'a Webauthn,
127 pub(crate) oauth2rs: Oauth2ResourceServersWriteTransaction<'a>,
128 pub(crate) applications: LdapApplicationsWriteTransaction<'a>,
129}
130
131pub struct IdmServerDelayed {
132 pub(crate) async_rx: Receiver<DelayedAction>,
133}
134
135pub struct IdmServerAudit {
136 pub(crate) audit_rx: Receiver<AuditEvent>,
137}
138
139impl IdmServer {
140 pub async fn new(
141 qs: QueryServer,
142 origin: &str,
143 is_integration_test: bool,
144 current_time: Duration,
145 ) -> Result<(IdmServer, IdmServerDelayed, IdmServerAudit), OperationError> {
146 let crypto_policy = if cfg!(test) || is_integration_test {
147 CryptoPolicy::danger_test_minimum()
148 } else {
149 CryptoPolicy::time_target(Duration::from_millis(10))
152 };
153
154 let (async_tx, async_rx) = unbounded();
155 let (audit_tx, audit_rx) = unbounded();
156
157 let (rp_id, rp_name, application_set) = {
159 let mut qs_read = qs.read().await?;
160 (
161 qs_read.get_domain_name().to_string(),
162 qs_read.get_domain_display_name().to_string(),
163 qs_read.get_applications_set()?,
165 )
166 };
167
168 let origin_url = Url::parse(origin)
170 .map_err(|_e| {
171 admin_error!("Unable to parse origin URL - refusing to start. You must correct the value for origin. {:?}", origin);
172 OperationError::InvalidState
173 })
174 .and_then(|url| {
175 let valid = url.domain().map(|effective_domain| {
176 effective_domain.ends_with(&format!(".{rp_id}"))
179 || effective_domain == rp_id
180 }).unwrap_or(false);
181
182 if valid {
183 Ok(url)
184 } else {
185 admin_error!("Effective domain (ed) is not a descendent of server domain name (rp_id).");
186 admin_error!("You must change origin or domain name to be consistent. ded: {:?} - rp_id: {:?}", origin, rp_id);
187 admin_error!("To change the origin or domain name see: https://kanidm.github.io/kanidm/master/server_configuration.html");
188 Err(OperationError::InvalidState)
189 }
190 })?;
191
192 let webauthn = WebauthnBuilder::new(&rp_id, &origin_url)
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_url).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 } = client_auth_info;
414
415 match (client_cert, bearer_token) {
416 (Some(client_cert_info), _) => {
417 self.client_certificate_to_identity(&client_cert_info, ct, source)
418 }
419 (None, Some(token)) => match self.validate_and_parse_token_to_token(&token, ct)? {
420 Token::UserAuthToken(uat) => self.process_uat_to_identity(&uat, ct, source),
421 Token::ApiToken(apit, entry) => {
422 self.process_apit_to_identity(&apit, source, entry, ct)
423 }
424 },
425 (None, None) => {
426 debug!("No client certificate or bearer tokens were supplied");
427 Err(OperationError::NotAuthenticated)
428 }
429 }
430 }
431
432 #[instrument(level = "info", skip_all)]
436 fn validate_client_auth_info_to_uat(
437 &mut self,
438 client_auth_info: ClientAuthInfo,
439 ct: Duration,
440 ) -> Result<UserAuthToken, OperationError> {
441 let ClientAuthInfo {
442 client_cert,
443 bearer_token,
444 source: _,
445 basic_authz: _,
446 } = client_auth_info;
447
448 match (client_cert, bearer_token) {
449 (Some(client_cert_info), _) => {
450 self.client_certificate_to_user_auth_token(&client_cert_info, ct)
451 }
452 (None, Some(token)) => match self.validate_and_parse_token_to_token(&token, ct)? {
453 Token::UserAuthToken(uat) => Ok(uat),
454 Token::ApiToken(_apit, _entry) => {
455 warn!("Unable to process non user auth token");
456 Err(OperationError::NotAuthenticated)
457 }
458 },
459 (None, None) => {
460 debug!("No client certificate or bearer tokens were supplied");
461 Err(OperationError::NotAuthenticated)
462 }
463 }
464 }
465
466 fn validate_and_parse_token_to_token(
467 &mut self,
468 jwsu: &JwsCompact,
469 ct: Duration,
470 ) -> Result<Token, OperationError> {
471 let jws_inner = self
474 .get_qs_txn()
475 .get_domain_key_object_handle()?
476 .jws_verify(jwsu)
477 .map_err(|err| {
478 security_info!(?err, "Unable to verify token");
479 OperationError::NotAuthenticated
480 })?;
481
482 if let Ok(uat) = jws_inner.from_json::<UserAuthToken>() {
484 if let Some(exp) = uat.expiry {
485 let ct_odt = time::OffsetDateTime::UNIX_EPOCH + ct;
486 if exp < ct_odt {
487 security_info!(?ct_odt, ?exp, "Session expired");
488 return Err(OperationError::SessionExpired);
489 } else {
490 trace!(?ct_odt, ?exp, "Session not yet expired");
491 return Ok(Token::UserAuthToken(uat));
492 }
493 } else {
494 debug!("Session has no expiry");
495 return Ok(Token::UserAuthToken(uat));
496 }
497 };
498
499 if let Ok(apit) = jws_inner.from_json::<ApiToken>() {
501 if let Some(expiry) = apit.expiry {
502 if time::OffsetDateTime::UNIX_EPOCH + ct >= expiry {
503 security_info!("Session expired");
504 return Err(OperationError::SessionExpired);
505 }
506 }
507
508 let entry = self
509 .get_qs_txn()
510 .internal_search_uuid(apit.account_id)
511 .map_err(|err| {
512 security_info!(?err, "Account associated with api token no longer exists.");
513 OperationError::NotAuthenticated
514 })?;
515
516 return Ok(Token::ApiToken(apit, entry));
517 };
518
519 security_info!("Unable to verify token, invalid inner JSON");
520 Err(OperationError::NotAuthenticated)
521 }
522
523 fn check_oauth2_account_uuid_valid(
524 &mut self,
525 uuid: Uuid,
526 session_id: Uuid,
527 parent_session_id: Option<Uuid>,
528 iat: i64,
529 ct: Duration,
530 ) -> Result<Option<Arc<Entry<EntrySealed, EntryCommitted>>>, OperationError> {
531 let entry = self.get_qs_txn().internal_search_uuid(uuid).map_err(|e| {
532 admin_error!(?e, "check_oauth2_account_uuid_valid failed");
533 e
534 })?;
535
536 let within_valid_window = Account::check_within_valid_time(
537 ct,
538 entry
539 .get_ava_single_datetime(Attribute::AccountValidFrom)
540 .as_ref(),
541 entry
542 .get_ava_single_datetime(Attribute::AccountExpire)
543 .as_ref(),
544 );
545
546 if !within_valid_window {
547 security_info!("Account has expired or is not yet valid, not allowing to proceed");
548 return Ok(None);
549 }
550
551 let grace_valid = ct < (Duration::from_secs(iat as u64) + AUTH_TOKEN_GRACE_WINDOW);
556
557 let oauth2_session = entry
558 .get_ava_as_oauth2session_map(Attribute::OAuth2Session)
559 .and_then(|sessions| sessions.get(&session_id));
560
561 if let Some(oauth2_session) = oauth2_session {
562 let oauth2_session_valid = !matches!(oauth2_session.state, SessionState::RevokedAt(_));
564
565 if !oauth2_session_valid {
566 security_info!("The oauth2 session associated to this token is revoked.");
567 return Ok(None);
568 }
569
570 if let Some(parent_session_id) = parent_session_id {
572 let uat_session = entry
573 .get_ava_as_session_map(Attribute::UserAuthTokenSession)
574 .and_then(|sessions| sessions.get(&parent_session_id));
575
576 if let Some(uat_session) = uat_session {
577 let parent_session_valid =
578 !matches!(uat_session.state, SessionState::RevokedAt(_));
579 if parent_session_valid {
580 security_info!(
581 "A valid parent and oauth2 session value exists for this token"
582 );
583 } else {
584 security_info!(
585 "The parent oauth2 session associated to this token is revoked."
586 );
587 return Ok(None);
588 }
589 } else if grace_valid {
590 security_info!(
591 "The token grace window is in effect. Assuming parent session valid."
592 );
593 } else {
594 security_info!("The token grace window has passed and no entry parent sessions exist. Assuming invalid.");
595 return Ok(None);
596 }
597 }
598 } else if grace_valid {
600 security_info!("The token grace window is in effect. Assuming valid.");
601 } else {
602 security_info!(
603 "The token grace window has passed and no entry sessions exist. Assuming invalid."
604 );
605 return Ok(None);
606 }
607
608 Ok(Some(entry))
609 }
610
611 #[instrument(level = "debug", skip_all)]
624 fn process_uat_to_identity(
625 &mut self,
626 uat: &UserAuthToken,
627 ct: Duration,
628 source: Source,
629 ) -> Result<Identity, OperationError> {
630 let entry = self
632 .get_qs_txn()
633 .internal_search_uuid(uat.uuid)
634 .map_err(|e| {
635 admin_error!(?e, "from_ro_uat failed");
636 e
637 })?;
638
639 let valid = Account::check_user_auth_token_valid(ct, uat, &entry);
640
641 if !valid {
642 return Err(OperationError::SessionExpired);
643 }
644
645 let scope = match uat.purpose {
648 UatPurpose::ReadOnly => AccessScope::ReadOnly,
649 UatPurpose::ReadWrite { expiry: None } => AccessScope::ReadOnly,
650 UatPurpose::ReadWrite {
651 expiry: Some(expiry),
652 } => {
653 let cot = time::OffsetDateTime::UNIX_EPOCH + ct;
654 if cot < expiry {
655 AccessScope::ReadWrite
656 } else {
657 AccessScope::ReadOnly
658 }
659 }
660 };
661
662 let mut limits = Limits::default();
663 if let Some(lim) = uat.limit_search_max_results.and_then(|v| v.try_into().ok()) {
665 limits.search_max_results = lim;
666 }
667 if let Some(lim) = uat
668 .limit_search_max_filter_test
669 .and_then(|v| v.try_into().ok())
670 {
671 limits.search_max_filter_test = lim;
672 }
673
674 Ok(Identity::new(
690 IdentType::User(IdentUser { entry }),
691 source,
692 uat.session_id,
693 scope,
694 limits,
695 ))
696 }
697
698 #[instrument(level = "debug", skip_all)]
699 fn process_apit_to_identity(
700 &mut self,
701 apit: &ApiToken,
702 source: Source,
703 entry: Arc<EntrySealedCommitted>,
704 ct: Duration,
705 ) -> Result<Identity, OperationError> {
706 let valid = ServiceAccount::check_api_token_valid(ct, apit, &entry);
707
708 if !valid {
709 return Err(OperationError::SessionExpired);
711 }
712
713 let scope = (&apit.purpose).into();
714
715 let limits = Limits::api_token();
716 Ok(Identity::new(
717 IdentType::User(IdentUser { entry }),
718 source,
719 apit.token_id,
720 scope,
721 limits,
722 ))
723 }
724
725 fn client_cert_info_entry(
726 &mut self,
727 client_cert_info: &ClientCertInfo,
728 ) -> Result<Arc<EntrySealedCommitted>, OperationError> {
729 let pks256 = hex::encode(client_cert_info.public_key_s256);
730 let mut maybe_cert_entries = self.get_qs_txn().internal_search(filter!(f_eq(
732 Attribute::Certificate,
733 PartialValue::HexString(pks256.clone())
734 )))?;
735
736 let maybe_cert_entry = maybe_cert_entries.pop();
737
738 if let Some(cert_entry) = maybe_cert_entry {
739 if maybe_cert_entries.is_empty() {
740 Ok(cert_entry)
741 } else {
742 debug!(?pks256, "Multiple certificates matched, unable to proceed.");
743 Err(OperationError::NotAuthenticated)
744 }
745 } else {
746 debug!(?pks256, "No certificates were able to be mapped.");
747 Err(OperationError::NotAuthenticated)
748 }
749 }
750
751 #[instrument(level = "debug", skip_all)]
760 fn client_certificate_to_identity(
761 &mut self,
762 client_cert_info: &ClientCertInfo,
763 ct: Duration,
764 source: Source,
765 ) -> Result<Identity, OperationError> {
766 let cert_entry = self.client_cert_info_entry(client_cert_info)?;
767
768 let refers_uuid = cert_entry
770 .get_ava_single_refer(Attribute::Refers)
771 .ok_or_else(|| {
772 warn!("Invalid certificate entry, missing refers");
773 OperationError::InvalidState
774 })?;
775
776 let entry = self.get_qs_txn().internal_search_uuid(refers_uuid)?;
778
779 let (account, account_policy) =
780 Account::try_from_entry_with_policy(entry.as_ref(), self.get_qs_txn())?;
781
782 if !account.is_within_valid_time(ct) {
784 return Err(OperationError::SessionExpired);
786 };
787
788 let scope = AccessScope::ReadOnly;
790
791 let mut limits = Limits::default();
792 if let Some(lim) = account_policy
794 .limit_search_max_results()
795 .and_then(|v| v.try_into().ok())
796 {
797 limits.search_max_results = lim;
798 }
799 if let Some(lim) = account_policy
800 .limit_search_max_filter_test()
801 .and_then(|v| v.try_into().ok())
802 {
803 limits.search_max_filter_test = lim;
804 }
805
806 let certificate_uuid = cert_entry.get_uuid();
807
808 Ok(Identity::new(
809 IdentType::User(IdentUser { entry }),
810 source,
811 certificate_uuid,
813 scope,
814 limits,
815 ))
816 }
817
818 #[instrument(level = "debug", skip_all)]
819 fn client_certificate_to_user_auth_token(
820 &mut self,
821 client_cert_info: &ClientCertInfo,
822 ct: Duration,
823 ) -> Result<UserAuthToken, OperationError> {
824 let cert_entry = self.client_cert_info_entry(client_cert_info)?;
825
826 let refers_uuid = cert_entry
828 .get_ava_single_refer(Attribute::Refers)
829 .ok_or_else(|| {
830 warn!("Invalid certificate entry, missing refers");
831 OperationError::InvalidState
832 })?;
833
834 let entry = self.get_qs_txn().internal_search_uuid(refers_uuid)?;
836
837 let (account, account_policy) =
838 Account::try_from_entry_with_policy(entry.as_ref(), self.get_qs_txn())?;
839
840 if !account.is_within_valid_time(ct) {
842 return Err(OperationError::SessionExpired);
844 };
845
846 let certificate_uuid = cert_entry.get_uuid();
847 let session_is_rw = false;
848
849 account
850 .client_cert_info_to_userauthtoken(certificate_uuid, session_is_rw, ct, &account_policy)
851 .ok_or(OperationError::InvalidState)
852 }
853
854 fn process_ldap_uuid_to_identity(
855 &mut self,
856 uuid: &Uuid,
857 ct: Duration,
858 source: Source,
859 ) -> Result<Identity, OperationError> {
860 let entry = self
861 .get_qs_txn()
862 .internal_search_uuid(*uuid)
863 .map_err(|err| {
864 error!(?err, ?uuid, "Failed to search user by uuid");
865 err
866 })?;
867
868 let (account, account_policy) =
869 Account::try_from_entry_with_policy(entry.as_ref(), self.get_qs_txn())?;
870
871 if !account.is_within_valid_time(ct) {
872 info!("Account is expired or not yet valid.");
873 return Err(OperationError::SessionExpired);
874 }
875
876 let anon_entry = if *uuid == UUID_ANONYMOUS {
878 entry
880 } else {
881 self.get_qs_txn()
883 .internal_search_uuid(UUID_ANONYMOUS)
884 .map_err(|err| {
885 error!(
886 ?err,
887 "Unable to search anonymous user for privilege bounding."
888 );
889 err
890 })?
891 };
892
893 let mut limits = Limits::default();
894 let session_id = Uuid::new_v4();
895
896 if let Some(max_results) = account_policy.limit_search_max_results() {
898 limits.search_max_results = max_results as usize;
899 }
900 if let Some(max_filter) = account_policy.limit_search_max_filter_test() {
901 limits.search_max_filter_test = max_filter as usize;
902 }
903
904 Ok(Identity::new(
907 IdentType::User(IdentUser { entry: anon_entry }),
908 source,
909 session_id,
910 AccessScope::ReadOnly,
911 limits,
912 ))
913 }
914
915 #[instrument(level = "debug", skip_all)]
916 fn validate_ldap_session(
917 &mut self,
918 session: &LdapSession,
919 source: Source,
920 ct: Duration,
921 ) -> Result<Identity, OperationError> {
922 match session {
923 LdapSession::UnixBind(uuid) | LdapSession::ApplicationPasswordBind(_, uuid) => {
924 self.process_ldap_uuid_to_identity(uuid, ct, source)
925 }
926 LdapSession::UserAuthToken(uat) => self.process_uat_to_identity(uat, ct, source),
927 LdapSession::ApiToken(apit) => {
928 let entry = self
929 .get_qs_txn()
930 .internal_search_uuid(apit.account_id)
931 .map_err(|e| {
932 admin_error!("Failed to validate ldap session -> {:?}", e);
933 e
934 })?;
935
936 self.process_apit_to_identity(apit, source, entry, ct)
937 }
938 }
939 }
940
941 #[instrument(level = "info", skip_all)]
942 fn validate_sync_client_auth_info_to_ident(
943 &mut self,
944 client_auth_info: ClientAuthInfo,
945 ct: Duration,
946 ) -> Result<Identity, OperationError> {
947 let jwsu = client_auth_info.bearer_token.ok_or_else(|| {
950 security_info!("No token provided");
951 OperationError::NotAuthenticated
952 })?;
953
954 let jws_inner = self
955 .get_qs_txn()
956 .get_domain_key_object_handle()?
957 .jws_verify(&jwsu)
958 .map_err(|err| {
959 security_info!(?err, "Unable to verify token");
960 OperationError::NotAuthenticated
961 })?;
962
963 let sync_token = jws_inner.from_json::<ScimSyncToken>().map_err(|err| {
964 error!(?err, "Unable to deserialise JWS");
965 OperationError::SerdeJsonError
966 })?;
967
968 let entry = self
969 .get_qs_txn()
970 .internal_search(filter!(f_eq(
971 Attribute::SyncTokenSession,
972 PartialValue::Refer(sync_token.token_id)
973 )))
974 .and_then(|mut vs| match vs.pop() {
975 Some(entry) if vs.is_empty() => Ok(entry),
976 _ => {
977 admin_error!(
978 token_id = ?sync_token.token_id,
979 "entries was empty, or matched multiple results for token id"
980 );
981 Err(OperationError::NotAuthenticated)
982 }
983 })?;
984
985 let valid = SyncAccount::check_sync_token_valid(ct, &sync_token, &entry);
986
987 if !valid {
988 security_info!("Unable to proceed with invalid sync token");
989 return Err(OperationError::NotAuthenticated);
990 }
991
992 let scope = (&sync_token.purpose).into();
994
995 let limits = Limits::unlimited();
996 Ok(Identity::new(
997 IdentType::Synch(entry.get_uuid()),
998 client_auth_info.source,
999 sync_token.token_id,
1000 scope,
1001 limits,
1002 ))
1003 }
1004}
1005
1006impl<'a> IdmServerTransaction<'a> for IdmServerAuthTransaction<'a> {
1007 type QsTransactionType = QueryServerReadTransaction<'a>;
1008
1009 fn get_qs_txn(&mut self) -> &mut Self::QsTransactionType {
1010 &mut self.qs_read
1011 }
1012}
1013
1014impl IdmServerAuthTransaction<'_> {
1015 #[cfg(test)]
1016 pub fn is_sessionid_present(&self, sessionid: Uuid) -> bool {
1017 let session_read = self.sessions.read();
1018 session_read.contains_key(&sessionid)
1019 }
1020
1021 pub fn get_origin(&self) -> &Url {
1022 #[allow(clippy::unwrap_used)]
1023 self.webauthn.get_allowed_origins().first().unwrap()
1024 }
1025
1026 #[instrument(level = "trace", skip(self))]
1027 pub async fn expire_auth_sessions(&mut self, ct: Duration) {
1028 let expire = ct - Duration::from_secs(AUTH_SESSION_TIMEOUT);
1030 let split_at = uuid_from_duration(expire, self.sid);
1031 let _session_ticket = self.session_ticket.acquire().await;
1033 let mut session_write = self.sessions.write();
1034 session_write.split_off_lt(&split_at);
1035 session_write.commit();
1037 }
1038
1039 pub async fn auth(
1040 &mut self,
1041 ae: &AuthEvent,
1042 ct: Duration,
1043 client_auth_info: ClientAuthInfo,
1044 ) -> Result<AuthResult, OperationError> {
1045 match &ae.step {
1047 AuthEventStep::Init(init) => {
1048 let sessionid = uuid_from_duration(ct, self.sid);
1051
1052 let euuid = self.qs_read.name_to_uuid(init.username.as_str())?;
1068
1069 let entry = self.qs_read.internal_search_uuid(euuid)?;
1071
1072 security_info!(
1073 username = %init.username,
1074 issue = ?init.issue,
1075 privileged = ?init.privileged,
1076 uuid = %euuid,
1077 "Initiating Authentication Session",
1078 );
1079
1080 let (account, account_policy) =
1085 Account::try_from_entry_with_policy(entry.as_ref(), &mut self.qs_read)?;
1086
1087 trace!(?account.primary);
1088
1089 let _session_ticket = self.session_ticket.acquire().await;
1091
1092 let _maybe_slock_ref =
1098 account
1099 .primary_cred_uuid_and_policy()
1100 .map(|(cred_uuid, policy)| {
1101 let mut softlock_write = self.softlocks.write();
1106 let slock_ref: CredSoftLockMutex =
1107 if let Some(slock_ref) = softlock_write.get(&cred_uuid) {
1108 slock_ref.clone()
1109 } else {
1110 let slock = Arc::new(Mutex::new(CredSoftLock::new(policy)));
1112 softlock_write.insert(cred_uuid, slock.clone());
1113 slock
1114 };
1115 softlock_write.commit();
1116 slock_ref
1117 });
1118
1119 let asd: AuthSessionData = AuthSessionData {
1120 account,
1121 account_policy,
1122 issue: init.issue,
1123 webauthn: self.webauthn,
1124 ct,
1125 client_auth_info,
1126 };
1127
1128 let domain_keys = self.qs_read.get_domain_key_object_handle()?;
1129
1130 let (auth_session, state) = AuthSession::new(asd, init.privileged, domain_keys);
1131
1132 match auth_session {
1133 Some(auth_session) => {
1134 let mut session_write = self.sessions.write();
1135 if session_write.contains_key(&sessionid) {
1136 Err(OperationError::InvalidSessionState)
1139 } else {
1140 session_write.insert(sessionid, Arc::new(Mutex::new(auth_session)));
1141 debug_assert!(session_write.get(&sessionid).is_some());
1143 Ok(())
1144 }?;
1145 session_write.commit();
1146 }
1147 None => {
1148 security_info!("Authentication Session Unable to begin");
1149 }
1150 };
1151
1152 Ok(AuthResult { sessionid, state })
1153 } AuthEventStep::Begin(mech) => {
1155 let session_read = self.sessions.read();
1156 let auth_session_ref = session_read
1158 .get(&mech.sessionid)
1160 .cloned()
1161 .ok_or_else(|| {
1162 admin_error!("Invalid Session State (no present session uuid)");
1163 OperationError::InvalidSessionState
1164 })?;
1165
1166 let mut auth_session = auth_session_ref.lock().await;
1167
1168 let auth_result = auth_session.start_session(&mech.mech);
1170
1171 let is_valid = match auth_session.get_credential_uuid()? {
1172 Some(cred_uuid) => {
1173 let softlock_read = self.softlocks.read();
1176 if let Some(slock_ref) = softlock_read.get(&cred_uuid) {
1177 let mut slock = slock_ref.lock().await;
1178 slock.apply_time_step(ct);
1180 slock.is_valid()
1182 } else {
1183 trace!("slock not found");
1184 false
1185 }
1186 }
1187 None => true,
1188 };
1189
1190 if is_valid {
1191 auth_result
1192 } else {
1193 trace!("lock step begin");
1195 auth_session.end_session("Account is temporarily locked")
1196 }
1197 .map(|aus| AuthResult {
1198 sessionid: mech.sessionid,
1199 state: aus,
1200 })
1201 } AuthEventStep::Cred(creds) => {
1203 let session_read = self.sessions.read();
1207 let auth_session_ref = session_read
1209 .get(&creds.sessionid)
1211 .cloned()
1212 .ok_or_else(|| {
1213 admin_error!("Invalid Session State (no present session uuid)");
1214 OperationError::InvalidSessionState
1215 })?;
1216
1217 let mut auth_session = auth_session_ref.lock().await;
1218
1219 let maybe_slock_ref = match auth_session.get_credential_uuid()? {
1220 Some(cred_uuid) => {
1221 let softlock_read = self.softlocks.read();
1222 softlock_read.get(&cred_uuid).cloned()
1223 }
1224 None => None,
1225 };
1226
1227 let mut maybe_slock = if let Some(s) = maybe_slock_ref.as_ref() {
1230 Some(s.lock().await)
1231 } else {
1232 None
1233 };
1234
1235 let is_valid = if let Some(ref mut slock) = maybe_slock {
1236 slock.apply_time_step(ct);
1238 slock.is_valid()
1240 } else {
1241 true
1243 };
1244
1245 if is_valid {
1246 auth_session
1250 .validate_creds(
1251 &creds.cred,
1252 ct,
1253 &self.async_tx,
1254 &self.audit_tx,
1255 self.webauthn,
1256 self.qs_read.pw_badlist(),
1257 )
1258 .inspect(|aus| {
1259 if let AuthState::Denied(_) = aus {
1262 if let Some(ref mut slock) = maybe_slock {
1264 slock.record_failure(ct);
1265 }
1266 };
1267 })
1268 } else {
1269 trace!("lock step cred");
1271 auth_session.end_session("Account is temporarily locked")
1272 }
1273 .map(|aus| AuthResult {
1274 sessionid: creds.sessionid,
1275 state: aus,
1276 })
1277 } }
1279 }
1280
1281 async fn auth_with_unix_pass(
1282 &mut self,
1283 id: Uuid,
1284 cleartext: &str,
1285 ct: Duration,
1286 ) -> Result<Option<Account>, OperationError> {
1287 let entry = match self.qs_read.internal_search_uuid(id) {
1288 Ok(entry) => entry,
1289 Err(e) => {
1290 admin_error!("Failed to start auth unix -> {:?}", e);
1291 return Err(e);
1292 }
1293 };
1294
1295 let (account, acp) =
1296 Account::try_from_entry_with_policy(entry.as_ref(), &mut self.qs_read)?;
1297
1298 if !account.is_within_valid_time(ct) {
1299 security_info!("Account is expired or not yet valid.");
1300 return Ok(None);
1301 }
1302
1303 let cred = if acp.allow_primary_cred_fallback() == Some(true) {
1304 account
1305 .unix_extn()
1306 .and_then(|extn| extn.ucred())
1307 .or_else(|| account.primary())
1308 } else {
1309 account.unix_extn().and_then(|extn| extn.ucred())
1310 };
1311
1312 let (cred, cred_id, cred_slock_policy) = match cred {
1313 None => {
1314 if acp.allow_primary_cred_fallback() == Some(true) {
1315 security_info!("Account does not have a POSIX or primary password configured.");
1316 } else {
1317 security_info!("Account does not have a POSIX password configured.");
1318 }
1319 return Ok(None);
1320 }
1321 Some(cred) => (cred, cred.uuid, cred.softlock_policy()),
1322 };
1323
1324 let Ok(password) = cred.password_ref() else {
1326 error!("User's UNIX or primary credential is not a password, can't authenticate!");
1327 return Err(OperationError::InvalidState);
1328 };
1329
1330 let slock_ref = {
1331 let softlock_read = self.softlocks.read();
1332 if let Some(slock_ref) = softlock_read.get(&cred_id) {
1333 slock_ref.clone()
1334 } else {
1335 let _session_ticket = self.session_ticket.acquire().await;
1336 let mut softlock_write = self.softlocks.write();
1337 let slock = Arc::new(Mutex::new(CredSoftLock::new(cred_slock_policy)));
1338 softlock_write.insert(cred_id, slock.clone());
1339 softlock_write.commit();
1340 slock
1341 }
1342 };
1343
1344 let mut slock = slock_ref.lock().await;
1345
1346 slock.apply_time_step(ct);
1347
1348 if !slock.is_valid() {
1349 security_info!("Account is softlocked.");
1350 return Ok(None);
1351 }
1352
1353 let valid = password.verify(cleartext).map_err(|e| {
1355 error!(crypto_err = ?e);
1356 e.into()
1357 })?;
1358
1359 if !valid {
1360 slock.record_failure(ct);
1362
1363 return Ok(None);
1364 }
1365
1366 security_info!("Successfully authenticated with unix (or primary) password");
1367 if password.requires_upgrade() {
1368 self.async_tx
1369 .send(DelayedAction::UnixPwUpgrade(UnixPasswordUpgrade {
1370 target_uuid: id,
1371 existing_password: cleartext.to_string(),
1372 }))
1373 .map_err(|_| {
1374 admin_error!("failed to queue delayed action - unix password upgrade");
1375 OperationError::InvalidState
1376 })?;
1377 }
1378
1379 Ok(Some(account))
1380 }
1381
1382 pub async fn auth_unix(
1383 &mut self,
1384 uae: &UnixUserAuthEvent,
1385 ct: Duration,
1386 ) -> Result<Option<UnixUserToken>, OperationError> {
1387 Ok(self
1388 .auth_with_unix_pass(uae.target, &uae.cleartext, ct)
1389 .await?
1390 .and_then(|acc| acc.to_unixusertoken(ct).ok()))
1391 }
1392
1393 pub async fn auth_ldap(
1394 &mut self,
1395 lae: &LdapAuthEvent,
1396 ct: Duration,
1397 ) -> Result<Option<LdapBoundToken>, OperationError> {
1398 if lae.target == UUID_ANONYMOUS {
1399 let account_entry = self.qs_read.internal_search_uuid(lae.target).map_err(|e| {
1400 admin_error!("Failed to start auth ldap -> {:?}", e);
1401 e
1402 })?;
1403
1404 let account = Account::try_from_entry_ro(account_entry.as_ref(), &mut self.qs_read)?;
1405
1406 if !account.is_within_valid_time(ct) {
1408 security_info!("Account is not within valid time period");
1409 return Ok(None);
1410 }
1411
1412 let session_id = Uuid::new_v4();
1413 security_info!(
1414 "Starting session {} for {} {}",
1415 session_id,
1416 account.spn,
1417 account.uuid
1418 );
1419
1420 Ok(Some(LdapBoundToken {
1422 session_id,
1423 spn: account.spn,
1424 effective_session: LdapSession::UnixBind(UUID_ANONYMOUS),
1425 }))
1426 } else {
1427 if !self.qs_read.d_info.d_ldap_allow_unix_pw_bind {
1428 security_info!("Bind not allowed through Unix passwords.");
1429 return Ok(None);
1430 }
1431
1432 let auth = self
1433 .auth_with_unix_pass(lae.target, &lae.cleartext, ct)
1434 .await?;
1435
1436 match auth {
1437 Some(account) => {
1438 let session_id = Uuid::new_v4();
1439 security_info!(
1440 "Starting session {} for {} {}",
1441 session_id,
1442 account.spn,
1443 account.uuid
1444 );
1445
1446 Ok(Some(LdapBoundToken {
1447 spn: account.spn,
1448 session_id,
1449 effective_session: LdapSession::UnixBind(account.uuid),
1450 }))
1451 }
1452 None => Ok(None),
1453 }
1454 }
1455 }
1456
1457 pub async fn token_auth_ldap(
1458 &mut self,
1459 lae: &LdapTokenAuthEvent,
1460 ct: Duration,
1461 ) -> Result<Option<LdapBoundToken>, OperationError> {
1462 match self.validate_and_parse_token_to_token(&lae.token, ct)? {
1463 Token::UserAuthToken(uat) => {
1464 let spn = uat.spn.clone();
1465 Ok(Some(LdapBoundToken {
1466 session_id: uat.session_id,
1467 spn,
1468 effective_session: LdapSession::UserAuthToken(uat),
1469 }))
1470 }
1471 Token::ApiToken(apit, entry) => {
1472 let spn = entry
1473 .get_ava_single_proto_string(Attribute::Spn)
1474 .ok_or_else(|| OperationError::MissingAttribute(Attribute::Spn))?;
1475
1476 Ok(Some(LdapBoundToken {
1477 session_id: apit.token_id,
1478 spn,
1479 effective_session: LdapSession::ApiToken(apit),
1480 }))
1481 }
1482 }
1483 }
1484
1485 pub fn commit(self) -> Result<(), OperationError> {
1486 Ok(())
1487 }
1488}
1489
1490impl<'a> IdmServerTransaction<'a> for IdmServerProxyReadTransaction<'a> {
1491 type QsTransactionType = QueryServerReadTransaction<'a>;
1492
1493 fn get_qs_txn(&mut self) -> &mut Self::QsTransactionType {
1494 &mut self.qs_read
1495 }
1496}
1497
1498fn gen_password_mod(
1499 cleartext: &str,
1500 crypto_policy: &CryptoPolicy,
1501) -> Result<ModifyList<ModifyInvalid>, OperationError> {
1502 let new_cred = Credential::new_password_only(crypto_policy, cleartext)?;
1503 let cred_value = Value::new_credential("unix", new_cred);
1504 Ok(ModifyList::new_purge_and_set(
1505 Attribute::UnixPassword,
1506 cred_value,
1507 ))
1508}
1509
1510fn gen_password_upgrade_mod(
1511 unix_cred: &Credential,
1512 cleartext: &str,
1513 crypto_policy: &CryptoPolicy,
1514) -> Result<Option<ModifyList<ModifyInvalid>>, OperationError> {
1515 if let Some(new_cred) = unix_cred.upgrade_password(crypto_policy, cleartext)? {
1516 let cred_value = Value::new_credential("primary", new_cred);
1517 Ok(Some(ModifyList::new_purge_and_set(
1518 Attribute::UnixPassword,
1519 cred_value,
1520 )))
1521 } else {
1522 Ok(None)
1524 }
1525}
1526
1527impl IdmServerProxyReadTransaction<'_> {
1528 pub fn jws_public_jwk(&mut self, key_id: &str) -> Result<Jwk, OperationError> {
1529 self.qs_read
1530 .get_key_providers()
1531 .get_key_object_handle(UUID_DOMAIN_INFO)
1532 .ok_or(OperationError::NoMatchingEntries)
1534 .and_then(|key_object| key_object.jws_public_jwk(key_id))
1535 .and_then(|maybe_key: Option<Jwk>| maybe_key.ok_or(OperationError::NoMatchingEntries))
1536 }
1537
1538 pub fn get_radiusauthtoken(
1539 &mut self,
1540 rate: &RadiusAuthTokenEvent,
1541 ct: Duration,
1542 ) -> Result<RadiusAuthToken, OperationError> {
1543 let account = self
1544 .qs_read
1545 .impersonate_search_ext_uuid(rate.target, &rate.ident)
1546 .and_then(|account_entry| {
1547 RadiusAccount::try_from_entry_reduced(&account_entry, &mut self.qs_read)
1548 })
1549 .map_err(|e| {
1550 admin_error!("Failed to start radius auth token {:?}", e);
1551 e
1552 })?;
1553
1554 account.to_radiusauthtoken(ct)
1555 }
1556
1557 pub fn get_unixusertoken(
1558 &mut self,
1559 uute: &UnixUserTokenEvent,
1560 ct: Duration,
1561 ) -> Result<UnixUserToken, OperationError> {
1562 let account = self
1563 .qs_read
1564 .impersonate_search_uuid(uute.target, &uute.ident)
1565 .and_then(|account_entry| Account::try_from_entry_ro(&account_entry, &mut self.qs_read))
1566 .map_err(|e| {
1567 admin_error!("Failed to start unix user token -> {:?}", e);
1568 e
1569 })?;
1570
1571 account.to_unixusertoken(ct)
1572 }
1573
1574 pub fn get_unixgrouptoken(
1575 &mut self,
1576 uute: &UnixGroupTokenEvent,
1577 ) -> Result<UnixGroupToken, OperationError> {
1578 let group = self
1579 .qs_read
1580 .impersonate_search_ext_uuid(uute.target, &uute.ident)
1581 .and_then(|e| Group::<Unix>::try_from_entry(&e))
1582 .map_err(|e| {
1583 admin_error!("Failed to start unix group token {:?}", e);
1584 e
1585 })?;
1586 Ok(group.to_unixgrouptoken())
1587 }
1588
1589 pub fn get_credentialstatus(
1590 &mut self,
1591 cse: &CredentialStatusEvent,
1592 ) -> Result<CredentialStatus, OperationError> {
1593 let account = self
1594 .qs_read
1595 .impersonate_search_ext_uuid(cse.target, &cse.ident)
1596 .and_then(|account_entry| {
1597 Account::try_from_entry_reduced(&account_entry, &mut self.qs_read)
1598 })
1599 .map_err(|e| {
1600 admin_error!("Failed to search account {:?}", e);
1601 e
1602 })?;
1603
1604 account.to_credentialstatus()
1605 }
1606}
1607
1608impl<'a> IdmServerTransaction<'a> for IdmServerProxyWriteTransaction<'a> {
1609 type QsTransactionType = QueryServerWriteTransaction<'a>;
1610
1611 fn get_qs_txn(&mut self) -> &mut Self::QsTransactionType {
1612 &mut self.qs_write
1613 }
1614}
1615
1616impl IdmServerProxyWriteTransaction<'_> {
1617 pub(crate) fn crypto_policy(&self) -> &CryptoPolicy {
1618 self.crypto_policy
1619 }
1620
1621 pub fn get_origin(&self) -> &Url {
1622 #[allow(clippy::unwrap_used)]
1623 self.webauthn.get_allowed_origins().first().unwrap()
1624 }
1625
1626 fn check_password_quality(
1627 &mut self,
1628 cleartext: &str,
1629 related_inputs: &[&str],
1630 ) -> Result<(), OperationError> {
1631 if cleartext.len() < PW_MIN_LENGTH as usize {
1637 return Err(OperationError::PasswordQuality(vec![
1638 PasswordFeedback::TooShort(PW_MIN_LENGTH),
1639 ]));
1640 }
1641
1642 let entropy = zxcvbn(cleartext, related_inputs);
1645
1646 if entropy.score() < Score::Four {
1648 let feedback: zxcvbn::feedback::Feedback = entropy
1651 .feedback()
1652 .ok_or(OperationError::InvalidState)
1653 .cloned()
1654 .inspect_err(|err| {
1655 security_info!(?err, "zxcvbn returned no feedback when score < 3");
1656 })?;
1657
1658 security_info!(?feedback, "pw quality feedback");
1659
1660 return Err(OperationError::PasswordQuality(vec![
1663 PasswordFeedback::BadListed,
1664 ]));
1665 }
1666
1667 if self
1671 .qs_write
1672 .pw_badlist()
1673 .contains(&cleartext.to_lowercase())
1674 {
1675 security_info!("Password found in badlist, rejecting");
1676 Err(OperationError::PasswordQuality(vec![
1677 PasswordFeedback::BadListed,
1678 ]))
1679 } else {
1680 Ok(())
1681 }
1682 }
1683
1684 pub(crate) fn target_to_account(&mut self, target: Uuid) -> Result<Account, OperationError> {
1685 let account = self
1687 .qs_write
1688 .internal_search_uuid(target)
1689 .and_then(|account_entry| {
1690 Account::try_from_entry_rw(&account_entry, &mut self.qs_write)
1691 })
1692 .map_err(|e| {
1693 admin_error!("Failed to search account {:?}", e);
1694 e
1695 })?;
1696 if account.is_anonymous() {
1700 admin_warn!("Unable to convert anonymous to account during write txn");
1701 Err(OperationError::SystemProtectedObject)
1702 } else {
1703 Ok(account)
1704 }
1705 }
1706
1707 #[cfg(test)]
1708 pub(crate) fn set_account_password(
1709 &mut self,
1710 pce: &PasswordChangeEvent,
1711 ) -> Result<(), OperationError> {
1712 let account = self.target_to_account(pce.target)?;
1713
1714 let modlist = account
1716 .gen_password_mod(pce.cleartext.as_str(), self.crypto_policy)
1717 .map_err(|e| {
1718 admin_error!("Failed to generate password mod {:?}", e);
1719 e
1720 })?;
1721 trace!(?modlist, "processing change");
1722
1723 let me = self
1726 .qs_write
1727 .impersonate_modify_gen_event(
1728 &filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(pce.target))),
1730 &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(pce.target))),
1732 &modlist,
1733 &pce.ident,
1734 )
1735 .map_err(|e| {
1736 request_error!(error = ?e);
1737 e
1738 })?;
1739
1740 let mp = self
1741 .qs_write
1742 .modify_pre_apply(&me)
1743 .and_then(|opt_mp| opt_mp.ok_or(OperationError::NoMatchingEntries))
1744 .map_err(|e| {
1745 request_error!(error = ?e);
1746 e
1747 })?;
1748
1749 self.qs_write.modify_apply(mp).map_err(|e| {
1754 request_error!(error = ?e);
1755 e
1756 })?;
1757
1758 Ok(())
1759 }
1760
1761 pub fn set_unix_account_password(
1762 &mut self,
1763 pce: &UnixPasswordChangeEvent,
1764 ) -> Result<(), OperationError> {
1765 let account = self
1767 .qs_write
1768 .internal_search_uuid(pce.target)
1769 .and_then(|account_entry| {
1770 Account::try_from_entry_rw(&account_entry, &mut self.qs_write)
1772 })
1773 .map_err(|e| {
1774 admin_error!("Failed to start set unix account password {:?}", e);
1775 e
1776 })?;
1777
1778 if account.unix_extn().is_none() {
1780 return Err(OperationError::MissingClass(
1781 ENTRYCLASS_POSIX_ACCOUNT.into(),
1782 ));
1783 }
1784
1785 if account.is_anonymous() {
1787 trace!("Unable to use anonymous to change UNIX account password");
1788 return Err(OperationError::SystemProtectedObject);
1789 }
1790
1791 let modlist =
1792 gen_password_mod(pce.cleartext.as_str(), self.crypto_policy).map_err(|e| {
1793 admin_error!(?e, "Unable to generate password change modlist");
1794 e
1795 })?;
1796 trace!(?modlist, "processing change");
1797
1798 let me = self
1801 .qs_write
1802 .impersonate_modify_gen_event(
1803 &filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(pce.target))),
1805 &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(pce.target))),
1807 &modlist,
1808 &pce.ident,
1809 )
1810 .map_err(|e| {
1811 request_error!(error = ?e);
1812 e
1813 })?;
1814
1815 let mp = self
1816 .qs_write
1817 .modify_pre_apply(&me)
1818 .and_then(|opt_mp| opt_mp.ok_or(OperationError::NoMatchingEntries))
1819 .map_err(|e| {
1820 request_error!(error = ?e);
1821 e
1822 })?;
1823
1824 self.check_password_quality(pce.cleartext.as_str(), account.related_inputs().as_slice())
1828 .map_err(|e| {
1829 admin_error!(?e, "Failed to checked password quality");
1830 e
1831 })?;
1832
1833 self.qs_write.modify_apply(mp).map_err(|e| {
1835 request_error!(error = ?e);
1836 e
1837 })?;
1838
1839 Ok(())
1840 }
1841
1842 #[instrument(level = "debug", skip_all)]
1843 pub fn recover_account(
1844 &mut self,
1845 name: &str,
1846 cleartext: Option<&str>,
1847 ) -> Result<String, OperationError> {
1848 let target = self.qs_write.name_to_uuid(name).map_err(|e| {
1850 admin_error!(?e, "name to uuid failed");
1851 e
1852 })?;
1853
1854 let cleartext = cleartext
1855 .map(|s| s.to_string())
1856 .unwrap_or_else(password_from_random);
1857
1858 let ncred = Credential::new_generatedpassword_only(self.crypto_policy, &cleartext)
1859 .map_err(|e| {
1860 admin_error!("Unable to generate password mod {:?}", e);
1861 e
1862 })?;
1863 let vcred = Value::new_credential("primary", ncred);
1864 let modlist = ModifyList::new_list(vec![
1866 m_purge(Attribute::PassKeys),
1867 m_purge(Attribute::PrimaryCredential),
1868 Modify::Present(Attribute::PrimaryCredential, vcred),
1869 ]);
1870
1871 trace!(?modlist, "processing change");
1872
1873 self.qs_write
1874 .internal_modify(
1875 &filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(target))),
1877 &modlist,
1878 )
1879 .map_err(|e| {
1880 request_error!(error = ?e);
1881 e
1882 })?;
1883
1884 Ok(cleartext)
1885 }
1886
1887 #[instrument(level = "debug", skip_all)]
1888 pub fn regenerate_radius_secret(
1889 &mut self,
1890 rrse: &RegenerateRadiusSecretEvent,
1891 ) -> Result<String, OperationError> {
1892 let account = self.target_to_account(rrse.target)?;
1893
1894 let cleartext = readable_password_from_random();
1897
1898 let modlist = account
1900 .regenerate_radius_secret_mod(cleartext.as_str())
1901 .map_err(|e| {
1902 admin_error!("Unable to generate radius secret mod {:?}", e);
1903 e
1904 })?;
1905 trace!(?modlist, "processing change");
1906
1907 self.qs_write
1909 .impersonate_modify(
1910 &filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(rrse.target))),
1912 &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(rrse.target))),
1914 &modlist,
1915 &rrse.ident,
1917 )
1918 .map_err(|e| {
1919 request_error!(error = ?e);
1920 e
1921 })
1922 .map(|_| cleartext)
1923 }
1924
1925 #[instrument(level = "debug", skip_all)]
1927 fn process_pwupgrade(&mut self, pwu: &PasswordUpgrade) -> Result<(), OperationError> {
1928 let account = self.target_to_account(pwu.target_uuid)?;
1930
1931 info!(session_id = %pwu.target_uuid, "Processing password hash upgrade");
1932
1933 let maybe_modlist = account
1934 .gen_password_upgrade_mod(pwu.existing_password.as_str(), self.crypto_policy)
1935 .map_err(|e| {
1936 admin_error!("Unable to generate password mod {:?}", e);
1937 e
1938 })?;
1939
1940 if let Some(modlist) = maybe_modlist {
1941 self.qs_write.internal_modify(
1942 &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(pwu.target_uuid))),
1943 &modlist,
1944 )
1945 } else {
1946 Ok(())
1948 }
1949 }
1950
1951 #[instrument(level = "debug", skip_all)]
1952 fn process_unixpwupgrade(&mut self, pwu: &UnixPasswordUpgrade) -> Result<(), OperationError> {
1953 info!(session_id = %pwu.target_uuid, "Processing unix password hash upgrade");
1954
1955 let account = self
1956 .qs_write
1957 .internal_search_uuid(pwu.target_uuid)
1958 .and_then(|account_entry| {
1959 Account::try_from_entry_rw(&account_entry, &mut self.qs_write)
1960 })
1961 .map_err(|e| {
1962 admin_error!("Failed to start unix pw upgrade -> {:?}", e);
1963 e
1964 })?;
1965
1966 let cred = match account.unix_extn() {
1967 Some(ue) => ue.ucred(),
1968 None => {
1969 return Err(OperationError::MissingClass(
1970 ENTRYCLASS_POSIX_ACCOUNT.into(),
1971 ));
1972 }
1973 };
1974
1975 let Some(cred) = cred else {
1977 return Ok(());
1978 };
1979
1980 let maybe_modlist =
1981 gen_password_upgrade_mod(cred, pwu.existing_password.as_str(), self.crypto_policy)?;
1982
1983 match maybe_modlist {
1984 Some(modlist) => self.qs_write.internal_modify(
1985 &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(pwu.target_uuid))),
1986 &modlist,
1987 ),
1988 None => Ok(()),
1989 }
1990 }
1991
1992 #[instrument(level = "debug", skip_all)]
1993 pub(crate) fn process_webauthncounterinc(
1994 &mut self,
1995 wci: &WebauthnCounterIncrement,
1996 ) -> Result<(), OperationError> {
1997 info!(session_id = %wci.target_uuid, "Processing webauthn counter increment");
1998
1999 let mut account = self.target_to_account(wci.target_uuid)?;
2000
2001 let opt_modlist = account
2003 .gen_webauthn_counter_mod(&wci.auth_result)
2004 .map_err(|e| {
2005 admin_error!("Unable to generate webauthn counter mod {:?}", e);
2006 e
2007 })?;
2008
2009 if let Some(modlist) = opt_modlist {
2010 self.qs_write.internal_modify(
2011 &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(wci.target_uuid))),
2012 &modlist,
2013 )
2014 } else {
2015 trace!("No modification required");
2017 Ok(())
2018 }
2019 }
2020
2021 #[instrument(level = "debug", skip_all)]
2022 pub(crate) fn process_backupcoderemoval(
2023 &mut self,
2024 bcr: &BackupCodeRemoval,
2025 ) -> Result<(), OperationError> {
2026 info!(session_id = %bcr.target_uuid, "Processing backup code removal");
2027
2028 let account = self.target_to_account(bcr.target_uuid)?;
2029 let modlist = account
2031 .invalidate_backup_code_mod(&bcr.code_to_remove)
2032 .map_err(|e| {
2033 admin_error!("Unable to generate backup code mod {:?}", e);
2034 e
2035 })?;
2036
2037 self.qs_write.internal_modify(
2038 &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(bcr.target_uuid))),
2039 &modlist,
2040 )
2041 }
2042
2043 #[instrument(level = "debug", skip_all)]
2044 pub(crate) fn process_authsessionrecord(
2045 &mut self,
2046 asr: &AuthSessionRecord,
2047 ) -> Result<(), OperationError> {
2048 let state = match asr.expiry {
2050 Some(e) => SessionState::ExpiresAt(e),
2051 None => SessionState::NeverExpires,
2052 };
2053
2054 let session = Value::Session(
2055 asr.session_id,
2056 Session {
2057 label: asr.label.clone(),
2058 state,
2059 issued_at: asr.issued_at,
2062 issued_by: asr.issued_by.clone(),
2064 cred_id: asr.cred_id,
2066 scope: asr.scope,
2069 type_: asr.type_,
2070 },
2071 );
2072
2073 info!(session_id = %asr.session_id, "Persisting auth session");
2074
2075 let modlist = ModifyList::new_append(Attribute::UserAuthTokenSession, session);
2077
2078 self.qs_write
2079 .internal_modify(
2080 &filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(asr.target_uuid))),
2081 &modlist,
2082 )
2083 .map_err(|e| {
2084 admin_error!("Failed to persist user auth token {:?}", e);
2085 e
2086 })
2087 }
2089
2090 #[instrument(level = "debug", skip_all)]
2091 pub fn process_delayedaction(
2092 &mut self,
2093 da: &DelayedAction,
2094 _ct: Duration,
2095 ) -> Result<(), OperationError> {
2096 match da {
2097 DelayedAction::PwUpgrade(pwu) => self.process_pwupgrade(pwu),
2098 DelayedAction::UnixPwUpgrade(upwu) => self.process_unixpwupgrade(upwu),
2099 DelayedAction::WebauthnCounterIncrement(wci) => self.process_webauthncounterinc(wci),
2100 DelayedAction::BackupCodeRemoval(bcr) => self.process_backupcoderemoval(bcr),
2101 DelayedAction::AuthSessionRecord(asr) => self.process_authsessionrecord(asr),
2102 }
2103 }
2104
2105 fn reload_applications(&mut self) -> Result<(), OperationError> {
2106 self.qs_write
2107 .get_applications_set()
2108 .and_then(|application_set| self.applications.reload(application_set))
2109 }
2110
2111 fn reload_oauth2(&mut self) -> Result<(), OperationError> {
2112 let domain_level = self.qs_write.get_domain_version();
2113 self.qs_write.get_oauth2rs_set().and_then(|oauth2rs_set| {
2114 let key_providers = self.qs_write.get_key_providers();
2115 self.oauth2rs
2116 .reload(oauth2rs_set, key_providers, domain_level)
2117 })?;
2118 self.qs_write.clear_changed_oauth2();
2120 Ok(())
2121 }
2122
2123 #[instrument(level = "debug", skip_all)]
2124 pub fn commit(mut self) -> Result<(), OperationError> {
2125 self.qs_write.reload()?;
2128
2129 if self.qs_write.get_changed_app() {
2131 self.reload_applications()?;
2132 }
2133
2134 if self.qs_write.get_changed_oauth2() {
2135 self.reload_oauth2()?;
2136 }
2137
2138 self.applications.commit();
2140 self.oauth2rs.commit();
2141 self.cred_update_sessions.commit();
2142
2143 trace!("cred_update_session.commit");
2144 self.qs_write.commit()
2145 }
2146
2147 #[instrument(level = "debug", skip_all)]
2148 pub fn generate_application_password(
2149 &mut self,
2150 ev: &GenerateApplicationPasswordEvent,
2151 ) -> Result<String, OperationError> {
2152 let account = self.target_to_account(ev.target)?;
2153
2154 let cleartext = readable_password_from_random();
2156
2157 let modlist = account
2159 .generate_application_password_mod(
2160 ev.application,
2161 ev.label.as_str(),
2162 cleartext.as_str(),
2163 self.crypto_policy,
2164 )
2165 .map_err(|e| {
2166 admin_error!("Unable to generate application password mod {:?}", e);
2167 e
2168 })?;
2169 trace!(?modlist, "processing change");
2170 self.qs_write
2172 .impersonate_modify(
2173 &filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(ev.target))),
2175 &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(ev.target))),
2177 &modlist,
2178 &ev.ident,
2180 )
2181 .map_err(|e| {
2182 error!(error = ?e);
2183 e
2184 })
2185 .map(|_| cleartext)
2186 }
2187}
2188
2189#[cfg(test)]
2192mod tests {
2193 use std::convert::TryFrom;
2194 use std::time::Duration;
2195
2196 use kanidm_proto::v1::{AuthAllowed, AuthIssueSession, AuthMech};
2197 use time::OffsetDateTime;
2198 use uuid::Uuid;
2199
2200 use crate::credential::{Credential, Password};
2201 use crate::idm::account::DestroySessionTokenEvent;
2202 use crate::idm::accountpolicy::ResolvedAccountPolicy;
2203 use crate::idm::audit::AuditEvent;
2204 use crate::idm::delayed::{AuthSessionRecord, DelayedAction};
2205 use crate::idm::event::{AuthEvent, AuthResult};
2206 use crate::idm::event::{
2207 LdapAuthEvent, PasswordChangeEvent, RadiusAuthTokenEvent, RegenerateRadiusSecretEvent,
2208 UnixGroupTokenEvent, UnixPasswordChangeEvent, UnixUserAuthEvent, UnixUserTokenEvent,
2209 };
2210
2211 use crate::idm::server::{IdmServer, IdmServerTransaction, Token};
2212 use crate::idm::AuthState;
2213 use crate::modify::{Modify, ModifyList};
2214 use crate::prelude::*;
2215 use crate::server::keys::KeyProvidersTransaction;
2216 use crate::value::{AuthType, SessionState};
2217 use compact_jwt::{traits::JwsVerifiable, JwsCompact, JwsEs256Verifier, JwsVerifier};
2218 use kanidm_lib_crypto::CryptoPolicy;
2219
2220 const TEST_PASSWORD: &str = "ntaoeuntnaoeuhraohuercahu😍";
2221 const TEST_PASSWORD_INC: &str = "ntaoentu nkrcgaeunhibwmwmqj;k wqjbkx ";
2222 const TEST_CURRENT_TIME: u64 = 6000;
2223
2224 #[idm_test]
2225 async fn test_idm_anonymous_auth(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2226 let mut idms_auth = idms.auth().await.unwrap();
2228 let anon_init = AuthEvent::anonymous_init();
2230 let r1 = idms_auth
2232 .auth(
2233 &anon_init,
2234 Duration::from_secs(TEST_CURRENT_TIME),
2235 Source::Internal.into(),
2236 )
2237 .await;
2238 let sid = match r1 {
2241 Ok(ar) => {
2242 let AuthResult { sessionid, state } = ar;
2243 match state {
2244 AuthState::Choose(mut conts) => {
2245 assert_eq!(conts.len(), 1);
2247 let m = conts.pop().expect("Should not fail");
2249 assert_eq!(m, AuthMech::Anonymous);
2250 }
2251 _ => {
2252 error!("A critical error has occurred! We have a non-continue result!");
2253 panic!();
2254 }
2255 };
2256 sessionid
2258 }
2259 Err(e) => {
2260 error!("A critical error has occurred! {:?}", e);
2262 panic!();
2263 }
2264 };
2265
2266 debug!("sessionid is ==> {:?}", sid);
2267
2268 idms_auth.commit().expect("Must not fail");
2269
2270 let mut idms_auth = idms.auth().await.unwrap();
2271 let anon_begin = AuthEvent::begin_mech(sid, AuthMech::Anonymous);
2272
2273 let r2 = idms_auth
2274 .auth(
2275 &anon_begin,
2276 Duration::from_secs(TEST_CURRENT_TIME),
2277 Source::Internal.into(),
2278 )
2279 .await;
2280 debug!("r2 ==> {:?}", r2);
2281
2282 match r2 {
2283 Ok(ar) => {
2284 let AuthResult {
2285 sessionid: _,
2286 state,
2287 } = ar;
2288
2289 match state {
2290 AuthState::Continue(allowed) => {
2291 assert_eq!(allowed.len(), 1);
2293 assert_eq!(allowed.first(), Some(&AuthAllowed::Anonymous));
2294 }
2295 _ => {
2296 error!("A critical error has occurred! We have a non-continue result!");
2297 panic!();
2298 }
2299 }
2300 }
2301 Err(e) => {
2302 error!("A critical error has occurred! {:?}", e);
2303 panic!();
2305 }
2306 };
2307
2308 idms_auth.commit().expect("Must not fail");
2309
2310 let mut idms_auth = idms.auth().await.unwrap();
2311 let anon_step = AuthEvent::cred_step_anonymous(sid);
2313
2314 let r2 = idms_auth
2316 .auth(
2317 &anon_step,
2318 Duration::from_secs(TEST_CURRENT_TIME),
2319 Source::Internal.into(),
2320 )
2321 .await;
2322 debug!("r2 ==> {:?}", r2);
2323
2324 match r2 {
2325 Ok(ar) => {
2326 let AuthResult {
2327 sessionid: _,
2328 state,
2329 } = ar;
2330
2331 match state {
2332 AuthState::Success(_uat, AuthIssueSession::Token) => {
2333 }
2335 _ => {
2336 error!("A critical error has occurred! We have a non-success result!");
2337 panic!();
2338 }
2339 }
2340 }
2341 Err(e) => {
2342 error!("A critical error has occurred! {:?}", e);
2343 panic!();
2345 }
2346 };
2347
2348 idms_auth.commit().expect("Must not fail");
2349 }
2350
2351 #[idm_test]
2353 async fn test_idm_anonymous_auth_invalid_states(
2354 idms: &IdmServer,
2355 _idms_delayed: &IdmServerDelayed,
2356 ) {
2357 {
2358 let mut idms_auth = idms.auth().await.unwrap();
2359 let sid = Uuid::new_v4();
2360 let anon_step = AuthEvent::cred_step_anonymous(sid);
2361
2362 let r2 = idms_auth
2364 .auth(
2365 &anon_step,
2366 Duration::from_secs(TEST_CURRENT_TIME),
2367 Source::Internal.into(),
2368 )
2369 .await;
2370 debug!("r2 ==> {:?}", r2);
2371
2372 match r2 {
2373 Ok(_) => {
2374 error!("Auth state machine not correctly enforced!");
2375 panic!();
2376 }
2377 Err(e) => match e {
2378 OperationError::InvalidSessionState => {}
2379 _ => panic!(),
2380 },
2381 };
2382 }
2383 }
2384
2385 async fn init_testperson_w_password(
2386 idms: &IdmServer,
2387 pw: &str,
2388 ) -> Result<Uuid, OperationError> {
2389 let p = CryptoPolicy::minimum();
2390 let cred = Credential::new_password_only(&p, pw)?;
2391 let cred_id = cred.uuid;
2392 let v_cred = Value::new_credential("primary", cred);
2393 let mut idms_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2394
2395 idms_write
2396 .qs_write
2397 .internal_create(vec![E_TESTPERSON_1.clone()])
2398 .expect("Failed to create test person");
2399
2400 let me_inv_m = ModifyEvent::new_internal_invalid(
2402 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_TESTPERSON_1))),
2403 ModifyList::new_list(vec![Modify::Present(Attribute::PrimaryCredential, v_cred)]),
2404 );
2405 assert!(idms_write.qs_write.modify(&me_inv_m).is_ok());
2407
2408 idms_write.commit().map(|()| cred_id)
2409 }
2410
2411 async fn init_authsession_sid(idms: &IdmServer, ct: Duration, name: &str) -> Uuid {
2412 let mut idms_auth = idms.auth().await.unwrap();
2413 let admin_init = AuthEvent::named_init(name);
2414
2415 let r1 = idms_auth
2416 .auth(&admin_init, ct, Source::Internal.into())
2417 .await;
2418 let ar = r1.unwrap();
2419 let AuthResult { sessionid, state } = ar;
2420
2421 assert!(matches!(state, AuthState::Choose(_)));
2422
2423 let admin_begin = AuthEvent::begin_mech(sessionid, AuthMech::Password);
2425
2426 let r2 = idms_auth
2427 .auth(&admin_begin, ct, Source::Internal.into())
2428 .await;
2429 let ar = r2.unwrap();
2430 let AuthResult { sessionid, state } = ar;
2431
2432 match state {
2433 AuthState::Continue(_) => {}
2434 s => {
2435 error!(?s, "Sessions was not initialised");
2436 panic!();
2437 }
2438 };
2439
2440 idms_auth.commit().expect("Must not fail");
2441
2442 sessionid
2443 }
2444
2445 async fn check_testperson_password(idms: &IdmServer, pw: &str, ct: Duration) -> JwsCompact {
2446 let sid = init_authsession_sid(idms, ct, "testperson1").await;
2447
2448 let mut idms_auth = idms.auth().await.unwrap();
2449 let anon_step = AuthEvent::cred_step_password(sid, pw);
2450
2451 let r2 = idms_auth
2453 .auth(&anon_step, ct, Source::Internal.into())
2454 .await;
2455 debug!("r2 ==> {:?}", r2);
2456
2457 let token = match r2 {
2458 Ok(ar) => {
2459 let AuthResult {
2460 sessionid: _,
2461 state,
2462 } = ar;
2463
2464 match state {
2465 AuthState::Success(token, AuthIssueSession::Token) => {
2466 token
2468 }
2469 _ => {
2470 error!("A critical error has occurred! We have a non-success result!");
2471 panic!();
2472 }
2473 }
2474 }
2475 Err(e) => {
2476 error!("A critical error has occurred! {:?}", e);
2477 panic!();
2479 }
2480 };
2481
2482 idms_auth.commit().expect("Must not fail");
2483
2484 *token
2485 }
2486
2487 #[idm_test]
2488 async fn test_idm_simple_password_auth(idms: &IdmServer, idms_delayed: &mut IdmServerDelayed) {
2489 let ct = duration_from_epoch_now();
2490 init_testperson_w_password(idms, TEST_PASSWORD)
2491 .await
2492 .expect("Failed to setup admin account");
2493 check_testperson_password(idms, TEST_PASSWORD, ct).await;
2494
2495 let da = idms_delayed.try_recv().expect("invalid");
2497 assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
2498 idms_delayed.check_is_empty_or_panic();
2499 }
2500
2501 #[idm_test]
2502 async fn test_idm_simple_password_spn_auth(
2503 idms: &IdmServer,
2504 idms_delayed: &mut IdmServerDelayed,
2505 ) {
2506 init_testperson_w_password(idms, TEST_PASSWORD)
2507 .await
2508 .expect("Failed to setup admin account");
2509
2510 let sid = init_authsession_sid(
2511 idms,
2512 Duration::from_secs(TEST_CURRENT_TIME),
2513 "testperson1@example.com",
2514 )
2515 .await;
2516
2517 let mut idms_auth = idms.auth().await.unwrap();
2518 let anon_step = AuthEvent::cred_step_password(sid, TEST_PASSWORD);
2519
2520 let r2 = idms_auth
2522 .auth(
2523 &anon_step,
2524 Duration::from_secs(TEST_CURRENT_TIME),
2525 Source::Internal.into(),
2526 )
2527 .await;
2528 debug!("r2 ==> {:?}", r2);
2529
2530 match r2 {
2531 Ok(ar) => {
2532 let AuthResult {
2533 sessionid: _,
2534 state,
2535 } = ar;
2536 match state {
2537 AuthState::Success(_uat, AuthIssueSession::Token) => {
2538 }
2540 _ => {
2541 error!("A critical error has occurred! We have a non-success result!");
2542 panic!();
2543 }
2544 }
2545 }
2546 Err(e) => {
2547 error!("A critical error has occurred! {:?}", e);
2548 panic!();
2550 }
2551 };
2552
2553 let da = idms_delayed.try_recv().expect("invalid");
2555 assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
2556 idms_delayed.check_is_empty_or_panic();
2557
2558 idms_auth.commit().expect("Must not fail");
2559 }
2560
2561 #[idm_test(audit = 1)]
2562 async fn test_idm_simple_password_invalid(
2563 idms: &IdmServer,
2564 _idms_delayed: &IdmServerDelayed,
2565 idms_audit: &mut IdmServerAudit,
2566 ) {
2567 init_testperson_w_password(idms, TEST_PASSWORD)
2568 .await
2569 .expect("Failed to setup admin account");
2570 let sid =
2571 init_authsession_sid(idms, Duration::from_secs(TEST_CURRENT_TIME), "testperson1").await;
2572 let mut idms_auth = idms.auth().await.unwrap();
2573 let anon_step = AuthEvent::cred_step_password(sid, TEST_PASSWORD_INC);
2574
2575 let r2 = idms_auth
2577 .auth(
2578 &anon_step,
2579 Duration::from_secs(TEST_CURRENT_TIME),
2580 Source::Internal.into(),
2581 )
2582 .await;
2583 debug!("r2 ==> {:?}", r2);
2584
2585 match r2 {
2586 Ok(ar) => {
2587 let AuthResult {
2588 sessionid: _,
2589 state,
2590 } = ar;
2591 match state {
2592 AuthState::Denied(_reason) => {
2593 }
2595 _ => {
2596 error!("A critical error has occurred! We have a non-denied result!");
2597 panic!();
2598 }
2599 }
2600 }
2601 Err(e) => {
2602 error!("A critical error has occurred! {:?}", e);
2603 panic!();
2605 }
2606 };
2607
2608 match idms_audit.audit_rx().try_recv() {
2610 Ok(AuditEvent::AuthenticationDenied { .. }) => {}
2611 _ => panic!("Oh no"),
2612 }
2613
2614 idms_auth.commit().expect("Must not fail");
2615 }
2616
2617 #[idm_test]
2618 async fn test_idm_simple_password_reset(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2619 let pce = PasswordChangeEvent::new_internal(UUID_ADMIN, TEST_PASSWORD);
2620
2621 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2622 assert!(idms_prox_write.set_account_password(&pce).is_ok());
2623 assert!(idms_prox_write.set_account_password(&pce).is_ok());
2624 assert!(idms_prox_write.commit().is_ok());
2625 }
2626
2627 #[idm_test]
2628 async fn test_idm_anonymous_set_password_denied(
2629 idms: &IdmServer,
2630 _idms_delayed: &IdmServerDelayed,
2631 ) {
2632 let pce = PasswordChangeEvent::new_internal(UUID_ANONYMOUS, TEST_PASSWORD);
2633
2634 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2635 assert!(idms_prox_write.set_account_password(&pce).is_err());
2636 assert!(idms_prox_write.commit().is_ok());
2637 }
2638
2639 #[idm_test]
2640 async fn test_idm_regenerate_radius_secret(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2641 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2642
2643 idms_prox_write
2644 .qs_write
2645 .internal_create(vec![E_TESTPERSON_1.clone()])
2646 .expect("unable to create test person");
2647
2648 let rrse = RegenerateRadiusSecretEvent::new_internal(UUID_TESTPERSON_1);
2649
2650 let r1 = idms_prox_write
2652 .regenerate_radius_secret(&rrse)
2653 .expect("Failed to reset radius credential 1");
2654 let r2 = idms_prox_write
2656 .regenerate_radius_secret(&rrse)
2657 .expect("Failed to reset radius credential 2");
2658 assert!(r1 != r2);
2659 }
2660
2661 #[idm_test]
2662 async fn test_idm_radiusauthtoken(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2663 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2664
2665 idms_prox_write
2666 .qs_write
2667 .internal_create(vec![E_TESTPERSON_1.clone()])
2668 .expect("unable to create test person");
2669
2670 let rrse = RegenerateRadiusSecretEvent::new_internal(UUID_TESTPERSON_1);
2671 let r1 = idms_prox_write
2672 .regenerate_radius_secret(&rrse)
2673 .expect("Failed to reset radius credential 1");
2674 idms_prox_write.commit().expect("failed to commit");
2675
2676 let mut idms_prox_read = idms.proxy_read().await.unwrap();
2677 let person_entry = idms_prox_read
2678 .qs_read
2679 .internal_search_uuid(UUID_TESTPERSON_1)
2680 .expect("Can't access admin entry.");
2681
2682 let rate = RadiusAuthTokenEvent::new_impersonate(person_entry, UUID_TESTPERSON_1);
2683 let tok_r = idms_prox_read
2684 .get_radiusauthtoken(&rate, duration_from_epoch_now())
2685 .expect("Failed to generate radius auth token");
2686
2687 assert_eq!(r1, tok_r.secret);
2689 }
2690
2691 #[idm_test]
2692 async fn test_idm_unixusertoken(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2693 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2694 let me_posix = ModifyEvent::new_internal_invalid(
2696 filter!(f_eq(Attribute::Name, PartialValue::new_iname("admin"))),
2697 ModifyList::new_list(vec![
2698 Modify::Present(Attribute::Class, EntryClass::PosixAccount.into()),
2699 Modify::Present(Attribute::GidNumber, Value::new_uint32(2001)),
2700 ]),
2701 );
2702 assert!(idms_prox_write.qs_write.modify(&me_posix).is_ok());
2703 let e: Entry<EntryInit, EntryNew> = entry_init!(
2705 (Attribute::Class, EntryClass::Object.to_value()),
2706 (Attribute::Class, EntryClass::Group.to_value()),
2707 (Attribute::Class, EntryClass::PosixGroup.to_value()),
2708 (Attribute::Name, Value::new_iname("testgroup")),
2709 (
2710 Attribute::Uuid,
2711 Value::Uuid(uuid::uuid!("01609135-a1c4-43d5-966b-a28227644445"))
2712 ),
2713 (Attribute::Description, Value::new_utf8s("testgroup")),
2714 (
2715 Attribute::Member,
2716 Value::Refer(uuid::uuid!("00000000-0000-0000-0000-000000000000"))
2717 )
2718 );
2719
2720 let ce = CreateEvent::new_internal(vec![e]);
2721
2722 assert!(idms_prox_write.qs_write.create(&ce).is_ok());
2723
2724 idms_prox_write.commit().expect("failed to commit");
2725
2726 let mut idms_prox_read = idms.proxy_read().await.unwrap();
2727
2728 let admin_entry = idms_prox_read
2730 .qs_read
2731 .internal_search_uuid(UUID_ADMIN)
2732 .expect("Can't access admin entry.");
2733
2734 let ugte = UnixGroupTokenEvent::new_impersonate(
2735 admin_entry.clone(),
2736 uuid!("01609135-a1c4-43d5-966b-a28227644445"),
2737 );
2738 let tok_g = idms_prox_read
2739 .get_unixgrouptoken(&ugte)
2740 .expect("Failed to generate unix group token");
2741
2742 assert_eq!(tok_g.name, "testgroup");
2743 assert_eq!(tok_g.spn, "testgroup@example.com");
2744
2745 let uute = UnixUserTokenEvent::new_internal(UUID_ADMIN);
2746 let tok_r = idms_prox_read
2747 .get_unixusertoken(&uute, duration_from_epoch_now())
2748 .expect("Failed to generate unix user token");
2749
2750 assert_eq!(tok_r.name, "admin");
2751 assert_eq!(tok_r.spn, "admin@example.com");
2752 assert_eq!(tok_r.groups.len(), 2);
2753 assert_eq!(tok_r.groups[0].name, "admin");
2754 assert_eq!(tok_r.groups[1].name, "testgroup");
2755 assert!(tok_r.valid);
2756
2757 let ugte = UnixGroupTokenEvent::new_impersonate(
2759 admin_entry,
2760 uuid!("00000000-0000-0000-0000-000000000000"),
2761 );
2762 let tok_g = idms_prox_read
2763 .get_unixgrouptoken(&ugte)
2764 .expect("Failed to generate unix group token");
2765
2766 assert_eq!(tok_g.name, "admin");
2767 assert_eq!(tok_g.spn, "admin@example.com");
2768 }
2769
2770 #[idm_test]
2771 async fn test_idm_simple_unix_password_reset(
2772 idms: &IdmServer,
2773 _idms_delayed: &IdmServerDelayed,
2774 ) {
2775 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2776 let me_posix = ModifyEvent::new_internal_invalid(
2778 filter!(f_eq(Attribute::Name, PartialValue::new_iname("admin"))),
2779 ModifyList::new_list(vec![
2780 Modify::Present(Attribute::Class, EntryClass::PosixAccount.into()),
2781 Modify::Present(Attribute::GidNumber, Value::new_uint32(2001)),
2782 ]),
2783 );
2784 assert!(idms_prox_write.qs_write.modify(&me_posix).is_ok());
2785
2786 let pce = UnixPasswordChangeEvent::new_internal(UUID_ADMIN, TEST_PASSWORD);
2787
2788 assert!(idms_prox_write.set_unix_account_password(&pce).is_ok());
2789 assert!(idms_prox_write.commit().is_ok());
2790
2791 let mut idms_auth = idms.auth().await.unwrap();
2792 let uuae_good = UnixUserAuthEvent::new_internal(UUID_ADMIN, TEST_PASSWORD);
2795 let a1 = idms_auth
2796 .auth_unix(&uuae_good, Duration::from_secs(TEST_CURRENT_TIME))
2797 .await;
2798 match a1 {
2799 Ok(Some(_tok)) => {}
2800 _ => panic!("Oh no"),
2801 };
2802 let uuae_bad = UnixUserAuthEvent::new_internal(UUID_ADMIN, TEST_PASSWORD_INC);
2804 let a2 = idms_auth
2805 .auth_unix(&uuae_bad, Duration::from_secs(TEST_CURRENT_TIME))
2806 .await;
2807 match a2 {
2808 Ok(None) => {}
2809 _ => panic!("Oh no"),
2810 };
2811 assert!(idms_auth.commit().is_ok());
2812
2813 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2815 let me_purge_up = ModifyEvent::new_internal_invalid(
2816 filter!(f_eq(Attribute::Name, PartialValue::new_iname("admin"))),
2817 ModifyList::new_list(vec![Modify::Purged(Attribute::UnixPassword)]),
2818 );
2819 assert!(idms_prox_write.qs_write.modify(&me_purge_up).is_ok());
2820 assert!(idms_prox_write.commit().is_ok());
2821
2822 let mut idms_auth = idms.auth().await.unwrap();
2825 let a3 = idms_auth
2826 .auth_unix(&uuae_good, Duration::from_secs(TEST_CURRENT_TIME))
2827 .await;
2828 match a3 {
2829 Ok(None) => {}
2830 _ => panic!("Oh no"),
2831 };
2832 assert!(idms_auth.commit().is_ok());
2833 }
2834
2835 #[idm_test]
2836 async fn test_idm_simple_password_upgrade(
2837 idms: &IdmServer,
2838 idms_delayed: &mut IdmServerDelayed,
2839 ) {
2840 let ct = duration_from_epoch_now();
2841 idms_delayed.check_is_empty_or_panic();
2843 {
2845 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
2846 idms_prox_write
2849 .qs_write
2850 .internal_create(vec![E_TESTPERSON_1.clone()])
2851 .expect("Failed to create test person");
2852
2853 let me_inv_m =
2854 ModifyEvent::new_internal_invalid(
2855 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_TESTPERSON_1))),
2856 ModifyList::new_list(vec![Modify::Present(
2857 Attribute::PasswordImport,
2858 Value::from("{SSHA512}JwrSUHkI7FTAfHRVR6KoFlSN0E3dmaQWARjZ+/UsShYlENOqDtFVU77HJLLrY2MuSp0jve52+pwtdVl2QUAHukQ0XUf5LDtM")
2859 )]),
2860 );
2861 assert!(idms_prox_write.qs_write.modify(&me_inv_m).is_ok());
2863 assert!(idms_prox_write.commit().is_ok());
2864 }
2865 idms_delayed.check_is_empty_or_panic();
2867
2868 let mut idms_prox_read = idms.proxy_read().await.unwrap();
2869 let person_entry = idms_prox_read
2870 .qs_read
2871 .internal_search_uuid(UUID_TESTPERSON_1)
2872 .expect("Can't access admin entry.");
2873 let cred_before = person_entry
2874 .get_ava_single_credential(Attribute::PrimaryCredential)
2875 .expect("No credential present")
2876 .clone();
2877 drop(idms_prox_read);
2878
2879 check_testperson_password(idms, "password", ct).await;
2881
2882 let da = idms_delayed.try_recv().expect("invalid");
2891 assert!(matches!(da, DelayedAction::PwUpgrade(_)));
2893 let r = idms.delayed_action(duration_from_epoch_now(), da).await;
2894 let da = idms_delayed.try_recv().expect("invalid");
2896 assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
2897 assert_eq!(Ok(true), r);
2898
2899 let mut idms_prox_read = idms.proxy_read().await.unwrap();
2900 let person_entry = idms_prox_read
2901 .qs_read
2902 .internal_search_uuid(UUID_TESTPERSON_1)
2903 .expect("Can't access admin entry.");
2904 let cred_after = person_entry
2905 .get_ava_single_credential(Attribute::PrimaryCredential)
2906 .expect("No credential present")
2907 .clone();
2908 drop(idms_prox_read);
2909
2910 assert_eq!(cred_before.uuid, cred_after.uuid);
2911
2912 check_testperson_password(idms, "password", ct).await;
2914 let da = idms_delayed.try_recv().expect("invalid");
2916 assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
2917
2918 idms_delayed.check_is_empty_or_panic();
2920 }
2921
2922 #[idm_test]
2923 async fn test_idm_unix_password_upgrade(idms: &IdmServer, idms_delayed: &mut IdmServerDelayed) {
2924 idms_delayed.check_is_empty_or_panic();
2926 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2928
2929 let im_pw = "{SSHA512}JwrSUHkI7FTAfHRVR6KoFlSN0E3dmaQWARjZ+/UsShYlENOqDtFVU77HJLLrY2MuSp0jve52+pwtdVl2QUAHukQ0XUf5LDtM";
2930 let pw = Password::try_from(im_pw).expect("failed to parse");
2931 let cred = Credential::new_from_password(pw);
2932 let v_cred = Value::new_credential("unix", cred);
2933
2934 let me_posix = ModifyEvent::new_internal_invalid(
2935 filter!(f_eq(Attribute::Name, PartialValue::new_iname("admin"))),
2936 ModifyList::new_list(vec![
2937 Modify::Present(Attribute::Class, EntryClass::PosixAccount.into()),
2938 Modify::Present(Attribute::GidNumber, Value::new_uint32(2001)),
2939 Modify::Present(Attribute::UnixPassword, v_cred),
2940 ]),
2941 );
2942 assert!(idms_prox_write.qs_write.modify(&me_posix).is_ok());
2943 assert!(idms_prox_write.commit().is_ok());
2944 idms_delayed.check_is_empty_or_panic();
2945 let uuae = UnixUserAuthEvent::new_internal(UUID_ADMIN, "password");
2947 let mut idms_auth = idms.auth().await.unwrap();
2948 let a1 = idms_auth
2949 .auth_unix(&uuae, Duration::from_secs(TEST_CURRENT_TIME))
2950 .await;
2951 match a1 {
2952 Ok(Some(_tok)) => {}
2953 _ => panic!("Oh no"),
2954 };
2955 idms_auth.commit().expect("Must not fail");
2956 let da = idms_delayed.try_recv().expect("invalid");
2959 let _r = idms.delayed_action(duration_from_epoch_now(), da).await;
2960 let mut idms_auth = idms.auth().await.unwrap();
2962 let a2 = idms_auth
2963 .auth_unix(&uuae, Duration::from_secs(TEST_CURRENT_TIME))
2964 .await;
2965 match a2 {
2966 Ok(Some(_tok)) => {}
2967 _ => panic!("Oh no"),
2968 };
2969 idms_auth.commit().expect("Must not fail");
2970 idms_delayed.check_is_empty_or_panic();
2972 }
2973
2974 const TEST_NOT_YET_VALID_TIME: u64 = TEST_CURRENT_TIME - 240;
2978 const TEST_VALID_FROM_TIME: u64 = TEST_CURRENT_TIME - 120;
2979 const TEST_EXPIRE_TIME: u64 = TEST_CURRENT_TIME + 120;
2980 const TEST_AFTER_EXPIRY: u64 = TEST_CURRENT_TIME + 240;
2981
2982 async fn set_testperson_valid_time(idms: &IdmServer) {
2983 let mut idms_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2984
2985 let v_valid_from = Value::new_datetime_epoch(Duration::from_secs(TEST_VALID_FROM_TIME));
2986 let v_expire = Value::new_datetime_epoch(Duration::from_secs(TEST_EXPIRE_TIME));
2987
2988 let me_inv_m = ModifyEvent::new_internal_invalid(
2990 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_TESTPERSON_1))),
2991 ModifyList::new_list(vec![
2992 Modify::Present(Attribute::AccountExpire, v_expire),
2993 Modify::Present(Attribute::AccountValidFrom, v_valid_from),
2994 ]),
2995 );
2996 assert!(idms_write.qs_write.modify(&me_inv_m).is_ok());
2998
2999 idms_write.commit().expect("Must not fail");
3000 }
3001
3002 #[idm_test]
3003 async fn test_idm_account_valid_from_expire(
3004 idms: &IdmServer,
3005 _idms_delayed: &mut IdmServerDelayed,
3006 ) {
3007 init_testperson_w_password(idms, TEST_PASSWORD)
3010 .await
3011 .expect("Failed to setup admin account");
3012 set_testperson_valid_time(idms).await;
3015
3016 let time_low = Duration::from_secs(TEST_NOT_YET_VALID_TIME);
3017 let time_high = Duration::from_secs(TEST_AFTER_EXPIRY);
3018
3019 let mut idms_auth = idms.auth().await.unwrap();
3020 let admin_init = AuthEvent::named_init("admin");
3021 let r1 = idms_auth
3022 .auth(&admin_init, time_low, Source::Internal.into())
3023 .await;
3024
3025 let ar = r1.unwrap();
3026 let AuthResult {
3027 sessionid: _,
3028 state,
3029 } = ar;
3030
3031 match state {
3032 AuthState::Denied(_) => {}
3033 _ => {
3034 panic!();
3035 }
3036 };
3037
3038 idms_auth.commit().expect("Must not fail");
3039
3040 let mut idms_auth = idms.auth().await.unwrap();
3042 let admin_init = AuthEvent::named_init("admin");
3043 let r1 = idms_auth
3044 .auth(&admin_init, time_high, Source::Internal.into())
3045 .await;
3046
3047 let ar = r1.unwrap();
3048 let AuthResult {
3049 sessionid: _,
3050 state,
3051 } = ar;
3052
3053 match state {
3054 AuthState::Denied(_) => {}
3055 _ => {
3056 panic!();
3057 }
3058 };
3059
3060 idms_auth.commit().expect("Must not fail");
3061 }
3062
3063 #[idm_test]
3064 async fn test_idm_unix_valid_from_expire(
3065 idms: &IdmServer,
3066 _idms_delayed: &mut IdmServerDelayed,
3067 ) {
3068 init_testperson_w_password(idms, TEST_PASSWORD)
3070 .await
3071 .expect("Failed to setup admin account");
3072 set_testperson_valid_time(idms).await;
3073
3074 let time_low = Duration::from_secs(TEST_NOT_YET_VALID_TIME);
3075 let time_high = Duration::from_secs(TEST_AFTER_EXPIRY);
3076
3077 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
3079 let me_posix = ModifyEvent::new_internal_invalid(
3080 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_TESTPERSON_1))),
3081 ModifyList::new_list(vec![
3082 Modify::Present(Attribute::Class, EntryClass::PosixAccount.into()),
3083 Modify::Present(Attribute::GidNumber, Value::new_uint32(2001)),
3084 ]),
3085 );
3086 assert!(idms_prox_write.qs_write.modify(&me_posix).is_ok());
3087
3088 let pce = UnixPasswordChangeEvent::new_internal(UUID_TESTPERSON_1, TEST_PASSWORD);
3089
3090 assert!(idms_prox_write.set_unix_account_password(&pce).is_ok());
3091 assert!(idms_prox_write.commit().is_ok());
3092
3093 let mut idms_auth = idms.auth().await.unwrap();
3095 let uuae_good = UnixUserAuthEvent::new_internal(UUID_TESTPERSON_1, TEST_PASSWORD);
3096
3097 let a1 = idms_auth.auth_unix(&uuae_good, time_low).await;
3098 match a1 {
3101 Ok(None) => {}
3102 _ => panic!("Oh no"),
3103 };
3104
3105 let a2 = idms_auth.auth_unix(&uuae_good, time_high).await;
3106 match a2 {
3107 Ok(None) => {}
3108 _ => panic!("Oh no"),
3109 };
3110
3111 idms_auth.commit().expect("Must not fail");
3112 let mut idms_prox_read = idms.proxy_read().await.unwrap();
3114 let uute = UnixUserTokenEvent::new_internal(UUID_TESTPERSON_1);
3115
3116 let tok_r = idms_prox_read
3117 .get_unixusertoken(&uute, time_low)
3118 .expect("Failed to generate unix user token");
3119
3120 assert_eq!(tok_r.name, "testperson1");
3121 assert!(!tok_r.valid);
3122
3123 let tok_r = idms_prox_read
3124 .get_unixusertoken(&uute, time_high)
3125 .expect("Failed to generate unix user token");
3126
3127 assert_eq!(tok_r.name, "testperson1");
3128 assert!(!tok_r.valid);
3129 }
3130
3131 #[idm_test]
3132 async fn test_idm_radius_valid_from_expire(
3133 idms: &IdmServer,
3134 _idms_delayed: &mut IdmServerDelayed,
3135 ) {
3136 init_testperson_w_password(idms, TEST_PASSWORD)
3139 .await
3140 .expect("Failed to setup admin account");
3141 set_testperson_valid_time(idms).await;
3142
3143 let time_low = Duration::from_secs(TEST_NOT_YET_VALID_TIME);
3144 let time_high = Duration::from_secs(TEST_AFTER_EXPIRY);
3145
3146 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
3147 let rrse = RegenerateRadiusSecretEvent::new_internal(UUID_TESTPERSON_1);
3148 let _r1 = idms_prox_write
3149 .regenerate_radius_secret(&rrse)
3150 .expect("Failed to reset radius credential 1");
3151 idms_prox_write.commit().expect("failed to commit");
3152
3153 let mut idms_prox_read = idms.proxy_read().await.unwrap();
3154 let admin_entry = idms_prox_read
3155 .qs_read
3156 .internal_search_uuid(UUID_ADMIN)
3157 .expect("Can't access admin entry.");
3158
3159 let rate = RadiusAuthTokenEvent::new_impersonate(admin_entry, UUID_ADMIN);
3160 let tok_r = idms_prox_read.get_radiusauthtoken(&rate, time_low);
3161
3162 if tok_r.is_err() {
3163 } else {
3165 debug_assert!(false);
3166 }
3167
3168 let tok_r = idms_prox_read.get_radiusauthtoken(&rate, time_high);
3169
3170 if tok_r.is_err() {
3171 } else {
3173 debug_assert!(false);
3174 }
3175 }
3176
3177 #[idm_test(audit = 1)]
3178 async fn test_idm_account_softlocking(
3179 idms: &IdmServer,
3180 idms_delayed: &mut IdmServerDelayed,
3181 idms_audit: &mut IdmServerAudit,
3182 ) {
3183 init_testperson_w_password(idms, TEST_PASSWORD)
3184 .await
3185 .expect("Failed to setup admin account");
3186
3187 let sid =
3189 init_authsession_sid(idms, Duration::from_secs(TEST_CURRENT_TIME), "testperson1").await;
3190 let mut idms_auth = idms.auth().await.unwrap();
3191 let anon_step = AuthEvent::cred_step_password(sid, TEST_PASSWORD_INC);
3192
3193 let r2 = idms_auth
3194 .auth(
3195 &anon_step,
3196 Duration::from_secs(TEST_CURRENT_TIME),
3197 Source::Internal.into(),
3198 )
3199 .await;
3200 debug!("r2 ==> {:?}", r2);
3201
3202 match r2 {
3203 Ok(ar) => {
3204 let AuthResult {
3205 sessionid: _,
3206 state,
3207 } = ar;
3208 match state {
3209 AuthState::Denied(reason) => {
3210 assert!(reason != "Account is temporarily locked");
3211 }
3212 _ => {
3213 error!("A critical error has occurred! We have a non-denied result!");
3214 panic!();
3215 }
3216 }
3217 }
3218 Err(e) => {
3219 error!("A critical error has occurred! {:?}", e);
3220 panic!();
3221 }
3222 };
3223
3224 match idms_audit.audit_rx().try_recv() {
3226 Ok(AuditEvent::AuthenticationDenied { .. }) => {}
3227 _ => panic!("Oh no"),
3228 }
3229
3230 idms_auth.commit().expect("Must not fail");
3231
3232 let mut idms_auth = idms.auth().await.unwrap();
3236 let admin_init = AuthEvent::named_init("testperson1");
3237
3238 let r1 = idms_auth
3239 .auth(
3240 &admin_init,
3241 Duration::from_secs(TEST_CURRENT_TIME),
3242 Source::Internal.into(),
3243 )
3244 .await;
3245 let ar = r1.unwrap();
3246 let AuthResult { sessionid, state } = ar;
3247 assert!(matches!(state, AuthState::Choose(_)));
3248
3249 let admin_begin = AuthEvent::begin_mech(sessionid, AuthMech::Password);
3251
3252 let r2 = idms_auth
3253 .auth(
3254 &admin_begin,
3255 Duration::from_secs(TEST_CURRENT_TIME),
3256 Source::Internal.into(),
3257 )
3258 .await;
3259 let ar = r2.unwrap();
3260 let AuthResult {
3261 sessionid: _,
3262 state,
3263 } = ar;
3264
3265 match state {
3266 AuthState::Denied(reason) => {
3267 assert_eq!(reason, "Account is temporarily locked");
3268 }
3269 _ => {
3270 error!("Sessions was not denied (softlock)");
3271 panic!();
3272 }
3273 };
3274
3275 idms_auth.commit().expect("Must not fail");
3276
3277 let sid = init_authsession_sid(
3282 idms,
3283 Duration::from_secs(TEST_CURRENT_TIME + 2),
3284 "testperson1",
3285 )
3286 .await;
3287
3288 let mut idms_auth = idms.auth().await.unwrap();
3289 let anon_step = AuthEvent::cred_step_password(sid, TEST_PASSWORD);
3290
3291 let r2 = idms_auth
3293 .auth(
3294 &anon_step,
3295 Duration::from_secs(TEST_CURRENT_TIME + 2),
3296 Source::Internal.into(),
3297 )
3298 .await;
3299 debug!("r2 ==> {:?}", r2);
3300
3301 match r2 {
3302 Ok(ar) => {
3303 let AuthResult {
3304 sessionid: _,
3305 state,
3306 } = ar;
3307 match state {
3308 AuthState::Success(_uat, AuthIssueSession::Token) => {
3309 }
3311 _ => {
3312 error!("A critical error has occurred! We have a non-success result!");
3313 panic!();
3314 }
3315 }
3316 }
3317 Err(e) => {
3318 error!("A critical error has occurred! {:?}", e);
3319 panic!();
3321 }
3322 };
3323
3324 idms_auth.commit().expect("Must not fail");
3325
3326 let da = idms_delayed.try_recv().expect("invalid");
3328 assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
3329 idms_delayed.check_is_empty_or_panic();
3330
3331 }
3338
3339 #[idm_test(audit = 1)]
3340 async fn test_idm_account_softlocking_interleaved(
3341 idms: &IdmServer,
3342 _idms_delayed: &mut IdmServerDelayed,
3343 idms_audit: &mut IdmServerAudit,
3344 ) {
3345 init_testperson_w_password(idms, TEST_PASSWORD)
3346 .await
3347 .expect("Failed to setup admin account");
3348
3349 let sid_early =
3351 init_authsession_sid(idms, Duration::from_secs(TEST_CURRENT_TIME), "testperson1").await;
3352
3353 let sid_later =
3355 init_authsession_sid(idms, Duration::from_secs(TEST_CURRENT_TIME), "testperson1").await;
3356 let mut idms_auth = idms.auth().await.unwrap();
3358 let anon_step = AuthEvent::cred_step_password(sid_later, TEST_PASSWORD_INC);
3359
3360 let r2 = idms_auth
3361 .auth(
3362 &anon_step,
3363 Duration::from_secs(TEST_CURRENT_TIME),
3364 Source::Internal.into(),
3365 )
3366 .await;
3367 debug!("r2 ==> {:?}", r2);
3368
3369 match r2 {
3370 Ok(ar) => {
3371 let AuthResult {
3372 sessionid: _,
3373 state,
3374 } = ar;
3375 match state {
3376 AuthState::Denied(reason) => {
3377 assert!(reason != "Account is temporarily locked");
3378 }
3379 _ => {
3380 error!("A critical error has occurred! We have a non-denied result!");
3381 panic!();
3382 }
3383 }
3384 }
3385 Err(e) => {
3386 error!("A critical error has occurred! {:?}", e);
3387 panic!();
3388 }
3389 };
3390
3391 match idms_audit.audit_rx().try_recv() {
3392 Ok(AuditEvent::AuthenticationDenied { .. }) => {}
3393 _ => panic!("Oh no"),
3394 }
3395
3396 idms_auth.commit().expect("Must not fail");
3397
3398 let mut idms_auth = idms.auth().await.unwrap();
3400 let anon_step = AuthEvent::cred_step_password(sid_early, TEST_PASSWORD);
3401
3402 let r2 = idms_auth
3404 .auth(
3405 &anon_step,
3406 Duration::from_secs(TEST_CURRENT_TIME),
3407 Source::Internal.into(),
3408 )
3409 .await;
3410 debug!("r2 ==> {:?}", r2);
3411 match r2 {
3412 Ok(ar) => {
3413 let AuthResult {
3414 sessionid: _,
3415 state,
3416 } = ar;
3417 match state {
3418 AuthState::Denied(reason) => {
3419 assert_eq!(reason, "Account is temporarily locked");
3420 }
3421 _ => {
3422 error!("A critical error has occurred! We have a non-denied result!");
3423 panic!();
3424 }
3425 }
3426 }
3427 Err(e) => {
3428 error!("A critical error has occurred! {:?}", e);
3429 panic!();
3430 }
3431 };
3432 idms_auth.commit().expect("Must not fail");
3433 }
3434
3435 #[idm_test]
3436 async fn test_idm_account_unix_softlocking(
3437 idms: &IdmServer,
3438 _idms_delayed: &mut IdmServerDelayed,
3439 ) {
3440 init_testperson_w_password(idms, TEST_PASSWORD)
3441 .await
3442 .expect("Failed to setup admin account");
3443 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
3445 let me_posix = ModifyEvent::new_internal_invalid(
3446 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_TESTPERSON_1))),
3447 ModifyList::new_list(vec![
3448 Modify::Present(Attribute::Class, EntryClass::PosixAccount.into()),
3449 Modify::Present(Attribute::GidNumber, Value::new_uint32(2001)),
3450 ]),
3451 );
3452 assert!(idms_prox_write.qs_write.modify(&me_posix).is_ok());
3453
3454 let pce = UnixPasswordChangeEvent::new_internal(UUID_TESTPERSON_1, TEST_PASSWORD);
3455 assert!(idms_prox_write.set_unix_account_password(&pce).is_ok());
3456 assert!(idms_prox_write.commit().is_ok());
3457
3458 let mut idms_auth = idms.auth().await.unwrap();
3459 let uuae_good = UnixUserAuthEvent::new_internal(UUID_TESTPERSON_1, TEST_PASSWORD);
3460 let uuae_bad = UnixUserAuthEvent::new_internal(UUID_TESTPERSON_1, TEST_PASSWORD_INC);
3461
3462 let a2 = idms_auth
3463 .auth_unix(&uuae_bad, Duration::from_secs(TEST_CURRENT_TIME))
3464 .await;
3465 match a2 {
3466 Ok(None) => {}
3467 _ => panic!("Oh no"),
3468 };
3469
3470 let a1 = idms_auth
3472 .auth_unix(&uuae_good, Duration::from_secs(TEST_CURRENT_TIME))
3473 .await;
3474 match a1 {
3475 Ok(None) => {}
3476 _ => panic!("Oh no"),
3477 };
3478
3479 let a1 = idms_auth
3481 .auth_unix(&uuae_good, Duration::from_secs(TEST_CURRENT_TIME + 2))
3482 .await;
3483 match a1 {
3484 Ok(Some(_tok)) => {}
3485 _ => panic!("Oh no"),
3486 };
3487
3488 assert!(idms_auth.commit().is_ok());
3489 }
3490
3491 #[idm_test]
3492 async fn test_idm_jwt_uat_expiry(idms: &IdmServer, idms_delayed: &mut IdmServerDelayed) {
3493 let ct = Duration::from_secs(TEST_CURRENT_TIME);
3494 let expiry = ct + Duration::from_secs((DEFAULT_AUTH_SESSION_EXPIRY + 1).into());
3495 init_testperson_w_password(idms, TEST_PASSWORD)
3497 .await
3498 .expect("Failed to setup admin account");
3499 let token = check_testperson_password(idms, TEST_PASSWORD, ct).await;
3500
3501 let da = idms_delayed.try_recv().expect("invalid");
3503 assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
3504 let r = idms.delayed_action(ct, da).await;
3506 assert_eq!(Ok(true), r);
3507 idms_delayed.check_is_empty_or_panic();
3508
3509 let mut idms_prox_read = idms.proxy_read().await.unwrap();
3510
3511 idms_prox_read
3513 .validate_client_auth_info_to_ident(token.clone().into(), ct)
3514 .expect("Failed to validate");
3515
3516 match idms_prox_read.validate_client_auth_info_to_ident(token.into(), expiry) {
3518 Err(OperationError::SessionExpired) => {}
3519 _ => panic!("Oh no"),
3520 }
3521 }
3522
3523 #[idm_test]
3524 async fn test_idm_expired_auth_session_cleanup(
3525 idms: &IdmServer,
3526 _idms_delayed: &mut IdmServerDelayed,
3527 ) {
3528 let ct = Duration::from_secs(TEST_CURRENT_TIME);
3529 let expiry_a = ct + Duration::from_secs((DEFAULT_AUTH_SESSION_EXPIRY + 1).into());
3530 let expiry_b = ct + Duration::from_secs(((DEFAULT_AUTH_SESSION_EXPIRY + 1) * 2).into());
3531
3532 let session_a = Uuid::new_v4();
3533 let session_b = Uuid::new_v4();
3534
3535 let cred_id = init_testperson_w_password(idms, TEST_PASSWORD)
3537 .await
3538 .expect("Failed to setup admin account");
3539
3540 let mut idms_prox_read = idms.proxy_read().await.unwrap();
3542 let admin = idms_prox_read
3543 .qs_read
3544 .internal_search_uuid(UUID_TESTPERSON_1)
3545 .expect("failed");
3546 let sessions = admin.get_ava_as_session_map(Attribute::UserAuthTokenSession);
3547 assert!(sessions.is_none());
3548 drop(idms_prox_read);
3549
3550 let da = DelayedAction::AuthSessionRecord(AuthSessionRecord {
3551 target_uuid: UUID_TESTPERSON_1,
3552 session_id: session_a,
3553 cred_id,
3554 label: "Test Session A".to_string(),
3555 expiry: Some(OffsetDateTime::UNIX_EPOCH + expiry_a),
3556 issued_at: OffsetDateTime::UNIX_EPOCH + ct,
3557 issued_by: IdentityId::User(UUID_ADMIN),
3558 scope: SessionScope::ReadOnly,
3559 type_: AuthType::Passkey,
3560 });
3561 let r = idms.delayed_action(ct, da).await;
3563 assert_eq!(Ok(true), r);
3564
3565 let mut idms_prox_read = idms.proxy_read().await.unwrap();
3567 let admin = idms_prox_read
3568 .qs_read
3569 .internal_search_uuid(UUID_TESTPERSON_1)
3570 .expect("failed");
3571 let sessions = admin
3572 .get_ava_as_session_map(Attribute::UserAuthTokenSession)
3573 .expect("Sessions must be present!");
3574 assert_eq!(sessions.len(), 1);
3575 let session_data_a = sessions.get(&session_a).expect("Session A is missing!");
3576 assert!(matches!(session_data_a.state, SessionState::ExpiresAt(_)));
3577
3578 drop(idms_prox_read);
3579
3580 let da = DelayedAction::AuthSessionRecord(AuthSessionRecord {
3583 target_uuid: UUID_TESTPERSON_1,
3584 session_id: session_b,
3585 cred_id,
3586 label: "Test Session B".to_string(),
3587 expiry: Some(OffsetDateTime::UNIX_EPOCH + expiry_b),
3588 issued_at: OffsetDateTime::UNIX_EPOCH + ct,
3589 issued_by: IdentityId::User(UUID_ADMIN),
3590 scope: SessionScope::ReadOnly,
3591 type_: AuthType::Passkey,
3592 });
3593 let r = idms.delayed_action(expiry_a, da).await;
3595 assert_eq!(Ok(true), r);
3596
3597 let mut idms_prox_read = idms.proxy_read().await.unwrap();
3598 let admin = idms_prox_read
3599 .qs_read
3600 .internal_search_uuid(UUID_TESTPERSON_1)
3601 .expect("failed");
3602 let sessions = admin
3603 .get_ava_as_session_map(Attribute::UserAuthTokenSession)
3604 .expect("Sessions must be present!");
3605 trace!(?sessions);
3606 assert_eq!(sessions.len(), 2);
3607
3608 let session_data_a = sessions.get(&session_a).expect("Session A is missing!");
3609 assert!(matches!(session_data_a.state, SessionState::RevokedAt(_)));
3610
3611 let session_data_b = sessions.get(&session_b).expect("Session B is missing!");
3612 assert!(matches!(session_data_b.state, SessionState::ExpiresAt(_)));
3613 }
3615
3616 #[idm_test]
3617 async fn test_idm_account_session_validation(
3618 idms: &IdmServer,
3619 idms_delayed: &mut IdmServerDelayed,
3620 ) {
3621 use kanidm_proto::internal::UserAuthToken;
3622
3623 let ct = duration_from_epoch_now();
3624
3625 let post_grace = ct + AUTH_TOKEN_GRACE_WINDOW + Duration::from_secs(1);
3626 let expiry = ct + Duration::from_secs(DEFAULT_AUTH_SESSION_EXPIRY as u64 + 1);
3627
3628 assert!(post_grace < expiry);
3631
3632 init_testperson_w_password(idms, TEST_PASSWORD)
3634 .await
3635 .expect("Failed to setup admin account");
3636 let uat_unverified = check_testperson_password(idms, TEST_PASSWORD, ct).await;
3637
3638 let da = idms_delayed.try_recv().expect("invalid");
3640 assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
3641 let r = idms.delayed_action(ct, da).await;
3642 assert_eq!(Ok(true), r);
3643
3644 let mut idms_prox_read = idms.proxy_read().await.unwrap();
3645
3646 let token_kid = uat_unverified.kid().expect("no key id present");
3647
3648 let uat_jwk = idms_prox_read
3649 .qs_read
3650 .get_key_providers()
3651 .get_key_object(UUID_DOMAIN_INFO)
3652 .and_then(|object| {
3653 object
3654 .jws_public_jwk(token_kid)
3655 .expect("Unable to access uat jwk")
3656 })
3657 .expect("No jwk by this kid");
3658
3659 let jws_validator = JwsEs256Verifier::try_from(&uat_jwk).unwrap();
3660
3661 let uat_inner: UserAuthToken = jws_validator
3662 .verify(&uat_unverified)
3663 .unwrap()
3664 .from_json()
3665 .unwrap();
3666
3667 idms_prox_read
3669 .validate_client_auth_info_to_ident(uat_unverified.clone().into(), ct)
3670 .expect("Failed to validate");
3671
3672 idms_prox_read
3674 .validate_client_auth_info_to_ident(uat_unverified.clone().into(), post_grace)
3675 .expect("Failed to validate");
3676
3677 drop(idms_prox_read);
3678
3679 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
3681 let dte = DestroySessionTokenEvent::new_internal(uat_inner.uuid, uat_inner.session_id);
3682 assert!(idms_prox_write.account_destroy_session_token(&dte).is_ok());
3683 assert!(idms_prox_write.commit().is_ok());
3684
3685 let mut idms_prox_read = idms.proxy_read().await.unwrap();
3687
3688 match idms_prox_read
3691 .validate_client_auth_info_to_ident(uat_unverified.clone().into(), post_grace)
3692 {
3693 Err(OperationError::SessionExpired) => {}
3694 _ => panic!("Oh no"),
3695 }
3696 drop(idms_prox_read);
3697
3698 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
3700 let filt = filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(uat_inner.uuid)));
3701 let mut work_set = idms_prox_write
3702 .qs_write
3703 .internal_search_writeable(&filt)
3704 .expect("Failed to perform internal search writeable");
3705 for (_, entry) in work_set.iter_mut() {
3706 let _ = entry.force_trim_ava(Attribute::UserAuthTokenSession);
3707 }
3708 assert!(idms_prox_write
3709 .qs_write
3710 .internal_apply_writable(work_set)
3711 .is_ok());
3712
3713 assert!(idms_prox_write.commit().is_ok());
3714
3715 let mut idms_prox_read = idms.proxy_read().await.unwrap();
3716 idms_prox_read
3717 .validate_client_auth_info_to_ident(uat_unverified.clone().into(), ct)
3718 .expect("Failed to validate");
3719
3720 match idms_prox_read
3722 .validate_client_auth_info_to_ident(uat_unverified.clone().into(), post_grace)
3723 {
3724 Err(OperationError::SessionExpired) => {}
3725 _ => panic!("Oh no"),
3726 }
3727 }
3728
3729 #[idm_test]
3730 async fn test_idm_account_session_expiry(
3731 idms: &IdmServer,
3732 _idms_delayed: &mut IdmServerDelayed,
3733 ) {
3734 let ct = Duration::from_secs(TEST_CURRENT_TIME);
3735
3736 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
3738
3739 let new_authsession_expiry = 1000;
3740
3741 let modlist = ModifyList::new_purge_and_set(
3742 Attribute::AuthSessionExpiry,
3743 Value::Uint32(new_authsession_expiry),
3744 );
3745 idms_prox_write
3746 .qs_write
3747 .internal_modify_uuid(UUID_IDM_ALL_ACCOUNTS, &modlist)
3748 .expect("Unable to change default session exp");
3749
3750 assert!(idms_prox_write.commit().is_ok());
3751
3752 let mut idms_auth = idms.auth().await.unwrap();
3754 let anon_init = AuthEvent::anonymous_init();
3756 let r1 = idms_auth
3758 .auth(&anon_init, ct, Source::Internal.into())
3759 .await;
3760 let sid = match r1 {
3763 Ok(ar) => {
3764 let AuthResult { sessionid, state } = ar;
3765 match state {
3766 AuthState::Choose(mut conts) => {
3767 assert_eq!(conts.len(), 1);
3769 let m = conts.pop().expect("Should not fail");
3771 assert_eq!(m, AuthMech::Anonymous);
3772 }
3773 _ => {
3774 error!("A critical error has occurred! We have a non-continue result!");
3775 panic!();
3776 }
3777 };
3778 sessionid
3780 }
3781 Err(e) => {
3782 error!("A critical error has occurred! {:?}", e);
3784 panic!();
3785 }
3786 };
3787
3788 idms_auth.commit().expect("Must not fail");
3789
3790 let mut idms_auth = idms.auth().await.unwrap();
3791 let anon_begin = AuthEvent::begin_mech(sid, AuthMech::Anonymous);
3792
3793 let r2 = idms_auth
3794 .auth(&anon_begin, ct, Source::Internal.into())
3795 .await;
3796
3797 match r2 {
3798 Ok(ar) => {
3799 let AuthResult {
3800 sessionid: _,
3801 state,
3802 } = ar;
3803
3804 match state {
3805 AuthState::Continue(allowed) => {
3806 assert_eq!(allowed.len(), 1);
3808 assert_eq!(allowed.first(), Some(&AuthAllowed::Anonymous));
3809 }
3810 _ => {
3811 error!("A critical error has occurred! We have a non-continue result!");
3812 panic!();
3813 }
3814 }
3815 }
3816 Err(e) => {
3817 error!("A critical error has occurred! {:?}", e);
3818 panic!();
3820 }
3821 };
3822
3823 idms_auth.commit().expect("Must not fail");
3824
3825 let mut idms_auth = idms.auth().await.unwrap();
3826 let anon_step = AuthEvent::cred_step_anonymous(sid);
3828
3829 let r2 = idms_auth
3831 .auth(&anon_step, ct, Source::Internal.into())
3832 .await;
3833
3834 let token = match r2 {
3835 Ok(ar) => {
3836 let AuthResult {
3837 sessionid: _,
3838 state,
3839 } = ar;
3840
3841 match state {
3842 AuthState::Success(uat, AuthIssueSession::Token) => uat,
3843 _ => {
3844 error!("A critical error has occurred! We have a non-success result!");
3845 panic!();
3846 }
3847 }
3848 }
3849 Err(e) => {
3850 error!("A critical error has occurred! {:?}", e);
3851 panic!("A critical error has occurred! {:?}", e);
3853 }
3854 };
3855
3856 idms_auth.commit().expect("Must not fail");
3857
3858 let Token::UserAuthToken(uat) = idms
3861 .proxy_read()
3862 .await
3863 .unwrap()
3864 .validate_and_parse_token_to_token(&token, ct)
3865 .expect("Must not fail")
3866 else {
3867 panic!("Unexpected auth token type for anonymous auth");
3868 };
3869
3870 debug!(?uat);
3871
3872 assert!(
3873 matches!(uat.expiry, Some(exp) if exp == OffsetDateTime::UNIX_EPOCH + ct + Duration::from_secs(new_authsession_expiry as u64))
3874 );
3875 }
3876
3877 #[idm_test]
3878 async fn test_idm_uat_claim_insertion(idms: &IdmServer, _idms_delayed: &mut IdmServerDelayed) {
3879 let ct = Duration::from_secs(TEST_CURRENT_TIME);
3880 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
3881
3882 let account = idms_prox_write
3884 .target_to_account(UUID_ADMIN)
3885 .expect("account must exist");
3886
3887 let session_id = uuid::Uuid::new_v4();
3889
3890 let uat = account
3894 .to_userauthtoken(
3895 session_id,
3896 SessionScope::ReadWrite,
3897 ct,
3898 &ResolvedAccountPolicy::test_policy(),
3899 )
3900 .expect("Unable to create uat");
3901 let ident = idms_prox_write
3902 .process_uat_to_identity(&uat, ct, Source::Internal)
3903 .expect("Unable to process uat");
3904
3905 assert!(!ident.has_claim("authtype_anonymous"));
3906 assert!(!ident.has_claim("authlevel_strong"));
3908 assert!(!ident.has_claim("authclass_single"));
3909 assert!(!ident.has_claim("authclass_mfa"));
3910
3911 let uat = account
3913 .to_userauthtoken(
3914 session_id,
3915 SessionScope::ReadWrite,
3916 ct,
3917 &ResolvedAccountPolicy::test_policy(),
3918 )
3919 .expect("Unable to create uat");
3920 let ident = idms_prox_write
3921 .process_uat_to_identity(&uat, ct, Source::Internal)
3922 .expect("Unable to process uat");
3923
3924 assert!(!ident.has_claim("authtype_unixpassword"));
3925 assert!(!ident.has_claim("authclass_single"));
3926 assert!(!ident.has_claim("authlevel_strong"));
3928 assert!(!ident.has_claim("authclass_mfa"));
3929
3930 let uat = account
3932 .to_userauthtoken(
3933 session_id,
3934 SessionScope::ReadWrite,
3935 ct,
3936 &ResolvedAccountPolicy::test_policy(),
3937 )
3938 .expect("Unable to create uat");
3939 let ident = idms_prox_write
3940 .process_uat_to_identity(&uat, ct, Source::Internal)
3941 .expect("Unable to process uat");
3942
3943 assert!(!ident.has_claim("authtype_password"));
3944 assert!(!ident.has_claim("authclass_single"));
3945 assert!(!ident.has_claim("authlevel_strong"));
3947 assert!(!ident.has_claim("authclass_mfa"));
3948
3949 let uat = account
3951 .to_userauthtoken(
3952 session_id,
3953 SessionScope::ReadWrite,
3954 ct,
3955 &ResolvedAccountPolicy::test_policy(),
3956 )
3957 .expect("Unable to create uat");
3958 let ident = idms_prox_write
3959 .process_uat_to_identity(&uat, ct, Source::Internal)
3960 .expect("Unable to process uat");
3961
3962 assert!(!ident.has_claim("authtype_generatedpassword"));
3963 assert!(!ident.has_claim("authclass_single"));
3964 assert!(!ident.has_claim("authlevel_strong"));
3965 assert!(!ident.has_claim("authclass_mfa"));
3967
3968 let uat = account
3970 .to_userauthtoken(
3971 session_id,
3972 SessionScope::ReadWrite,
3973 ct,
3974 &ResolvedAccountPolicy::test_policy(),
3975 )
3976 .expect("Unable to create uat");
3977 let ident = idms_prox_write
3978 .process_uat_to_identity(&uat, ct, Source::Internal)
3979 .expect("Unable to process uat");
3980
3981 assert!(!ident.has_claim("authtype_webauthn"));
3982 assert!(!ident.has_claim("authclass_single"));
3983 assert!(!ident.has_claim("authlevel_strong"));
3984 assert!(!ident.has_claim("authclass_mfa"));
3986
3987 let uat = account
3989 .to_userauthtoken(
3990 session_id,
3991 SessionScope::ReadWrite,
3992 ct,
3993 &ResolvedAccountPolicy::test_policy(),
3994 )
3995 .expect("Unable to create uat");
3996 let ident = idms_prox_write
3997 .process_uat_to_identity(&uat, ct, Source::Internal)
3998 .expect("Unable to process uat");
3999
4000 assert!(!ident.has_claim("authtype_passwordmfa"));
4001 assert!(!ident.has_claim("authlevel_strong"));
4002 assert!(!ident.has_claim("authclass_mfa"));
4003 assert!(!ident.has_claim("authclass_single"));
4005 }
4006
4007 #[idm_test]
4008 async fn test_idm_uat_limits_account_policy(
4009 idms: &IdmServer,
4010 _idms_delayed: &mut IdmServerDelayed,
4011 ) {
4012 let ct = Duration::from_secs(TEST_CURRENT_TIME);
4013 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
4014
4015 idms_prox_write
4016 .qs_write
4017 .internal_create(vec![E_TESTPERSON_1.clone()])
4018 .expect("Failed to create test person");
4019
4020 let account = idms_prox_write
4022 .target_to_account(UUID_TESTPERSON_1)
4023 .expect("account must exist");
4024
4025 let session_id = uuid::Uuid::new_v4();
4027
4028 let uat = account
4029 .to_userauthtoken(
4030 session_id,
4031 SessionScope::ReadWrite,
4032 ct,
4033 &ResolvedAccountPolicy::test_policy(),
4034 )
4035 .expect("Unable to create uat");
4036
4037 let ident = idms_prox_write
4038 .process_uat_to_identity(&uat, ct, Source::Internal)
4039 .expect("Unable to process uat");
4040
4041 assert_eq!(
4042 ident.limits().search_max_results,
4043 DEFAULT_LIMIT_SEARCH_MAX_RESULTS as usize
4044 );
4045 assert_eq!(
4046 ident.limits().search_max_filter_test,
4047 DEFAULT_LIMIT_SEARCH_MAX_FILTER_TEST as usize
4048 );
4049 }
4050
4051 #[idm_test]
4052 async fn test_idm_jwt_uat_token_key_reload(
4053 idms: &IdmServer,
4054 idms_delayed: &mut IdmServerDelayed,
4055 ) {
4056 let ct = duration_from_epoch_now();
4057
4058 init_testperson_w_password(idms, TEST_PASSWORD)
4059 .await
4060 .expect("Failed to setup admin account");
4061 let token = check_testperson_password(idms, TEST_PASSWORD, ct).await;
4062
4063 let da = idms_delayed.try_recv().expect("invalid");
4065 assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
4066 idms_delayed.check_is_empty_or_panic();
4067
4068 let mut idms_prox_read = idms.proxy_read().await.unwrap();
4069
4070 idms_prox_read
4072 .validate_client_auth_info_to_ident(token.clone().into(), ct)
4073 .expect("Failed to validate");
4074
4075 drop(idms_prox_read);
4076
4077 let revoke_kid = token.kid().expect("token does not contain a key id");
4079
4080 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
4082 let me_reset_tokens = ModifyEvent::new_internal_invalid(
4083 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_DOMAIN_INFO))),
4084 ModifyList::new_append(
4085 Attribute::KeyActionRevoke,
4086 Value::HexString(revoke_kid.to_string()),
4087 ),
4088 );
4089 assert!(idms_prox_write.qs_write.modify(&me_reset_tokens).is_ok());
4090 assert!(idms_prox_write.commit().is_ok());
4091
4092 let new_token = check_testperson_password(idms, TEST_PASSWORD, ct).await;
4093
4094 let da = idms_delayed.try_recv().expect("invalid");
4096 assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
4097 idms_delayed.check_is_empty_or_panic();
4098
4099 let mut idms_prox_read = idms.proxy_read().await.unwrap();
4100
4101 assert!(idms_prox_read
4103 .validate_client_auth_info_to_ident(token.into(), ct)
4104 .is_err());
4105
4106 idms_prox_read
4108 .validate_client_auth_info_to_ident(new_token.into(), ct)
4109 .expect("Failed to validate");
4110 }
4111
4112 #[idm_test]
4113 async fn test_idm_service_account_to_person(
4114 idms: &IdmServer,
4115 _idms_delayed: &mut IdmServerDelayed,
4116 ) {
4117 let ct = Duration::from_secs(TEST_CURRENT_TIME);
4118 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
4119
4120 let ident = Identity::from_internal();
4121 let target_uuid = Uuid::new_v4();
4122
4123 let e = entry_init!(
4125 (Attribute::Class, EntryClass::Object.to_value()),
4126 (Attribute::Class, EntryClass::Account.to_value()),
4127 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
4128 (Attribute::Name, Value::new_iname("testaccount")),
4129 (Attribute::Uuid, Value::Uuid(target_uuid)),
4130 (Attribute::Description, Value::new_utf8s("testaccount")),
4131 (Attribute::DisplayName, Value::new_utf8s("Test Account"))
4132 );
4133
4134 let ce = CreateEvent::new_internal(vec![e]);
4135 let cr = idms_prox_write.qs_write.create(&ce);
4136 assert!(cr.is_ok());
4137
4138 assert!(idms_prox_write
4140 .service_account_into_person(&ident, target_uuid)
4141 .is_ok());
4142
4143 }
4145
4146 async fn idm_fallback_auth_fixture(
4147 idms: &IdmServer,
4148 _idms_delayed: &mut IdmServerDelayed,
4149 has_posix_password: bool,
4150 allow_primary_cred_fallback: Option<bool>,
4151 expected: Option<()>,
4152 ) {
4153 let ct = Duration::from_secs(TEST_CURRENT_TIME);
4154 let target_uuid = Uuid::new_v4();
4155 let p = CryptoPolicy::minimum();
4156
4157 {
4158 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
4159
4160 if let Some(allow_primary_cred_fallback) = allow_primary_cred_fallback {
4161 idms_prox_write
4162 .qs_write
4163 .internal_modify_uuid(
4164 UUID_IDM_ALL_ACCOUNTS,
4165 &ModifyList::new_purge_and_set(
4166 Attribute::AllowPrimaryCredFallback,
4167 Value::new_bool(allow_primary_cred_fallback),
4168 ),
4169 )
4170 .expect("Unable to change default session exp");
4171 }
4172
4173 let mut e = entry_init!(
4174 (Attribute::Class, EntryClass::Object.to_value()),
4175 (Attribute::Class, EntryClass::Account.to_value()),
4176 (Attribute::Class, EntryClass::Person.to_value()),
4177 (Attribute::Uuid, Value::Uuid(target_uuid)),
4178 (Attribute::Name, Value::new_iname("kevin")),
4179 (Attribute::DisplayName, Value::new_utf8s("Kevin")),
4180 (Attribute::Class, EntryClass::PosixAccount.to_value()),
4181 (
4182 Attribute::PrimaryCredential,
4183 Value::Cred(
4184 "primary".to_string(),
4185 Credential::new_password_only(&p, "banana").unwrap()
4186 )
4187 )
4188 );
4189
4190 if has_posix_password {
4191 e.add_ava(
4192 Attribute::UnixPassword,
4193 Value::Cred(
4194 "unix".to_string(),
4195 Credential::new_password_only(&p, "kampai").unwrap(),
4196 ),
4197 );
4198 }
4199
4200 let ce = CreateEvent::new_internal(vec![e]);
4201 let cr = idms_prox_write.qs_write.create(&ce);
4202 assert!(cr.is_ok());
4203 idms_prox_write.commit().expect("Must not fail");
4204 }
4205
4206 let result = idms
4207 .auth()
4208 .await
4209 .unwrap()
4210 .auth_ldap(
4211 &LdapAuthEvent {
4212 target: target_uuid,
4213 cleartext: if has_posix_password {
4214 "kampai".to_string()
4215 } else {
4216 "banana".to_string()
4217 },
4218 },
4219 ct,
4220 )
4221 .await;
4222
4223 assert!(result.is_ok());
4224 if expected.is_some() {
4225 assert!(result.unwrap().is_some());
4226 } else {
4227 assert!(result.unwrap().is_none());
4228 }
4229 }
4230
4231 #[idm_test]
4232 async fn test_idm_fallback_auth_no_pass_none_fallback(
4233 idms: &IdmServer,
4234 _idms_delayed: &mut IdmServerDelayed,
4235 ) {
4236 idm_fallback_auth_fixture(idms, _idms_delayed, false, None, None).await;
4237 }
4238 #[idm_test]
4239 async fn test_idm_fallback_auth_pass_none_fallback(
4240 idms: &IdmServer,
4241 _idms_delayed: &mut IdmServerDelayed,
4242 ) {
4243 idm_fallback_auth_fixture(idms, _idms_delayed, true, None, Some(())).await;
4244 }
4245 #[idm_test]
4246 async fn test_idm_fallback_auth_no_pass_true_fallback(
4247 idms: &IdmServer,
4248 _idms_delayed: &mut IdmServerDelayed,
4249 ) {
4250 idm_fallback_auth_fixture(idms, _idms_delayed, false, Some(true), Some(())).await;
4251 }
4252 #[idm_test]
4253 async fn test_idm_fallback_auth_pass_true_fallback(
4254 idms: &IdmServer,
4255 _idms_delayed: &mut IdmServerDelayed,
4256 ) {
4257 idm_fallback_auth_fixture(idms, _idms_delayed, true, Some(true), Some(())).await;
4258 }
4259 #[idm_test]
4260 async fn test_idm_fallback_auth_no_pass_false_fallback(
4261 idms: &IdmServer,
4262 _idms_delayed: &mut IdmServerDelayed,
4263 ) {
4264 idm_fallback_auth_fixture(idms, _idms_delayed, false, Some(false), None).await;
4265 }
4266 #[idm_test]
4267 async fn test_idm_fallback_auth_pass_false_fallback(
4268 idms: &IdmServer,
4269 _idms_delayed: &mut IdmServerDelayed,
4270 ) {
4271 idm_fallback_auth_fixture(idms, _idms_delayed, true, Some(false), Some(())).await;
4272 }
4273}