1use std::collections::BTreeSet;
5use std::iter;
6use std::str::FromStr;
7
8use compact_jwt::JwsCompact;
9use kanidm_proto::constants::*;
10use kanidm_proto::internal::{ApiToken, UserAuthToken};
11use ldap3_proto::simple::*;
12use regex::{Regex, RegexBuilder};
13use std::net::IpAddr;
14use tracing::trace;
15use uuid::Uuid;
16
17use crate::event::SearchEvent;
18use crate::idm::event::{LdapApplicationAuthEvent, LdapAuthEvent, LdapTokenAuthEvent};
19use crate::idm::server::{IdmServer, IdmServerAuthTransaction, IdmServerTransaction};
20use crate::prelude::*;
21
22#[allow(clippy::large_enum_variant)]
25pub enum LdapResponseState {
26 Unbind,
27 Disconnect(LdapMsg),
28 Bind(LdapBoundToken, LdapMsg),
29 Respond(LdapMsg),
30 MultiPartResponse(Vec<LdapMsg>),
31 BindMultiPartResponse(LdapBoundToken, Vec<LdapMsg>),
32}
33
34#[derive(Debug, Clone, PartialEq, Eq)]
35pub enum LdapSession {
36 UnixBind(Uuid),
39 UserAuthToken(UserAuthToken),
40 ApiToken(ApiToken),
41 ApplicationPasswordBind(Uuid, Uuid),
42}
43
44#[derive(Debug, Clone)]
45pub struct LdapBoundToken {
46 pub spn: String,
48 pub session_id: Uuid,
49 pub effective_session: LdapSession,
56}
57
58pub struct LdapServer {
59 rootdse: LdapSearchResultEntry,
60 basedn: String,
61 dnre: Regex,
62 binddnre: Regex,
63 max_queryable_attrs: usize,
64}
65
66#[derive(Debug)]
67enum LdapBindTarget {
68 Account(Uuid),
69 ApiToken,
70 Application(String, Uuid),
71}
72
73impl LdapServer {
74 pub async fn new(idms: &IdmServer) -> Result<Self, OperationError> {
75 let mut idms_prox_read = idms.proxy_read().await?;
77 let domain_entry = idms_prox_read
80 .qs_read
81 .internal_search_uuid(UUID_DOMAIN_INFO)?;
82
83 let max_queryable_attrs = domain_entry
85 .get_ava_single_uint32(Attribute::LdapMaxQueryableAttrs)
86 .map(|u| u as usize)
87 .unwrap_or(DEFAULT_LDAP_MAXIMUM_QUERYABLE_ATTRIBUTES);
88
89 let basedn = domain_entry
90 .get_ava_single_iutf8(Attribute::DomainLdapBasedn)
91 .map(|s| s.to_string())
92 .or_else(|| {
93 domain_entry
94 .get_ava_single_iname(Attribute::DomainName)
95 .map(ldap_domain_to_dc)
96 })
97 .ok_or(OperationError::InvalidEntryState)?;
98
99 let dnre = RegexBuilder::new(
113 format!("^((?P<attr>[^=,]+)=(?P<val>[^=,]+),)?(app=(?P<app>[^=,]+),)?({basedn})$")
114 .as_str(),
115 )
116 .swap_greed(true)
117 .build()
118 .map_err(|_| OperationError::InvalidEntryState)?;
119
120 let binddnre = Regex::new(
121 format!("^((([^=,]+)=)?(?P<val>[^=,]+))(,app=(?P<app>[^=,]+))?(,{basedn})?$").as_str(),
122 )
123 .map_err(|_| OperationError::InvalidEntryState)?;
124
125 let rootdse = LdapSearchResultEntry {
126 dn: "".to_string(),
127 attributes: vec![
128 LdapPartialAttribute {
129 atype: ATTR_OBJECTCLASS.to_string(),
130 vals: vec!["top".as_bytes().to_vec()],
131 },
132 LdapPartialAttribute {
133 atype: "vendorname".to_string(),
134 vals: vec!["Kanidm Project".as_bytes().to_vec()],
135 },
136 LdapPartialAttribute {
137 atype: "vendorversion".to_string(),
138 vals: vec![env!("CARGO_PKG_VERSION").as_bytes().to_vec()],
139 },
140 LdapPartialAttribute {
141 atype: "supportedldapversion".to_string(),
142 vals: vec!["3".as_bytes().to_vec()],
143 },
144 LdapPartialAttribute {
145 atype: "supportedextension".to_string(),
146 vals: vec!["1.3.6.1.4.1.4203.1.11.3".as_bytes().to_vec()],
147 },
148 LdapPartialAttribute {
149 atype: "supportedfeatures".to_string(),
150 vals: vec!["1.3.6.1.4.1.4203.1.5.1".as_bytes().to_vec()],
151 },
152 LdapPartialAttribute {
153 atype: "defaultnamingcontext".to_string(),
154 vals: vec![basedn.as_bytes().to_vec()],
155 },
156 ],
157 };
158
159 Ok(LdapServer {
160 rootdse,
161 basedn,
162 dnre,
163 binddnre,
164 max_queryable_attrs,
165 })
166 }
167
168 #[instrument(level = "debug", skip_all)]
169 async fn do_search(
170 &self,
171 idms: &IdmServer,
172 sr: &SearchRequest,
173 uat: &LdapBoundToken,
174 source: Source,
175 ) -> Result<Vec<LdapMsg>, OperationError> {
177 admin_info!("Attempt LDAP Search for {}", uat.spn);
178 if sr.base.is_empty() && sr.scope == LdapSearchScope::Base {
180 admin_info!("LDAP Search success - RootDSE");
181 Ok(vec![
182 sr.gen_result_entry(self.rootdse.clone()),
183 sr.gen_success(),
184 ])
185 } else {
186 let (opt_attr, opt_value) = match self.dnre.captures(sr.base.as_str()) {
192 Some(caps) => (
193 caps.name("attr").map(|v| v.as_str().to_string()),
194 caps.name("val").map(|v| v.as_str().to_string()),
195 ),
196 None => {
197 request_error!("LDAP Search failure - invalid basedn");
198 return Err(OperationError::InvalidRequestState);
199 }
200 };
201
202 let req_dn = match (opt_attr, opt_value) {
203 (Some(a), Some(v)) => Some((a, v)),
204 (None, None) => None,
205 _ => {
206 request_error!("LDAP Search failure - invalid rdn");
207 return Err(OperationError::InvalidRequestState);
208 }
209 };
210
211 trace!(rdn = ?req_dn);
212
213 let ext_filter = match (&sr.scope, req_dn) {
216 (LdapSearchScope::Children, Some(_r)) | (LdapSearchScope::OneLevel, Some(_r)) => {
220 return Ok(vec![sr.gen_success()])
221 }
222 (LdapSearchScope::Children, None) | (LdapSearchScope::OneLevel, None) => {
223 Some(LdapFilter::Not(Box::new(LdapFilter::Equality(
225 Attribute::Uuid.to_string(),
226 STR_UUID_DOMAIN_INFO.to_string(),
227 ))))
228 }
229 (LdapSearchScope::Base, Some((a, v)))
232 | (LdapSearchScope::Subtree, Some((a, v))) => Some(LdapFilter::Equality(a, v)),
233 (LdapSearchScope::Base, None) => {
234 Some(LdapFilter::Equality(
236 Attribute::Uuid.to_string(),
237 STR_UUID_DOMAIN_INFO.to_string(),
238 ))
239 }
240 (LdapSearchScope::Subtree, None) => {
241 None
243 }
244 };
245
246 let mut no_attrs = false;
247 let mut all_attrs = false;
248 let mut all_op_attrs = false;
249
250 let attrs_len = sr.attrs.len();
251 if sr.attrs.is_empty() {
252 all_attrs = true;
254 } else if attrs_len < self.max_queryable_attrs {
255 sr.attrs.iter().for_each(|a| {
256 if a == "*" {
257 all_attrs = true;
258 } else if a == "+" {
259 all_attrs = true;
262 all_op_attrs = true;
263 } else if a == "1.1" {
264 if sr.attrs.len() == 1 {
274 no_attrs = true;
275 }
276 }
277 })
278 } else {
279 admin_error!(
280 "Too many LDAP attributes requested. Maximum allowed is {}, while your search query had {}",
281 self.max_queryable_attrs, attrs_len
282 );
283 return Err(OperationError::ResourceLimit);
284 }
285
286 let (k_attrs, l_attrs) = if no_attrs {
288 (None, Vec::with_capacity(0))
290 } else if all_op_attrs {
291 (None, ldap_all_vattrs())
293 } else if all_attrs {
294 let req_attrs: Vec<String> = sr
297 .attrs
298 .iter()
299 .filter_map(|a| {
300 let a_lower = a.to_lowercase();
301
302 if ldap_vattr_map(&a_lower).is_some() {
303 Some(a_lower)
304 } else {
305 None
306 }
307 })
308 .collect();
309
310 (None, req_attrs)
311 } else {
312 let req_attrs: Vec<String> = sr
314 .attrs
315 .iter()
316 .filter_map(|a| {
317 if a == "*" || a == "+" || a == "1.1" {
318 None
319 } else {
320 Some(a.to_lowercase())
321 }
322 })
323 .collect();
324 let mapped_attrs: BTreeSet<_> = req_attrs
327 .iter()
328 .map(|a| Attribute::from(ldap_vattr_map(a).unwrap_or(a)))
329 .collect();
330
331 (Some(mapped_attrs), req_attrs)
332 };
333
334 admin_info!(attr = ?l_attrs, "LDAP Search Request LDAP Attrs");
335 admin_info!(attr = ?k_attrs, "LDAP Search Request Mapped Attrs");
336
337 let ct = duration_from_epoch_now();
338 let mut idm_read = idms.proxy_read().await?;
339 let lfilter = match ext_filter {
343 Some(ext) => LdapFilter::And(vec![
344 sr.filter.clone(),
345 ext,
346 LdapFilter::Not(Box::new(LdapFilter::Or(vec![
347 LdapFilter::Equality(Attribute::Class.to_string(), "classtype".to_string()),
348 LdapFilter::Equality(
349 Attribute::Class.to_string(),
350 "attributetype".to_string(),
351 ),
352 LdapFilter::Equality(
353 Attribute::Class.to_string(),
354 "access_control_profile".to_string(),
355 ),
356 ]))),
357 ]),
358 None => LdapFilter::And(vec![
359 sr.filter.clone(),
360 LdapFilter::Not(Box::new(LdapFilter::Or(vec![
361 LdapFilter::Equality(Attribute::Class.to_string(), "classtype".to_string()),
362 LdapFilter::Equality(
363 Attribute::Class.to_string(),
364 "attributetype".to_string(),
365 ),
366 LdapFilter::Equality(
367 Attribute::Class.to_string(),
368 "access_control_profile".to_string(),
369 ),
370 ]))),
371 ]),
372 };
373
374 admin_info!(filter = ?lfilter, "LDAP Search Filter");
375
376 let ident = idm_read
380 .validate_ldap_session(&uat.effective_session, source, ct)
381 .map_err(|e| {
382 admin_error!("Invalid identity: {:?}", e);
383 e
384 })?;
385 let se = SearchEvent::new_ext_impersonate_uuid(
386 &mut idm_read.qs_read,
387 ident,
388 &lfilter,
389 k_attrs,
390 )
391 .map_err(|e| {
392 admin_error!("failed to create search event -> {:?}", e);
393 e
394 })?;
395
396 let res = idm_read.qs_read.search_ext(&se).map_err(|e| {
397 admin_error!("search failure {:?}", e);
398 e
399 })?;
400
401 let lres: Result<Vec<_>, _> = res
405 .into_iter()
406 .map(|e| {
407 e.to_ldap(
408 &mut idm_read.qs_read,
409 self.basedn.as_str(),
410 all_attrs,
411 &l_attrs,
412 )
413 .map(|r| sr.gen_result_entry(r))
415 })
416 .chain(iter::once(Ok(sr.gen_success())))
417 .collect();
418
419 let lres = lres.map_err(|e| {
420 admin_error!("entry resolve failure {:?}", e);
421 e
422 })?;
423
424 admin_info!(
425 nentries = %lres.len(),
426 "LDAP Search Success -> number of entries"
427 );
428
429 Ok(lres)
430 }
431 }
432
433 async fn do_bind(
434 &self,
435 idms: &IdmServer,
436 dn: &str,
437 pw: &str,
438 ) -> Result<Option<LdapBoundToken>, OperationError> {
439 security_info!(
440 "Attempt LDAP Bind for {}",
441 if dn.is_empty() { "(empty dn)" } else { dn }
442 );
443 let ct = duration_from_epoch_now();
444
445 let mut idm_auth = idms.auth().await?;
446 let target = self.bind_target_from_bind_dn(&mut idm_auth, dn, pw).await?;
447
448 let result = match target {
449 LdapBindTarget::Account(uuid) => {
450 let lae = LdapAuthEvent::from_parts(uuid, pw.to_string())?;
451 idm_auth.auth_ldap(&lae, ct).await?
452 }
453 LdapBindTarget::ApiToken => {
454 let jwsc = JwsCompact::from_str(pw).map_err(|err| {
455 error!(?err, "Invalid JwsCompact supplied as authentication token.");
456 OperationError::NotAuthenticated
457 })?;
458
459 let lae = LdapTokenAuthEvent::from_parts(jwsc)?;
460 idm_auth.token_auth_ldap(&lae, ct).await?
461 }
462 LdapBindTarget::Application(ref app_name, usr_uuid) => {
463 let lae =
464 LdapApplicationAuthEvent::new(app_name.as_str(), usr_uuid, pw.to_string())?;
465 idm_auth.application_auth_ldap(&lae, ct).await?
466 }
467 };
468
469 idm_auth.commit()?;
470
471 if result.is_some() {
472 security_info!(
473 "✅ LDAP Bind success for {} -> {:?}",
474 if dn.is_empty() { "(empty dn)" } else { dn },
475 target
476 );
477 } else {
478 security_info!(
479 "❌ LDAP Bind failure for {} -> {:?}",
480 if dn.is_empty() { "(empty dn)" } else { dn },
481 target
482 );
483 }
484
485 Ok(result)
486 }
487
488 #[instrument(level = "debug", skip_all)]
489 async fn do_compare(
490 &self,
491 idms: &IdmServer,
492 cr: &CompareRequest,
493 uat: &LdapBoundToken,
494 source: Source,
495 ) -> Result<Vec<LdapMsg>, OperationError> {
496 admin_info!("Attempt LDAP CompareRequest for {}", uat.spn);
497
498 let (opt_attr, opt_value) = match self.dnre.captures(cr.entry.as_str()) {
499 Some(caps) => (
500 caps.name("attr").map(|v| v.as_str().to_string()),
501 caps.name("val").map(|v| v.as_str().to_string()),
502 ),
503 None => {
504 request_error!("LDAP Search failure - invalid basedn");
505 return Err(OperationError::InvalidRequestState);
506 }
507 };
508
509 let ext_filter = match (opt_attr, opt_value) {
510 (Some(a), Some(v)) => LdapFilter::Equality(a, v),
511 _ => {
512 request_error!("LDAP Search failure - invalid rdn");
513 return Err(OperationError::InvalidRequestState);
514 }
515 };
516
517 let ct = duration_from_epoch_now();
518 let mut idm_read = idms.proxy_read().await?;
519 let lfilter = LdapFilter::And(vec![
523 ext_filter.clone(),
524 LdapFilter::Equality(cr.atype.clone(), cr.val.clone()),
525 LdapFilter::Not(Box::new(LdapFilter::Or(vec![
526 LdapFilter::Equality(Attribute::Class.to_string(), "classtype".to_string()),
527 LdapFilter::Equality(Attribute::Class.to_string(), "attributetype".to_string()),
528 LdapFilter::Equality(
529 Attribute::Class.to_string(),
530 "access_control_profile".to_string(),
531 ),
532 ]))),
533 ]);
534
535 admin_info!(filter = ?lfilter, "LDAP Compare Filter");
536
537 let ident = idm_read
539 .validate_ldap_session(&uat.effective_session, source, ct)
540 .map_err(|e| {
541 admin_error!("Invalid identity: {:?}", e);
542 e
543 })?;
544
545 let f = Filter::from_ldap_ro(&ident, &lfilter, &mut idm_read.qs_read)?;
546 let filter_orig = f
547 .validate(idm_read.qs_read.get_schema())
548 .map_err(OperationError::SchemaViolation)?;
549 let filter = filter_orig.clone().into_ignore_hidden();
550
551 let ee = ExistsEvent {
552 ident: ident.clone(),
553 filter,
554 filter_orig,
555 };
556
557 let res = idm_read.qs_read.exists(&ee).map_err(|e| {
558 admin_error!("call to exists failure {:?}", e);
559 e
560 })?;
561
562 if res {
563 admin_info!("LDAP Compare -> True");
564 return Ok(vec![cr.gen_compare_true()]);
565 }
566
567 let lfilter = LdapFilter::And(vec![
569 ext_filter,
570 LdapFilter::Not(Box::new(LdapFilter::Or(vec![
571 LdapFilter::Equality(Attribute::Class.to_string(), "classtype".to_string()),
572 LdapFilter::Equality(Attribute::Class.to_string(), "attributetype".to_string()),
573 LdapFilter::Equality(
574 Attribute::Class.to_string(),
575 "access_control_profile".to_string(),
576 ),
577 ]))),
578 ]);
579 let f = Filter::from_ldap_ro(&ident, &lfilter, &mut idm_read.qs_read)?;
580 let filter_orig = f
581 .validate(idm_read.qs_read.get_schema())
582 .map_err(OperationError::SchemaViolation)?;
583 let filter = filter_orig.clone().into_ignore_hidden();
584 let ee = ExistsEvent {
585 ident,
586 filter,
587 filter_orig,
588 };
589
590 let res = idm_read.qs_read.exists(&ee).map_err(|e| {
591 admin_error!("call to exists failure {:?}", e);
592 e
593 })?;
594
595 if res {
596 admin_info!("LDAP Compare -> False");
597 return Ok(vec![cr.gen_compare_false()]);
598 }
599
600 Ok(vec![
601 cr.gen_error(LdapResultCode::NoSuchObject, "".to_string())
602 ])
603 }
604
605 pub async fn do_op(
606 &self,
607 idms: &IdmServer,
608 server_op: ServerOps,
609 uat: Option<LdapBoundToken>,
610 ip_addr: IpAddr,
611 eventid: Uuid,
612 ) -> Result<LdapResponseState, OperationError> {
613 let source = Source::Ldaps(ip_addr);
614
615 match server_op {
616 ServerOps::SimpleBind(sbr) => self
617 .do_bind(idms, sbr.dn.as_str(), sbr.pw.as_str())
618 .await
619 .map(|r| match r {
620 Some(lbt) => LdapResponseState::Bind(lbt, sbr.gen_success()),
621 None => LdapResponseState::Respond(sbr.gen_invalid_cred()),
622 })
623 .or_else(|e| {
624 let (rc, msg) = operationerr_to_ldapresultcode(e);
625 Ok(LdapResponseState::Respond(sbr.gen_error(rc, msg)))
626 }),
627 ServerOps::Search(sr) => match uat {
628 Some(u) => self
629 .do_search(idms, &sr, &u, source)
630 .await
631 .map(LdapResponseState::MultiPartResponse)
632 .or_else(|e| {
633 let (rc, msg) = operationerr_to_ldapresultcode(e);
634 Ok(LdapResponseState::Respond(sr.gen_error(rc, msg)))
635 }),
636 None => {
637 let lbt = match self.do_bind(idms, "", "").await {
640 Ok(Some(lbt)) => lbt,
641 Ok(None) => {
642 return Ok(LdapResponseState::Respond(
643 sr.gen_error(LdapResultCode::InvalidCredentials, "".to_string()),
644 ))
645 }
646 Err(e) => {
647 let (rc, msg) = operationerr_to_ldapresultcode(e);
648 return Ok(LdapResponseState::Respond(sr.gen_error(rc, msg)));
649 }
650 };
651 self.do_search(idms, &sr, &lbt, Source::Internal)
653 .await
654 .map(|r| LdapResponseState::BindMultiPartResponse(lbt, r))
655 .or_else(|e| {
656 let (rc, msg) = operationerr_to_ldapresultcode(e);
657 Ok(LdapResponseState::Respond(sr.gen_error(rc, msg)))
658 })
659 }
660 },
661 ServerOps::Unbind(_) => {
662 Ok(LdapResponseState::Unbind)
664 }
665 ServerOps::Compare(cr) => match uat {
666 Some(u) => self
667 .do_compare(idms, &cr, &u, source)
668 .await
669 .map(LdapResponseState::MultiPartResponse)
670 .or_else(|e| {
671 let (rc, msg) = operationerr_to_ldapresultcode(e);
672 Ok(LdapResponseState::Respond(cr.gen_error(rc, msg)))
673 }),
674 None => {
675 let lbt = match self.do_bind(idms, "", "").await {
678 Ok(Some(lbt)) => lbt,
679 Ok(None) => {
680 return Ok(LdapResponseState::Respond(
681 cr.gen_error(LdapResultCode::InvalidCredentials, "".to_string()),
682 ))
683 }
684 Err(e) => {
685 let (rc, msg) = operationerr_to_ldapresultcode(e);
686 return Ok(LdapResponseState::Respond(cr.gen_error(rc, msg)));
687 }
688 };
689 self.do_compare(idms, &cr, &lbt, Source::Internal)
691 .await
692 .map(|r| LdapResponseState::BindMultiPartResponse(lbt, r))
693 .or_else(|e| {
694 let (rc, msg) = operationerr_to_ldapresultcode(e);
695 Ok(LdapResponseState::Respond(cr.gen_error(rc, msg)))
696 })
697 }
698 },
699 ServerOps::Whoami(wr) => match uat {
700 Some(u) => Ok(LdapResponseState::Respond(
701 wr.gen_success(format!("u: {}", u.spn).as_str()),
702 )),
703 None => Ok(LdapResponseState::Respond(
704 wr.gen_operror(format!("Unbound Connection {eventid}").as_str()),
705 )),
706 },
707 } }
709
710 async fn bind_target_from_bind_dn(
711 &self,
712 idm_auth: &mut IdmServerAuthTransaction<'_>,
713 dn: &str,
714 pw: &str,
715 ) -> Result<LdapBindTarget, OperationError> {
716 if dn.is_empty() {
717 if pw.is_empty() {
718 return Ok(LdapBindTarget::Account(UUID_ANONYMOUS));
719 } else {
720 return Ok(LdapBindTarget::ApiToken);
722 }
723 } else if dn == "dn=token" {
724 return Ok(LdapBindTarget::ApiToken);
729 }
730
731 if let Some(captures) = self.binddnre.captures(dn) {
732 if let Some(usr) = captures.name("val") {
733 let usr = usr.as_str();
734
735 if usr.is_empty() {
736 error!("Failed to parse user name from bind DN, it is empty (capture group is {:#?})", captures.name("val"));
737 return Err(OperationError::NoMatchingEntries);
738 }
739
740 let usr_uuid = idm_auth.qs_read.name_to_uuid(usr).map_err(|e| {
741 error!(err = ?e, ?usr, "Error resolving rdn to target");
742 e
743 })?;
744
745 if let Some(app) = captures.name("app") {
746 let app = app.as_str();
747
748 if app.is_empty() {
749 error!("Failed to parse application name from bind DN, it is empty (capture group is {:#?})", captures.name("app"));
750 return Err(OperationError::NoMatchingEntries);
751 }
752
753 return Ok(LdapBindTarget::Application(app.to_string(), usr_uuid));
754 }
755
756 return Ok(LdapBindTarget::Account(usr_uuid));
757 }
758 }
759
760 error!(
761 binddn = ?dn,
762 "Failed to parse bind DN - check the basedn and app attribute if present are correct. Examples: name=tobias,app=lounge,{} OR name=ellie,{} OR name=claire,app=table OR name=william ", self.basedn, self.basedn
763 );
764
765 Err(OperationError::NoMatchingEntries)
766 }
767}
768
769fn ldap_domain_to_dc(input: &str) -> String {
770 let mut output: String = String::new();
771 input.split('.').for_each(|dc| {
772 output.push_str("dc=");
773 output.push_str(dc);
774 #[allow(clippy::single_char_pattern, clippy::single_char_add_str)]
775 output.push_str(",");
776 });
777 output.pop();
779 output
780}
781
782fn operationerr_to_ldapresultcode(e: OperationError) -> (LdapResultCode, String) {
783 match e {
784 OperationError::InvalidRequestState => {
785 (LdapResultCode::ConstraintViolation, "".to_string())
786 }
787 OperationError::InvalidAttributeName(s) | OperationError::InvalidAttribute(s) => {
788 (LdapResultCode::InvalidAttributeSyntax, s)
789 }
790 OperationError::SchemaViolation(se) => {
791 (LdapResultCode::UnwillingToPerform, format!("{se:?}"))
792 }
793 e => (LdapResultCode::Other, format!("{e:?}")),
794 }
795}
796
797#[inline]
798pub(crate) fn ldap_all_vattrs() -> Vec<String> {
799 vec![
800 ATTR_CN.to_string(),
801 ATTR_EMAIL.to_string(),
802 ATTR_LDAP_EMAIL_ADDRESS.to_string(),
803 LDAP_ATTR_DN.to_string(),
804 LDAP_ATTR_EMAIL_ALTERNATIVE.to_string(),
805 LDAP_ATTR_EMAIL_PRIMARY.to_string(),
806 LDAP_ATTR_ENTRYDN.to_string(),
807 LDAP_ATTR_ENTRYUUID.to_string(),
808 LDAP_ATTR_KEYS.to_string(),
809 LDAP_ATTR_MAIL_ALTERNATIVE.to_string(),
810 LDAP_ATTR_MAIL_PRIMARY.to_string(),
811 ATTR_OBJECTCLASS.to_string(),
812 ATTR_LDAP_SSHPUBLICKEY.to_string(),
813 ATTR_UIDNUMBER.to_string(),
814 ATTR_UID.to_string(),
815 ATTR_GECOS.to_string(),
816 ATTR_HOME_DIRECTORY.to_string(),
817 ]
818}
819
820#[inline]
821pub(crate) fn ldap_vattr_map(input: &str) -> Option<&str> {
822 match input {
829 ATTR_CN | ATTR_UID | LDAP_ATTR_ENTRYDN | LDAP_ATTR_DN => Some(ATTR_NAME),
835 ATTR_GECOS => Some(ATTR_DISPLAYNAME),
836 ATTR_EMAIL => Some(ATTR_MAIL),
837 ATTR_LDAP_EMAIL_ADDRESS => Some(ATTR_MAIL),
838 LDAP_ATTR_EMAIL_ALTERNATIVE => Some(ATTR_MAIL),
839 LDAP_ATTR_EMAIL_PRIMARY => Some(ATTR_MAIL),
840 LDAP_ATTR_ENTRYUUID => Some(ATTR_UUID),
841 LDAP_ATTR_KEYS => Some(ATTR_SSH_PUBLICKEY),
842 LDAP_ATTR_MAIL_ALTERNATIVE => Some(ATTR_MAIL),
843 LDAP_ATTR_MAIL_PRIMARY => Some(ATTR_MAIL),
844 ATTR_OBJECTCLASS => Some(ATTR_CLASS),
845 ATTR_LDAP_SSHPUBLICKEY => Some(ATTR_SSH_PUBLICKEY), ATTR_UIDNUMBER => Some(ATTR_GIDNUMBER), ATTR_HOME_DIRECTORY => Some(ATTR_UUID),
848 _ => None,
849 }
850}
851
852#[inline]
853pub(crate) fn ldap_attr_filter_map(input: &str) -> Attribute {
854 let a_lower = input.to_lowercase();
855 Attribute::from(ldap_vattr_map(&a_lower).unwrap_or(a_lower.as_str()))
856}
857
858#[cfg(test)]
859mod tests {
860 use crate::prelude::*;
861
862 use compact_jwt::{dangernoverify::JwsDangerReleaseWithoutVerify, JwsVerifier};
863 use hashbrown::HashSet;
864 use kanidm_proto::internal::ApiToken;
865 use ldap3_proto::proto::{
866 LdapFilter, LdapMsg, LdapOp, LdapResultCode, LdapSearchScope, LdapSubstringFilter,
867 };
868 use ldap3_proto::simple::*;
869
870 use super::{LdapServer, LdapSession};
871 use crate::idm::application::GenerateApplicationPasswordEvent;
872 use crate::idm::event::{LdapApplicationAuthEvent, UnixPasswordChangeEvent};
873 use crate::idm::serviceaccount::GenerateApiTokenEvent;
874
875 const TEST_PASSWORD: &str = "ntaoeuntnaoeuhraohuercahu😍";
876
877 #[idm_test]
878 async fn test_ldap_simple_bind(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
879 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
880
881 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
882 let me_posix = ModifyEvent::new_internal_invalid(
884 filter!(f_eq(Attribute::Name, PartialValue::new_iname("admin"))),
885 ModifyList::new_list(vec![
886 Modify::Present(Attribute::Class, EntryClass::PosixAccount.into()),
887 Modify::Present(Attribute::GidNumber, Value::new_uint32(2001)),
888 ]),
889 );
890 assert!(idms_prox_write.qs_write.modify(&me_posix).is_ok());
891
892 let pce = UnixPasswordChangeEvent::new_internal(UUID_ADMIN, TEST_PASSWORD);
893
894 assert!(idms_prox_write.set_unix_account_password(&pce).is_ok());
895 assert!(idms_prox_write.commit().is_ok()); let admin_t = ldaps
900 .do_bind(idms, "admin", TEST_PASSWORD)
901 .await
902 .unwrap()
903 .unwrap();
904 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
905 let admin_t = ldaps
906 .do_bind(idms, "admin@example.com", TEST_PASSWORD)
907 .await
908 .unwrap()
909 .unwrap();
910 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
911
912 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
915 let disallow_unix_pw_flag = ModifyEvent::new_internal_invalid(
916 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_DOMAIN_INFO))),
917 ModifyList::new_purge_and_set(Attribute::LdapAllowUnixPwBind, Value::Bool(false)),
918 );
919 assert!(idms_prox_write
920 .qs_write
921 .modify(&disallow_unix_pw_flag)
922 .is_ok());
923 assert!(idms_prox_write.commit().is_ok());
924 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
925 assert_eq!(
926 anon_t.effective_session,
927 LdapSession::UnixBind(UUID_ANONYMOUS)
928 );
929 assert!(
930 ldaps.do_bind(idms, "", "test").await.unwrap_err() == OperationError::NotAuthenticated
931 );
932 let admin_t = ldaps.do_bind(idms, "admin", TEST_PASSWORD).await.unwrap();
933 assert!(admin_t.is_none());
934
935 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
937 let allow_unix_pw_flag = ModifyEvent::new_internal_invalid(
938 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_DOMAIN_INFO))),
939 ModifyList::new_purge_and_set(Attribute::LdapAllowUnixPwBind, Value::Bool(true)),
940 );
941 assert!(idms_prox_write.qs_write.modify(&allow_unix_pw_flag).is_ok());
942 assert!(idms_prox_write.commit().is_ok());
943
944 let admin_t = ldaps
946 .do_bind(idms, "admin", TEST_PASSWORD)
947 .await
948 .unwrap()
949 .unwrap();
950 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
951 let admin_t = ldaps
952 .do_bind(idms, "admin@example.com", TEST_PASSWORD)
953 .await
954 .unwrap()
955 .unwrap();
956 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
957 let admin_t = ldaps
958 .do_bind(idms, STR_UUID_ADMIN, TEST_PASSWORD)
959 .await
960 .unwrap()
961 .unwrap();
962 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
963 let admin_t = ldaps
964 .do_bind(idms, "name=admin,dc=example,dc=com", TEST_PASSWORD)
965 .await
966 .unwrap()
967 .unwrap();
968 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
969 let admin_t = ldaps
970 .do_bind(
971 idms,
972 "spn=admin@example.com,dc=example,dc=com",
973 TEST_PASSWORD,
974 )
975 .await
976 .unwrap()
977 .unwrap();
978 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
979 let admin_t = ldaps
980 .do_bind(
981 idms,
982 format!("uuid={STR_UUID_ADMIN},dc=example,dc=com").as_str(),
983 TEST_PASSWORD,
984 )
985 .await
986 .unwrap()
987 .unwrap();
988 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
989
990 let admin_t = ldaps
991 .do_bind(idms, "name=admin", TEST_PASSWORD)
992 .await
993 .unwrap()
994 .unwrap();
995 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
996 let admin_t = ldaps
997 .do_bind(idms, "spn=admin@example.com", TEST_PASSWORD)
998 .await
999 .unwrap()
1000 .unwrap();
1001 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
1002 let admin_t = ldaps
1003 .do_bind(
1004 idms,
1005 format!("uuid={STR_UUID_ADMIN}").as_str(),
1006 TEST_PASSWORD,
1007 )
1008 .await
1009 .unwrap()
1010 .unwrap();
1011 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
1012
1013 let admin_t = ldaps
1014 .do_bind(idms, "admin,dc=example,dc=com", TEST_PASSWORD)
1015 .await
1016 .unwrap()
1017 .unwrap();
1018 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
1019 let admin_t = ldaps
1020 .do_bind(idms, "admin@example.com,dc=example,dc=com", TEST_PASSWORD)
1021 .await
1022 .unwrap()
1023 .unwrap();
1024 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
1025 let admin_t = ldaps
1026 .do_bind(
1027 idms,
1028 format!("{STR_UUID_ADMIN},dc=example,dc=com").as_str(),
1029 TEST_PASSWORD,
1030 )
1031 .await
1032 .unwrap()
1033 .unwrap();
1034 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
1035
1036 assert!(ldaps
1038 .do_bind(idms, "admin", "test")
1039 .await
1040 .unwrap()
1041 .is_none());
1042
1043 assert!(ldaps
1045 .do_bind(
1046 idms,
1047 "spn=admin@example.com,dc=clownshoes,dc=example,dc=com",
1048 TEST_PASSWORD
1049 )
1050 .await
1051 .is_err());
1052 assert!(ldaps
1053 .do_bind(
1054 idms,
1055 "spn=claire@example.com,dc=example,dc=com",
1056 TEST_PASSWORD
1057 )
1058 .await
1059 .is_err());
1060 assert!(ldaps
1061 .do_bind(idms, ",dc=example,dc=com", TEST_PASSWORD)
1062 .await
1063 .is_err());
1064 assert!(ldaps
1065 .do_bind(idms, "dc=example,dc=com", TEST_PASSWORD)
1066 .await
1067 .is_err());
1068
1069 assert!(ldaps.do_bind(idms, "claire", "test").await.is_err());
1070 }
1071
1072 #[idm_test]
1073 async fn test_ldap_application_dnre(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
1074 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
1075
1076 let testdn = format!("app=app1,{0}", ldaps.basedn);
1077 let captures = ldaps.dnre.captures(testdn.as_str()).unwrap();
1078 assert!(captures.name("app").is_some());
1079 assert!(captures.name("attr").is_none());
1080 assert!(captures.name("val").is_none());
1081
1082 let testdn = format!("uid=foo,app=app1,{0}", ldaps.basedn);
1083 let captures = ldaps.dnre.captures(testdn.as_str()).unwrap();
1084 assert!(captures.name("app").is_some());
1085 assert!(captures.name("attr").is_some());
1086 assert!(captures.name("val").is_some());
1087
1088 let testdn = format!("uid=foo,{0}", ldaps.basedn);
1089 let captures = ldaps.dnre.captures(testdn.as_str()).unwrap();
1090 assert!(captures.name("app").is_none());
1091 assert!(captures.name("attr").is_some());
1092 assert!(captures.name("val").is_some());
1093 }
1094
1095 #[idm_test]
1096 async fn test_ldap_application_search(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
1097 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
1098
1099 let usr_uuid = Uuid::new_v4();
1100 let grp_uuid = Uuid::new_v4();
1101 let app_uuid = Uuid::new_v4();
1102 let app_name = "testapp1";
1103
1104 {
1106 let e1 = entry_init!(
1107 (Attribute::Class, EntryClass::Object.to_value()),
1108 (Attribute::Class, EntryClass::Account.to_value()),
1109 (Attribute::Class, EntryClass::Person.to_value()),
1110 (Attribute::Name, Value::new_iname("testperson1")),
1111 (Attribute::Uuid, Value::Uuid(usr_uuid)),
1112 (Attribute::Description, Value::new_utf8s("testperson1")),
1113 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
1114 );
1115
1116 let e2 = entry_init!(
1117 (Attribute::Class, EntryClass::Object.to_value()),
1118 (Attribute::Class, EntryClass::Group.to_value()),
1119 (Attribute::Name, Value::new_iname("testgroup1")),
1120 (Attribute::Uuid, Value::Uuid(grp_uuid))
1121 );
1122
1123 let e3 = entry_init!(
1124 (Attribute::Class, EntryClass::Object.to_value()),
1125 (Attribute::Class, EntryClass::Account.to_value()),
1126 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
1127 (Attribute::Class, EntryClass::Application.to_value()),
1128 (Attribute::DisplayName, Value::new_utf8s("Application")),
1129 (Attribute::Name, Value::new_iname(app_name)),
1130 (Attribute::Uuid, Value::Uuid(app_uuid)),
1131 (Attribute::LinkedGroup, Value::Refer(grp_uuid))
1132 );
1133
1134 let ct = duration_from_epoch_now();
1135 let mut server_txn = idms.proxy_write(ct).await.unwrap();
1136 assert!(server_txn
1137 .qs_write
1138 .internal_create(vec![e1, e2, e3])
1139 .and_then(|_| server_txn.commit())
1140 .is_ok());
1141 }
1142
1143 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
1145 assert_eq!(
1146 anon_t.effective_session,
1147 LdapSession::UnixBind(UUID_ANONYMOUS)
1148 );
1149
1150 let sr = SearchRequest {
1152 msgid: 1,
1153 base: format!("app={app_name},dc=example,dc=com"),
1154 scope: LdapSearchScope::Subtree,
1155 filter: LdapFilter::Present(Attribute::ObjectClass.to_string()),
1156 attrs: vec!["*".to_string()],
1157 };
1158
1159 let r1 = ldaps
1160 .do_search(idms, &sr, &anon_t, Source::Internal)
1161 .await
1162 .unwrap();
1163
1164 let sr = SearchRequest {
1165 msgid: 1,
1166 base: "dc=example,dc=com".to_string(),
1167 scope: LdapSearchScope::Subtree,
1168 filter: LdapFilter::Present(Attribute::ObjectClass.to_string()),
1169 attrs: vec!["*".to_string()],
1170 };
1171
1172 let r2 = ldaps
1173 .do_search(idms, &sr, &anon_t, Source::Internal)
1174 .await
1175 .unwrap();
1176 assert!(!r1.is_empty());
1177 assert_eq!(r1.len(), r2.len());
1178 }
1179
1180 #[idm_test]
1181 async fn test_ldap_spn_search(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
1182 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
1183
1184 let usr_uuid = Uuid::new_v4();
1185 let usr_name = "panko";
1186
1187 {
1189 let e1: Entry<EntryInit, EntryNew> = entry_init!(
1190 (Attribute::Class, EntryClass::Object.to_value()),
1191 (Attribute::Class, EntryClass::Account.to_value()),
1192 (Attribute::Class, EntryClass::PosixAccount.to_value()),
1193 (Attribute::Class, EntryClass::Person.to_value()),
1194 (Attribute::Name, Value::new_iname(usr_name)),
1195 (Attribute::Uuid, Value::Uuid(usr_uuid)),
1196 (Attribute::DisplayName, Value::new_utf8s(usr_name))
1197 );
1198
1199 let ct = duration_from_epoch_now();
1200 let mut server_txn = idms.proxy_write(ct).await.unwrap();
1201
1202 server_txn
1204 .qs_write
1205 .internal_modify_uuid(
1206 UUID_IDM_UNIX_AUTHENTICATION_READ,
1207 &ModifyList::new_append(Attribute::Member, Value::Refer(UUID_ANONYMOUS)),
1208 )
1209 .expect("Unable to modify UNIX_AUTHENTICATION_READ group");
1210
1211 assert!(server_txn
1212 .qs_write
1213 .internal_create(vec![e1])
1214 .and_then(|_| server_txn.commit())
1215 .is_ok());
1216 }
1217
1218 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
1220 assert_eq!(
1221 anon_t.effective_session,
1222 LdapSession::UnixBind(UUID_ANONYMOUS)
1223 );
1224
1225 let sr = SearchRequest {
1227 msgid: 1,
1228 base: "dc=example,dc=com".to_string(),
1229 scope: LdapSearchScope::Subtree,
1230 filter: LdapFilter::Or(vec![
1231 LdapFilter::Equality(Attribute::Name.to_string(), usr_name.to_string()),
1232 LdapFilter::Equality(Attribute::Spn.to_string(), usr_name.to_string()),
1233 ]),
1234 attrs: vec!["*".to_string()],
1235 };
1236
1237 let result = ldaps
1238 .do_search(idms, &sr, &anon_t, Source::Internal)
1239 .await
1240 .map(|r| {
1241 r.into_iter()
1242 .filter(|r| matches!(r.op, LdapOp::SearchResultEntry(_)))
1243 .collect::<Vec<_>>()
1244 })
1245 .unwrap();
1246
1247 assert!(!result.is_empty());
1248
1249 let sr = SearchRequest {
1250 msgid: 1,
1251 base: "dc=example,dc=com".to_string(),
1252 scope: LdapSearchScope::Subtree,
1253 filter: LdapFilter::And(vec![
1254 LdapFilter::Equality(Attribute::Name.to_string(), usr_name.to_string()),
1255 LdapFilter::Equality(Attribute::Spn.to_string(), usr_name.to_string()),
1256 ]),
1257 attrs: vec!["*".to_string()],
1258 };
1259
1260 let empty_result = ldaps
1261 .do_search(idms, &sr, &anon_t, Source::Internal)
1262 .await
1263 .map(|r| {
1264 r.into_iter()
1265 .filter(|r| matches!(r.op, LdapOp::SearchResultEntry(_)))
1266 .collect::<Vec<_>>()
1267 })
1268 .unwrap();
1269
1270 assert!(empty_result.is_empty());
1271 }
1272
1273 #[idm_test]
1274 async fn test_ldap_application_bind(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
1275 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
1276
1277 let usr_uuid = Uuid::new_v4();
1278 let grp_uuid = Uuid::new_v4();
1279 let app_uuid = Uuid::new_v4();
1280
1281 {
1283 let e1 = entry_init!(
1284 (Attribute::Class, EntryClass::Object.to_value()),
1285 (Attribute::Class, EntryClass::Account.to_value()),
1286 (Attribute::Class, EntryClass::Person.to_value()),
1287 (Attribute::Name, Value::new_iname("testperson1")),
1288 (Attribute::Uuid, Value::Uuid(usr_uuid)),
1289 (Attribute::Description, Value::new_utf8s("testperson1")),
1290 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
1291 );
1292
1293 let e2 = entry_init!(
1294 (Attribute::Class, EntryClass::Object.to_value()),
1295 (Attribute::Class, EntryClass::Group.to_value()),
1296 (Attribute::Name, Value::new_iname("testgroup1")),
1297 (Attribute::Uuid, Value::Uuid(grp_uuid))
1298 );
1299
1300 let e3 = entry_init!(
1301 (Attribute::Class, EntryClass::Object.to_value()),
1302 (Attribute::Class, EntryClass::Account.to_value()),
1303 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
1304 (Attribute::Class, EntryClass::Application.to_value()),
1305 (Attribute::DisplayName, Value::new_utf8s("Application")),
1306 (Attribute::Name, Value::new_iname("testapp1")),
1307 (Attribute::Uuid, Value::Uuid(app_uuid)),
1308 (Attribute::LinkedGroup, Value::Refer(grp_uuid))
1309 );
1310
1311 let ct = duration_from_epoch_now();
1312 let mut server_txn = idms.proxy_write(ct).await.unwrap();
1313 assert!(server_txn
1314 .qs_write
1315 .internal_create(vec![e1, e2, e3])
1316 .and_then(|_| server_txn.commit())
1317 .is_ok());
1318 }
1319
1320 let res = ldaps
1322 .do_bind(idms, "spn=testperson1,app=testapp1,dc=example,dc=com", "")
1323 .await;
1324 assert!(res.is_ok());
1325 assert!(res.unwrap().is_none());
1326
1327 {
1328 let ml = ModifyList::new_append(Attribute::Member, Value::Refer(usr_uuid));
1329 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
1330 assert!(idms_prox_write
1331 .qs_write
1332 .internal_modify_uuid(grp_uuid, &ml)
1333 .is_ok());
1334 assert!(idms_prox_write.commit().is_ok());
1335 }
1336
1337 let res = ldaps
1339 .do_bind(idms, "spn=testperson1,app=testapp1,dc=example,dc=com", "")
1340 .await;
1341 assert!(res.is_ok());
1342 assert!(res.unwrap().is_none());
1343
1344 let pass1: String;
1345 let pass2: String;
1346 let pass3: String;
1347 {
1348 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
1349
1350 let ev = GenerateApplicationPasswordEvent::new_internal(
1351 usr_uuid,
1352 app_uuid,
1353 "apppwd1".to_string(),
1354 );
1355 (pass1, _) = idms_prox_write
1356 .generate_application_password(&ev)
1357 .expect("Failed to generate application password");
1358
1359 let ev = GenerateApplicationPasswordEvent::new_internal(
1360 usr_uuid,
1361 app_uuid,
1362 "apppwd2".to_string(),
1363 );
1364 (pass2, _) = idms_prox_write
1365 .generate_application_password(&ev)
1366 .expect("Failed to generate application password");
1367
1368 assert!(idms_prox_write.commit().is_ok());
1369
1370 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
1372 let ev = GenerateApplicationPasswordEvent::new_internal(
1373 usr_uuid,
1374 app_uuid,
1375 "apppwd2".to_string(),
1376 );
1377 (pass3, _) = idms_prox_write
1378 .generate_application_password(&ev)
1379 .expect("Failed to generate application password");
1380 assert!(idms_prox_write.commit().is_ok());
1381 }
1382
1383 let res = ldaps
1385 .do_bind(
1386 idms,
1387 "spn=testperson1,app=testapp1,dc=example,dc=com",
1388 pass1.as_str(),
1389 )
1390 .await;
1391 assert!(res.is_ok());
1392 assert!(res.unwrap().is_some());
1393
1394 let res = ldaps
1396 .do_bind(
1397 idms,
1398 "spn=testperson1,app=testapp1,dc=example,dc=com",
1399 pass2.as_str(),
1400 )
1401 .await;
1402 assert!(res.is_ok());
1403 assert!(res.unwrap().is_none());
1404
1405 let res = ldaps
1407 .do_bind(
1408 idms,
1409 "spn=testperson1,app=testapp1,dc=example,dc=com",
1410 pass3.as_str(),
1411 )
1412 .await;
1413 assert!(res.is_ok());
1414 assert!(res.unwrap().is_some());
1415
1416 let res = ldaps
1418 .do_bind(
1419 idms,
1420 "spn=testperson1,app=testapp1,dc=example,dc=com",
1421 "FOO",
1422 )
1423 .await;
1424 assert!(res.is_ok());
1425 assert!(res.unwrap().is_none());
1426 }
1427
1428 #[idm_test]
1429 async fn test_ldap_application_linked_group(
1430 idms: &IdmServer,
1431 _idms_delayed: &IdmServerDelayed,
1432 ) {
1433 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
1434
1435 let usr_uuid = Uuid::new_v4();
1436 let usr_name = "testuser1";
1437
1438 let grp1_uuid = Uuid::new_v4();
1439 let grp1_name = "testgroup1";
1440 let grp2_uuid = Uuid::new_v4();
1441 let grp2_name = "testgroup2";
1442
1443 let app1_uuid = Uuid::new_v4();
1444 let app1_name = "testapp1";
1445 let app2_uuid = Uuid::new_v4();
1446 let app2_name = "testapp2";
1447
1448 {
1450 let e1 = entry_init!(
1451 (Attribute::Class, EntryClass::Object.to_value()),
1452 (Attribute::Class, EntryClass::Account.to_value()),
1453 (Attribute::Class, EntryClass::Person.to_value()),
1454 (Attribute::Name, Value::new_iname(usr_name)),
1455 (Attribute::Uuid, Value::Uuid(usr_uuid)),
1456 (Attribute::Description, Value::new_utf8s(usr_name)),
1457 (Attribute::DisplayName, Value::new_utf8s(usr_name))
1458 );
1459
1460 let e2 = entry_init!(
1461 (Attribute::Class, EntryClass::Object.to_value()),
1462 (Attribute::Class, EntryClass::Group.to_value()),
1463 (Attribute::Name, Value::new_iname(grp1_name)),
1464 (Attribute::Uuid, Value::Uuid(grp1_uuid)),
1465 (Attribute::Member, Value::Refer(usr_uuid))
1466 );
1467
1468 let e3 = entry_init!(
1469 (Attribute::Class, EntryClass::Object.to_value()),
1470 (Attribute::Class, EntryClass::Group.to_value()),
1471 (Attribute::Name, Value::new_iname(grp2_name)),
1472 (Attribute::Uuid, Value::Uuid(grp2_uuid))
1473 );
1474
1475 let e4 = entry_init!(
1476 (Attribute::Class, EntryClass::Object.to_value()),
1477 (Attribute::Class, EntryClass::Account.to_value()),
1478 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
1479 (Attribute::Class, EntryClass::Application.to_value()),
1480 (Attribute::DisplayName, Value::new_utf8s("Application")),
1481 (Attribute::Name, Value::new_iname(app1_name)),
1482 (Attribute::Uuid, Value::Uuid(app1_uuid)),
1483 (Attribute::LinkedGroup, Value::Refer(grp1_uuid))
1484 );
1485
1486 let e5 = entry_init!(
1487 (Attribute::Class, EntryClass::Object.to_value()),
1488 (Attribute::Class, EntryClass::Account.to_value()),
1489 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
1490 (Attribute::Class, EntryClass::Application.to_value()),
1491 (Attribute::DisplayName, Value::new_utf8s("Application")),
1492 (Attribute::Name, Value::new_iname(app2_name)),
1493 (Attribute::Uuid, Value::Uuid(app2_uuid)),
1494 (Attribute::LinkedGroup, Value::Refer(grp2_uuid))
1495 );
1496
1497 let ct = duration_from_epoch_now();
1498 let mut server_txn = idms.proxy_write(ct).await.unwrap();
1499 assert!(server_txn
1500 .qs_write
1501 .internal_create(vec![e1, e2, e3, e4, e5])
1502 .and_then(|_| server_txn.commit())
1503 .is_ok());
1504 }
1505
1506 let pass_app1: String;
1507 let pass_app2: String;
1508 {
1509 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
1510
1511 let ev = GenerateApplicationPasswordEvent::new_internal(
1512 usr_uuid,
1513 app1_uuid,
1514 "label".to_string(),
1515 );
1516 (pass_app1, _) = idms_prox_write
1517 .generate_application_password(&ev)
1518 .expect("Failed to generate application password");
1519
1520 let ev = GenerateApplicationPasswordEvent::new_internal(
1523 usr_uuid,
1524 app2_uuid,
1525 "label".to_string(),
1526 );
1527 (pass_app2, _) = idms_prox_write
1528 .generate_application_password(&ev)
1529 .expect("Failed to generate application password");
1530
1531 assert!(idms_prox_write.commit().is_ok());
1532 }
1533
1534 let res = ldaps
1536 .do_bind(
1537 idms,
1538 format!("spn={usr_name},app={app1_name},dc=example,dc=com").as_str(),
1539 pass_app1.as_str(),
1540 )
1541 .await;
1542 assert!(res.is_ok());
1543 assert!(res.unwrap().is_some());
1544
1545 let res = ldaps
1547 .do_bind(
1548 idms,
1549 format!("spn={usr_name},app={app2_name},dc=example,dc=com").as_str(),
1550 pass_app2.as_str(),
1551 )
1552 .await;
1553 assert!(res.is_ok());
1554 assert!(res.unwrap().is_none());
1555
1556 {
1558 let ml = ModifyList::new_append(Attribute::Member, Value::Refer(usr_uuid));
1559 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
1560 assert!(idms_prox_write
1561 .qs_write
1562 .internal_modify_uuid(grp2_uuid, &ml)
1563 .is_ok());
1564 assert!(idms_prox_write.commit().is_ok());
1565 }
1566
1567 let res = ldaps
1569 .do_bind(
1570 idms,
1571 format!("spn={usr_name},app={app2_name},dc=example,dc=com").as_str(),
1572 pass_app2.as_str(),
1573 )
1574 .await;
1575 assert!(res.is_ok());
1576 assert!(res.unwrap().is_some());
1577
1578 let res = ldaps
1580 .do_bind(
1581 idms,
1582 format!("spn={usr_name},app={app1_name},dc=example,dc=com").as_str(),
1583 pass_app2.as_str(),
1584 )
1585 .await;
1586 assert!(res.is_ok());
1587 assert!(res.unwrap().is_none());
1588
1589 {
1591 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
1592 let de = DeleteEvent::new_internal_invalid(filter!(f_eq(
1593 Attribute::Uuid,
1594 PartialValue::Uuid(app2_uuid)
1595 )));
1596 assert!(idms_prox_write.qs_write.delete(&de).is_ok());
1597 assert!(idms_prox_write.commit().is_ok());
1598 }
1599
1600 let res = ldaps
1601 .do_bind(
1602 idms,
1603 format!("spn={usr_name},app={app2_name},dc=example,dc=com").as_str(),
1604 pass_app2.as_str(),
1605 )
1606 .await;
1607 assert!(res.is_err());
1608 }
1609
1610 const TEST_CURRENT_TIME: u64 = 6000;
1614 const TEST_NOT_YET_VALID_TIME: u64 = TEST_CURRENT_TIME - 240;
1615 const TEST_VALID_FROM_TIME: u64 = TEST_CURRENT_TIME - 120;
1616 const TEST_EXPIRE_TIME: u64 = TEST_CURRENT_TIME + 120;
1617 const TEST_AFTER_EXPIRY: u64 = TEST_CURRENT_TIME + 240;
1618
1619 async fn set_account_valid_time(idms: &IdmServer, acct: Uuid) {
1620 let mut idms_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
1621
1622 let v_valid_from = Value::new_datetime_epoch(Duration::from_secs(TEST_VALID_FROM_TIME));
1623 let v_expire = Value::new_datetime_epoch(Duration::from_secs(TEST_EXPIRE_TIME));
1624
1625 let me = ModifyEvent::new_internal_invalid(
1626 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(acct))),
1627 ModifyList::new_list(vec![
1628 Modify::Present(Attribute::AccountExpire, v_expire),
1629 Modify::Present(Attribute::AccountValidFrom, v_valid_from),
1630 ]),
1631 );
1632 assert!(idms_write.qs_write.modify(&me).is_ok());
1633 idms_write.commit().expect("Must not fail");
1634 }
1635
1636 #[idm_test]
1637 async fn test_ldap_application_valid_from_expire(
1638 idms: &IdmServer,
1639 _idms_delayed: &IdmServerDelayed,
1640 ) {
1641 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
1642
1643 let usr_uuid = Uuid::new_v4();
1644 let usr_name = "testuser1";
1645
1646 let grp1_uuid = Uuid::new_v4();
1647 let grp1_name = "testgroup1";
1648
1649 let app1_uuid = Uuid::new_v4();
1650 let app1_name = "testapp1";
1651
1652 let pass_app1: String;
1653
1654 {
1656 let e1 = entry_init!(
1657 (Attribute::Class, EntryClass::Object.to_value()),
1658 (Attribute::Class, EntryClass::Account.to_value()),
1659 (Attribute::Class, EntryClass::Person.to_value()),
1660 (Attribute::Name, Value::new_iname(usr_name)),
1661 (Attribute::Uuid, Value::Uuid(usr_uuid)),
1662 (Attribute::Description, Value::new_utf8s(usr_name)),
1663 (Attribute::DisplayName, Value::new_utf8s(usr_name))
1664 );
1665
1666 let e2 = entry_init!(
1667 (Attribute::Class, EntryClass::Object.to_value()),
1668 (Attribute::Class, EntryClass::Group.to_value()),
1669 (Attribute::Name, Value::new_iname(grp1_name)),
1670 (Attribute::Uuid, Value::Uuid(grp1_uuid)),
1671 (Attribute::Member, Value::Refer(usr_uuid))
1672 );
1673
1674 let e3 = entry_init!(
1675 (Attribute::Class, EntryClass::Object.to_value()),
1676 (Attribute::Class, EntryClass::Account.to_value()),
1677 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
1678 (Attribute::Class, EntryClass::Application.to_value()),
1679 (Attribute::DisplayName, Value::new_utf8s("Application")),
1680 (Attribute::Name, Value::new_iname(app1_name)),
1681 (Attribute::Uuid, Value::Uuid(app1_uuid)),
1682 (Attribute::LinkedGroup, Value::Refer(grp1_uuid))
1683 );
1684
1685 let ct = duration_from_epoch_now();
1686 let mut server_txn = idms.proxy_write(ct).await.unwrap();
1687 assert!(server_txn
1688 .qs_write
1689 .internal_create(vec![e1, e2, e3])
1690 .and_then(|_| server_txn.commit())
1691 .is_ok());
1692
1693 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
1694
1695 let ev = GenerateApplicationPasswordEvent::new_internal(
1696 usr_uuid,
1697 app1_uuid,
1698 "label".to_string(),
1699 );
1700 (pass_app1, _) = idms_prox_write
1701 .generate_application_password(&ev)
1702 .expect("Failed to generate application password");
1703
1704 assert!(idms_prox_write.commit().is_ok());
1705 }
1706
1707 let res = ldaps
1709 .do_bind(
1710 idms,
1711 format!("spn={usr_name},app={app1_name},dc=example,dc=com").as_str(),
1712 pass_app1.as_str(),
1713 )
1714 .await;
1715 assert!(res.is_ok());
1716 assert!(res.unwrap().is_some());
1717
1718 set_account_valid_time(idms, usr_uuid).await;
1722
1723 let time_low = Duration::from_secs(TEST_NOT_YET_VALID_TIME);
1724 let time = Duration::from_secs(TEST_CURRENT_TIME);
1725 let time_high = Duration::from_secs(TEST_AFTER_EXPIRY);
1726
1727 let mut idms_auth = idms.auth().await.unwrap();
1728 let lae = LdapApplicationAuthEvent::new(app1_name, usr_uuid, pass_app1)
1729 .expect("Failed to build auth event");
1730
1731 let r1 = idms_auth
1732 .application_auth_ldap(&lae, time_low)
1733 .await
1734 .expect_err("Authentication succeeded");
1735 assert_eq!(r1, OperationError::SessionExpired);
1736
1737 let r1 = idms_auth
1738 .application_auth_ldap(&lae, time)
1739 .await
1740 .expect("Failed auth");
1741 assert!(r1.is_some());
1742
1743 let r1 = idms_auth
1744 .application_auth_ldap(&lae, time_high)
1745 .await
1746 .expect_err("Authentication succeeded");
1747 assert_eq!(r1, OperationError::SessionExpired);
1748 }
1749
1750 macro_rules! assert_entry_contains {
1751 (
1752 $entry:expr,
1753 $dn:expr,
1754 $($item:expr),*
1755 ) => {{
1756 assert_eq!($entry.dn, $dn);
1757 let mut attrs = HashSet::new();
1759 for a in $entry.attributes.iter() {
1760 for v in a.vals.iter() {
1761 attrs.insert((a.atype.as_str(), v.as_slice()));
1762 }
1763 };
1764 info!(?attrs);
1765 $(
1766 warn!("{}", $item.0);
1767 assert!(attrs.contains(&(
1768 $item.0.as_ref(), $item.1.as_bytes()
1769 )));
1770 )*
1771
1772 }};
1773 }
1774
1775 #[idm_test]
1776 async fn test_ldap_virtual_attribute_generation(
1777 idms: &IdmServer,
1778 _idms_delayed: &IdmServerDelayed,
1779 ) {
1780 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
1781
1782 let ssh_ed25519 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAeGW1P6Pc2rPq0XqbRaDKBcXZUPRklo0L1EyR30CwoP william@amethyst";
1783
1784 {
1786 let e1 = entry_init!(
1787 (Attribute::Class, EntryClass::Object.to_value()),
1788 (Attribute::Class, EntryClass::Person.to_value()),
1789 (Attribute::Class, EntryClass::Account.to_value()),
1790 (Attribute::Class, EntryClass::PosixAccount.to_value()),
1791 (Attribute::Name, Value::new_iname("testperson1")),
1792 (
1793 Attribute::Uuid,
1794 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1795 ),
1796 (Attribute::Description, Value::new_utf8s("testperson1")),
1797 (Attribute::DisplayName, Value::new_utf8s("testperson1")),
1798 (Attribute::GidNumber, Value::new_uint32(12345)),
1799 (Attribute::LoginShell, Value::new_iutf8("/bin/zsh")),
1800 (
1801 Attribute::SshPublicKey,
1802 Value::new_sshkey_str("test", ssh_ed25519).expect("Invalid ssh key")
1803 )
1804 );
1805
1806 let mut server_txn = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
1807
1808 server_txn
1810 .qs_write
1811 .internal_modify_uuid(
1812 UUID_IDM_UNIX_AUTHENTICATION_READ,
1813 &ModifyList::new_append(Attribute::Member, Value::Refer(UUID_ANONYMOUS)),
1814 )
1815 .expect("Unable to modify UNIX_AUTHENTICATION_READ group");
1816
1817 let ce = CreateEvent::new_internal(vec![e1]);
1818 assert!(server_txn
1819 .qs_write
1820 .create(&ce)
1821 .and_then(|_| server_txn.commit())
1822 .is_ok());
1823 }
1824
1825 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
1827 assert_eq!(
1828 anon_t.effective_session,
1829 LdapSession::UnixBind(UUID_ANONYMOUS)
1830 );
1831
1832 let sr = SearchRequest {
1834 msgid: 1,
1835 base: "dc=example,dc=com".to_string(),
1836 scope: LdapSearchScope::Subtree,
1837 filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
1838 attrs: vec!["*".to_string()],
1839 };
1840 let r1 = ldaps
1841 .do_search(idms, &sr, &anon_t, Source::Internal)
1842 .await
1843 .unwrap();
1844
1845 assert_eq!(r1.len(), 2);
1847 match &r1[0].op {
1848 LdapOp::SearchResultEntry(lsre) => {
1849 assert_entry_contains!(
1850 lsre,
1851 "spn=testperson1@example.com,dc=example,dc=com",
1852 (Attribute::Class, EntryClass::Object.to_string()),
1853 (Attribute::Class, EntryClass::Person.to_string()),
1854 (Attribute::Class, EntryClass::Account.to_string()),
1855 (Attribute::Class, EntryClass::PosixAccount.to_string()),
1856 (Attribute::DisplayName, "testperson1"),
1857 (Attribute::Name, "testperson1"),
1858 (Attribute::GidNumber, "12345"),
1859 (Attribute::LoginShell, "/bin/zsh"),
1860 (Attribute::SshPublicKey, ssh_ed25519),
1861 (Attribute::Uuid, "cc8e95b4-c24f-4d68-ba54-8bed76f63930")
1862 );
1863 }
1864 _ => panic!("Oh no"),
1865 };
1866
1867 let sr = SearchRequest {
1869 msgid: 1,
1870 base: "dc=example,dc=com".to_string(),
1871 scope: LdapSearchScope::Subtree,
1872 filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
1873 attrs: vec!["+".to_string()],
1874 };
1875 let r1 = ldaps
1876 .do_search(idms, &sr, &anon_t, Source::Internal)
1877 .await
1878 .unwrap();
1879
1880 assert_eq!(r1.len(), 2);
1882 match &r1[0].op {
1883 LdapOp::SearchResultEntry(lsre) => {
1884 assert_entry_contains!(
1885 lsre,
1886 "spn=testperson1@example.com,dc=example,dc=com",
1887 (Attribute::ObjectClass, EntryClass::Object.as_ref()),
1888 (Attribute::ObjectClass, EntryClass::Person.as_ref()),
1889 (Attribute::ObjectClass, EntryClass::Account.as_ref()),
1890 (Attribute::ObjectClass, EntryClass::PosixAccount.as_ref()),
1891 (Attribute::DisplayName, "testperson1"),
1892 (Attribute::Name, "testperson1"),
1893 (Attribute::GidNumber, "12345"),
1894 (Attribute::LoginShell, "/bin/zsh"),
1895 (Attribute::SshPublicKey, ssh_ed25519),
1896 (Attribute::EntryUuid, "cc8e95b4-c24f-4d68-ba54-8bed76f63930"),
1897 (
1898 Attribute::HomeDirectory,
1899 "/home/cc8e95b4-c24f-4d68-ba54-8bed76f63930"
1900 ),
1901 (
1902 Attribute::EntryDn,
1903 "spn=testperson1@example.com,dc=example,dc=com"
1904 ),
1905 (Attribute::UidNumber, "12345"),
1906 (Attribute::Cn, "testperson1"),
1907 (Attribute::LdapKeys, ssh_ed25519)
1908 );
1909 }
1910 _ => panic!("Oh no"),
1911 };
1912
1913 let sr = SearchRequest {
1915 msgid: 1,
1916 base: "dc=example,dc=com".to_string(),
1917 scope: LdapSearchScope::Subtree,
1918 filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
1919 attrs: vec![
1920 LDAP_ATTR_NAME.to_string(),
1921 Attribute::EntryDn.to_string(),
1922 ATTR_LDAP_KEYS.to_string(),
1923 Attribute::UidNumber.to_string(),
1924 ],
1925 };
1926 let r1 = ldaps
1927 .do_search(idms, &sr, &anon_t, Source::Internal)
1928 .await
1929 .unwrap();
1930
1931 assert_eq!(r1.len(), 2);
1933 match &r1[0].op {
1934 LdapOp::SearchResultEntry(lsre) => {
1935 assert_entry_contains!(
1936 lsre,
1937 "spn=testperson1@example.com,dc=example,dc=com",
1938 (Attribute::Name, "testperson1"),
1939 (
1940 Attribute::EntryDn,
1941 "spn=testperson1@example.com,dc=example,dc=com"
1942 ),
1943 (Attribute::UidNumber, "12345"),
1944 (Attribute::LdapKeys, ssh_ed25519)
1945 );
1946 }
1947 _ => panic!("Oh no"),
1948 };
1949 }
1950
1951 #[idm_test]
1952 async fn test_ldap_token_privilege_granting(
1953 idms: &IdmServer,
1954 _idms_delayed: &IdmServerDelayed,
1955 ) {
1956 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
1958
1959 let sr = SearchRequest {
1961 msgid: 1,
1962 base: "dc=example,dc=com".to_string(),
1963 scope: LdapSearchScope::Subtree,
1964 filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
1965 attrs: vec![
1966 LDAP_ATTR_NAME,
1967 LDAP_ATTR_MAIL,
1968 LDAP_ATTR_MAIL_PRIMARY,
1969 LDAP_ATTR_MAIL_ALTERNATIVE,
1970 LDAP_ATTR_EMAIL_PRIMARY,
1971 LDAP_ATTR_EMAIL_ALTERNATIVE,
1972 ]
1973 .into_iter()
1974 .map(|s| s.to_string())
1975 .collect(),
1976 };
1977
1978 let sa_uuid = uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930");
1979
1980 let apitoken = {
1983 let e1 = entry_init!(
1986 (Attribute::Class, EntryClass::Object.to_value()),
1987 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
1988 (Attribute::Class, EntryClass::Account.to_value()),
1989 (Attribute::Uuid, Value::Uuid(sa_uuid)),
1990 (Attribute::Name, Value::new_iname("service_permission_test")),
1991 (
1992 Attribute::DisplayName,
1993 Value::new_utf8s("service_permission_test")
1994 )
1995 );
1996
1997 let e2 = entry_init!(
1999 (Attribute::Class, EntryClass::Object.to_value()),
2000 (Attribute::Class, EntryClass::Person.to_value()),
2001 (Attribute::Class, EntryClass::Account.to_value()),
2002 (Attribute::Class, EntryClass::PosixAccount.to_value()),
2003 (Attribute::Name, Value::new_iname("testperson1")),
2004 (
2005 Attribute::Mail,
2006 Value::EmailAddress("testperson1@example.com".to_string(), true)
2007 ),
2008 (
2009 Attribute::Mail,
2010 Value::EmailAddress("testperson1.alternative@example.com".to_string(), false)
2011 ),
2012 (Attribute::Description, Value::new_utf8s("testperson1")),
2013 (Attribute::DisplayName, Value::new_utf8s("testperson1")),
2014 (Attribute::GidNumber, Value::new_uint32(12345)),
2015 (Attribute::LoginShell, Value::new_iutf8("/bin/zsh"))
2016 );
2017
2018 let ct = duration_from_epoch_now();
2019
2020 let mut server_txn = idms.proxy_write(ct).await.unwrap();
2021
2022 let ce = CreateEvent::new_internal(vec![e1, e2]);
2023 assert!(server_txn.qs_write.create(&ce).is_ok());
2024
2025 server_txn
2027 .qs_write
2028 .internal_modify_uuid(
2029 UUID_IDM_ACCOUNT_MAIL_READ,
2030 &ModifyList::new_append(Attribute::Member, Value::Refer(sa_uuid)),
2031 )
2032 .expect("Unable to modify UNIX_AUTHENTICATION_READ group");
2033
2034 server_txn
2036 .qs_write
2037 .internal_modify_uuid(
2038 UUID_IDM_UNIX_AUTHENTICATION_READ,
2039 &ModifyList::new_append(Attribute::Member, Value::Refer(UUID_ANONYMOUS)),
2040 )
2041 .expect("Unable to modify UNIX_AUTHENTICATION_READ group");
2042
2043 let gte = GenerateApiTokenEvent::new_internal(sa_uuid, "TestToken", None);
2047
2048 let apitoken = server_txn
2049 .service_account_generate_api_token(>e, ct)
2050 .expect("Failed to create new apitoken");
2051
2052 assert!(server_txn.commit().is_ok());
2053
2054 apitoken
2055 };
2056
2057 let anon_lbt = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
2062 assert_eq!(
2063 anon_lbt.effective_session,
2064 LdapSession::UnixBind(UUID_ANONYMOUS)
2065 );
2066
2067 let r1 = ldaps
2068 .do_search(idms, &sr, &anon_lbt, Source::Internal)
2069 .await
2070 .unwrap();
2071 assert_eq!(r1.len(), 2);
2072 match &r1[0].op {
2073 LdapOp::SearchResultEntry(lsre) => {
2074 assert_entry_contains!(
2075 lsre,
2076 "spn=testperson1@example.com,dc=example,dc=com",
2077 (Attribute::Name, "testperson1")
2078 );
2079 }
2080 _ => panic!("Oh no"),
2081 };
2082
2083 let jws_verifier = JwsDangerReleaseWithoutVerify::default();
2085
2086 let apitoken_inner = jws_verifier
2087 .verify(&apitoken)
2088 .unwrap()
2089 .from_json::<ApiToken>()
2090 .unwrap();
2091
2092 let sa_lbt = ldaps
2094 .do_bind(idms, "dn=token", &apitoken.to_string())
2095 .await
2096 .unwrap()
2097 .unwrap();
2098 assert_eq!(
2099 sa_lbt.effective_session,
2100 LdapSession::ApiToken(apitoken_inner.clone())
2101 );
2102
2103 let sa_lbt = ldaps
2105 .do_bind(idms, "", &apitoken.to_string())
2106 .await
2107 .unwrap()
2108 .unwrap();
2109 assert_eq!(
2110 sa_lbt.effective_session,
2111 LdapSession::ApiToken(apitoken_inner)
2112 );
2113
2114 let r1 = ldaps
2116 .do_search(idms, &sr, &sa_lbt, Source::Internal)
2117 .await
2118 .unwrap();
2119 assert_eq!(r1.len(), 2);
2120 match &r1[0].op {
2121 LdapOp::SearchResultEntry(lsre) => {
2122 assert_entry_contains!(
2123 lsre,
2124 "spn=testperson1@example.com,dc=example,dc=com",
2125 (Attribute::Name, "testperson1"),
2126 (Attribute::Mail, "testperson1@example.com"),
2127 (Attribute::Mail, "testperson1.alternative@example.com"),
2128 (LDAP_ATTR_MAIL_PRIMARY, "testperson1@example.com"),
2129 (
2130 LDAP_ATTR_MAIL_ALTERNATIVE,
2131 "testperson1.alternative@example.com"
2132 ),
2133 (LDAP_ATTR_EMAIL_PRIMARY, "testperson1@example.com"),
2134 (
2135 LDAP_ATTR_EMAIL_ALTERNATIVE,
2136 "testperson1.alternative@example.com"
2137 )
2138 );
2139 }
2140 _ => panic!("Oh no"),
2141 };
2142
2143 let sr = SearchRequest {
2146 msgid: 2,
2147 base: "dc=example,dc=com".to_string(),
2148 scope: LdapSearchScope::Subtree,
2149 filter: LdapFilter::And(vec![
2150 LdapFilter::Equality(Attribute::Class.to_string(), "posixAccount".to_string()),
2151 LdapFilter::Substring(
2152 LDAP_ATTR_MAIL.to_string(),
2153 LdapSubstringFilter {
2154 initial: None,
2155 any: vec![],
2156 final_: Some("@example.com".to_string()),
2157 },
2158 ),
2159 ]),
2160 attrs: vec![
2161 LDAP_ATTR_NAME,
2162 LDAP_ATTR_MAIL,
2163 LDAP_ATTR_MAIL_PRIMARY,
2164 LDAP_ATTR_MAIL_ALTERNATIVE,
2165 ]
2166 .into_iter()
2167 .map(|s| s.to_string())
2168 .collect(),
2169 };
2170
2171 let r1 = ldaps
2172 .do_search(idms, &sr, &sa_lbt, Source::Internal)
2173 .await
2174 .unwrap();
2175
2176 assert_eq!(r1.len(), 2);
2177 match &r1[0].op {
2178 LdapOp::SearchResultEntry(lsre) => {
2179 assert_entry_contains!(
2180 lsre,
2181 "spn=testperson1@example.com,dc=example,dc=com",
2182 (Attribute::Name, "testperson1"),
2183 (Attribute::Mail, "testperson1@example.com"),
2184 (Attribute::Mail, "testperson1.alternative@example.com"),
2185 (LDAP_ATTR_MAIL_PRIMARY, "testperson1@example.com"),
2186 (
2187 LDAP_ATTR_MAIL_ALTERNATIVE,
2188 "testperson1.alternative@example.com"
2189 )
2190 );
2191 }
2192 _ => panic!("Oh no"),
2193 };
2194 }
2195
2196 #[idm_test]
2197 async fn test_ldap_virtual_attribute_with_all_attr_search(
2198 idms: &IdmServer,
2199 _idms_delayed: &IdmServerDelayed,
2200 ) {
2201 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
2202
2203 let acct_uuid = uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930");
2204
2205 {
2207 let e1 = entry_init!(
2208 (Attribute::Class, EntryClass::Person.to_value()),
2209 (Attribute::Class, EntryClass::Account.to_value()),
2210 (Attribute::Class, EntryClass::PosixAccount.to_value()),
2211 (Attribute::Name, Value::new_iname("testperson1")),
2212 (Attribute::Uuid, Value::Uuid(acct_uuid)),
2213 (Attribute::Description, Value::new_utf8s("testperson1")),
2214 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2215 );
2216
2217 let mut server_txn = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2218
2219 server_txn
2221 .qs_write
2222 .internal_modify_uuid(
2223 UUID_IDM_UNIX_AUTHENTICATION_READ,
2224 &ModifyList::new_append(Attribute::Member, Value::Refer(UUID_ANONYMOUS)),
2225 )
2226 .expect("Unable to modify UNIX_AUTHENTICATION_READ group");
2227
2228 assert!(server_txn
2229 .qs_write
2230 .internal_create(vec![e1])
2231 .and_then(|_| server_txn.commit())
2232 .is_ok());
2233 }
2234
2235 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
2237 assert_eq!(
2238 anon_t.effective_session,
2239 LdapSession::UnixBind(UUID_ANONYMOUS)
2240 );
2241
2242 let sr = SearchRequest {
2244 msgid: 1,
2245 base: "dc=example,dc=com".to_string(),
2246 scope: LdapSearchScope::Subtree,
2247 filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
2248 attrs: vec![
2249 "*".to_string(),
2250 LDAP_ATTR_NAME.to_string(),
2252 Attribute::EntryUuid.to_string(),
2254 ],
2255 };
2256 let r1 = ldaps
2257 .do_search(idms, &sr, &anon_t, Source::Internal)
2258 .await
2259 .unwrap();
2260
2261 assert_eq!(r1.len(), 2);
2263 match &r1[0].op {
2264 LdapOp::SearchResultEntry(lsre) => {
2265 assert_entry_contains!(
2266 lsre,
2267 "spn=testperson1@example.com,dc=example,dc=com",
2268 (Attribute::Name, "testperson1"),
2269 (Attribute::DisplayName, "testperson1"),
2270 (Attribute::Uuid, "cc8e95b4-c24f-4d68-ba54-8bed76f63930"),
2271 (Attribute::EntryUuid, "cc8e95b4-c24f-4d68-ba54-8bed76f63930")
2272 );
2273 }
2274 _ => panic!("Oh no"),
2275 };
2276 }
2277
2278 #[idm_test]
2280 async fn test_ldap_one_dot_one_attribute(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2281 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
2282
2283 let acct_uuid = uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930");
2284
2285 {
2287 let e1 = entry_init!(
2288 (Attribute::Class, EntryClass::Person.to_value()),
2289 (Attribute::Class, EntryClass::Account.to_value()),
2290 (Attribute::Class, EntryClass::PosixAccount.to_value()),
2291 (Attribute::Name, Value::new_iname("testperson1")),
2292 (Attribute::Uuid, Value::Uuid(acct_uuid)),
2293 (Attribute::Description, Value::new_utf8s("testperson1")),
2294 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2295 );
2296
2297 let mut server_txn = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2298
2299 server_txn
2301 .qs_write
2302 .internal_modify_uuid(
2303 UUID_IDM_UNIX_AUTHENTICATION_READ,
2304 &ModifyList::new_append(Attribute::Member, Value::Refer(UUID_ANONYMOUS)),
2305 )
2306 .expect("Unable to modify UNIX_AUTHENTICATION_READ group");
2307
2308 assert!(server_txn
2309 .qs_write
2310 .internal_create(vec![e1])
2311 .and_then(|_| server_txn.commit())
2312 .is_ok());
2313 }
2314
2315 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
2317 assert_eq!(
2318 anon_t.effective_session,
2319 LdapSession::UnixBind(UUID_ANONYMOUS)
2320 );
2321
2322 let sr = SearchRequest {
2324 msgid: 1,
2325 base: "dc=example,dc=com".to_string(),
2326 scope: LdapSearchScope::Subtree,
2327 filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
2328 attrs: vec!["1.1".to_string()],
2329 };
2330 let r1 = ldaps
2331 .do_search(idms, &sr, &anon_t, Source::Internal)
2332 .await
2333 .unwrap();
2334
2335 assert_eq!(r1.len(), 2);
2337 match &r1[0].op {
2338 LdapOp::SearchResultEntry(lsre) => {
2339 assert_eq!(
2340 lsre.dn.as_str(),
2341 "spn=testperson1@example.com,dc=example,dc=com"
2342 );
2343 assert!(lsre.attributes.is_empty());
2344 }
2345 _ => panic!("Oh no"),
2346 };
2347
2348 let sr = SearchRequest {
2350 msgid: 1,
2351 base: "dc=example,dc=com".to_string(),
2352 scope: LdapSearchScope::Subtree,
2353 filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
2354 attrs: vec![
2355 "1.1".to_string(),
2356 Attribute::EntryUuid.to_string(),
2358 ],
2359 };
2360 let r1 = ldaps
2361 .do_search(idms, &sr, &anon_t, Source::Internal)
2362 .await
2363 .unwrap();
2364
2365 assert_eq!(r1.len(), 2);
2367 match &r1[0].op {
2368 LdapOp::SearchResultEntry(lsre) => {
2369 assert_entry_contains!(
2370 lsre,
2371 "spn=testperson1@example.com,dc=example,dc=com",
2372 (Attribute::EntryUuid, "cc8e95b4-c24f-4d68-ba54-8bed76f63930")
2373 );
2374 }
2375 _ => panic!("Oh no"),
2376 };
2377 }
2378
2379 #[idm_test]
2380 async fn test_ldap_rootdse_basedn_change(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2381 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
2382
2383 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
2384 assert_eq!(
2385 anon_t.effective_session,
2386 LdapSession::UnixBind(UUID_ANONYMOUS)
2387 );
2388
2389 let sr = SearchRequest {
2390 msgid: 1,
2391 base: "".to_string(),
2392 scope: LdapSearchScope::Base,
2393 filter: LdapFilter::Present(Attribute::ObjectClass.to_string()),
2394 attrs: vec!["*".to_string()],
2395 };
2396 let r1 = ldaps
2397 .do_search(idms, &sr, &anon_t, Source::Internal)
2398 .await
2399 .unwrap();
2400
2401 trace!(?r1);
2402
2403 assert_eq!(r1.len(), 2);
2405 match &r1[0].op {
2406 LdapOp::SearchResultEntry(lsre) => {
2407 assert_entry_contains!(
2408 lsre,
2409 "",
2410 (Attribute::ObjectClass, "top"),
2411 ("vendorname", "Kanidm Project"),
2412 ("supportedldapversion", "3"),
2413 ("defaultnamingcontext", "dc=example,dc=com")
2414 );
2415 }
2416 _ => panic!("Oh no"),
2417 };
2418
2419 drop(ldaps);
2420
2421 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2424 let me_posix = ModifyEvent::new_internal_invalid(
2426 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_DOMAIN_INFO))),
2427 ModifyList::new_purge_and_set(
2428 Attribute::DomainLdapBasedn,
2429 Value::new_iutf8("o=kanidmproject"),
2430 ),
2431 );
2432 assert!(idms_prox_write.qs_write.modify(&me_posix).is_ok());
2433
2434 assert!(idms_prox_write.commit().is_ok());
2435
2436 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
2438
2439 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
2440 assert_eq!(
2441 anon_t.effective_session,
2442 LdapSession::UnixBind(UUID_ANONYMOUS)
2443 );
2444
2445 let sr = SearchRequest {
2446 msgid: 1,
2447 base: "".to_string(),
2448 scope: LdapSearchScope::Base,
2449 filter: LdapFilter::Present(Attribute::ObjectClass.to_string()),
2450 attrs: vec!["*".to_string()],
2451 };
2452 let r1 = ldaps
2453 .do_search(idms, &sr, &anon_t, Source::Internal)
2454 .await
2455 .unwrap();
2456
2457 trace!(?r1);
2458
2459 assert_eq!(r1.len(), 2);
2461 match &r1[0].op {
2462 LdapOp::SearchResultEntry(lsre) => {
2463 assert_entry_contains!(
2464 lsre,
2465 "",
2466 (Attribute::ObjectClass, "top"),
2467 ("vendorname", "Kanidm Project"),
2468 ("supportedldapversion", "3"),
2469 ("defaultnamingcontext", "o=kanidmproject")
2470 );
2471 }
2472 _ => panic!("Oh no"),
2473 };
2474 }
2475
2476 #[idm_test]
2477 async fn test_ldap_sssd_compat(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2478 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
2479
2480 let acct_uuid = uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930");
2481
2482 {
2484 let e1 = entry_init!(
2485 (Attribute::Class, EntryClass::Person.to_value()),
2486 (Attribute::Class, EntryClass::Account.to_value()),
2487 (Attribute::Class, EntryClass::PosixAccount.to_value()),
2488 (Attribute::Name, Value::new_iname("testperson1")),
2489 (Attribute::Uuid, Value::Uuid(acct_uuid)),
2490 (Attribute::GidNumber, Value::Uint32(12345)),
2491 (Attribute::Description, Value::new_utf8s("testperson1")),
2492 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2493 );
2494
2495 let mut server_txn = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2496
2497 server_txn
2499 .qs_write
2500 .internal_modify_uuid(
2501 UUID_IDM_UNIX_AUTHENTICATION_READ,
2502 &ModifyList::new_append(Attribute::Member, Value::Refer(UUID_ANONYMOUS)),
2503 )
2504 .expect("Unable to modify UNIX_AUTHENTICATION_READ group");
2505
2506 assert!(server_txn
2507 .qs_write
2508 .internal_create(vec![e1])
2509 .and_then(|_| server_txn.commit())
2510 .is_ok());
2511 }
2512
2513 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
2515 assert_eq!(
2516 anon_t.effective_session,
2517 LdapSession::UnixBind(UUID_ANONYMOUS)
2518 );
2519
2520 let sr = SearchRequest {
2522 msgid: 1,
2523 base: "dc=example,dc=com".to_string(),
2524 scope: LdapSearchScope::Subtree,
2525 filter: LdapFilter::And(vec![
2526 LdapFilter::Equality(Attribute::Class.to_string(), "sudohost".to_string()),
2527 LdapFilter::Substring(
2528 Attribute::SudoHost.to_string(),
2529 LdapSubstringFilter {
2530 initial: Some("a".to_string()),
2531 any: vec!["x".to_string()],
2532 final_: Some("z".to_string()),
2533 },
2534 ),
2535 ]),
2536 attrs: vec![
2537 "*".to_string(),
2538 LDAP_ATTR_NAME.to_string(),
2540 Attribute::EntryUuid.to_string(),
2542 ],
2543 };
2544 let r1 = ldaps
2545 .do_search(idms, &sr, &anon_t, Source::Internal)
2546 .await
2547 .unwrap();
2548
2549 assert_eq!(r1.len(), 1);
2551
2552 let sr = SearchRequest {
2555 msgid: 1,
2556 base: "dc=example,dc=com".to_string(),
2557 scope: LdapSearchScope::Subtree,
2558 filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
2559 attrs: vec![
2560 "uid".to_string(),
2561 "uidNumber".to_string(),
2562 "gidNumber".to_string(),
2563 "gecos".to_string(),
2564 "cn".to_string(),
2565 "entryuuid".to_string(),
2566 ],
2567 };
2568 let r1 = ldaps
2569 .do_search(idms, &sr, &anon_t, Source::Internal)
2570 .await
2571 .unwrap();
2572
2573 trace!(?r1);
2574
2575 assert_eq!(r1.len(), 2);
2577 match &r1[0].op {
2578 LdapOp::SearchResultEntry(lsre) => {
2579 assert_entry_contains!(
2580 lsre,
2581 "spn=testperson1@example.com,dc=example,dc=com",
2582 (Attribute::Uid, "testperson1"),
2583 (Attribute::Cn, "testperson1"),
2584 (Attribute::Gecos, "testperson1"),
2585 (Attribute::UidNumber, "12345"),
2586 (Attribute::GidNumber, "12345"),
2587 (Attribute::EntryUuid, "cc8e95b4-c24f-4d68-ba54-8bed76f63930")
2588 );
2589 }
2590 _ => panic!("Oh no"),
2591 };
2592 }
2593
2594 #[idm_test]
2595 async fn test_ldap_compare_request(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2596 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
2597
2598 {
2600 let acct_uuid = uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930");
2601
2602 let e1 = entry_init!(
2603 (Attribute::Class, EntryClass::Person.to_value()),
2604 (Attribute::Class, EntryClass::Account.to_value()),
2605 (Attribute::Class, EntryClass::PosixAccount.to_value()),
2606 (Attribute::Name, Value::new_iname("testperson1")),
2607 (Attribute::Uuid, Value::Uuid(acct_uuid)),
2608 (Attribute::GidNumber, Value::Uint32(12345)),
2609 (Attribute::Description, Value::new_utf8s("testperson1")),
2610 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2611 );
2612
2613 let mut server_txn = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2614
2615 server_txn
2617 .qs_write
2618 .internal_modify_uuid(
2619 UUID_IDM_UNIX_AUTHENTICATION_READ,
2620 &ModifyList::new_append(Attribute::Member, Value::Refer(UUID_ANONYMOUS)),
2621 )
2622 .expect("Unable to modify UNIX_AUTHENTICATION_READ group");
2623
2624 assert!(server_txn
2625 .qs_write
2626 .internal_create(vec![e1])
2627 .and_then(|_| server_txn.commit())
2628 .is_ok());
2629 }
2630
2631 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
2633 assert_eq!(
2634 anon_t.effective_session,
2635 LdapSession::UnixBind(UUID_ANONYMOUS)
2636 );
2637
2638 #[track_caller]
2639 fn assert_compare_result(r: &[LdapMsg], code: &LdapResultCode) {
2640 assert_eq!(r.len(), 1);
2641 match &r[0].op {
2642 LdapOp::CompareResult(lcr) => {
2643 assert_eq!(&lcr.code, code);
2644 }
2645 _ => panic!("Oh no"),
2646 };
2647 }
2648
2649 let cr = CompareRequest {
2650 msgid: 1,
2651 entry: "name=testperson1,dc=example,dc=com".to_string(),
2652 atype: Attribute::Name.to_string(),
2653 val: "testperson1".to_string(),
2654 };
2655
2656 assert_compare_result(
2657 &ldaps
2658 .do_compare(idms, &cr, &anon_t, Source::Internal)
2659 .await
2660 .unwrap(),
2661 &LdapResultCode::CompareTrue,
2662 );
2663
2664 let cr = CompareRequest {
2665 msgid: 1,
2666 entry: "name=testperson1,dc=example,dc=com".to_string(),
2667 atype: Attribute::GidNumber.to_string(),
2668 val: "12345".to_string(),
2669 };
2670
2671 assert_compare_result(
2672 &ldaps
2673 .do_compare(idms, &cr, &anon_t, Source::Internal)
2674 .await
2675 .unwrap(),
2676 &LdapResultCode::CompareTrue,
2677 );
2678
2679 let cr = CompareRequest {
2680 msgid: 1,
2681 entry: "name=testperson1,dc=example,dc=com".to_string(),
2682 atype: Attribute::Name.to_string(),
2683 val: "other".to_string(),
2684 };
2685 assert_compare_result(
2686 &ldaps
2687 .do_compare(idms, &cr, &anon_t, Source::Internal)
2688 .await
2689 .unwrap(),
2690 &LdapResultCode::CompareFalse,
2691 );
2692
2693 let cr = CompareRequest {
2694 msgid: 1,
2695 entry: "name=other,dc=example,dc=com".to_string(),
2696 atype: Attribute::Name.to_string(),
2697 val: "other".to_string(),
2698 };
2699 assert_compare_result(
2700 &ldaps
2701 .do_compare(idms, &cr, &anon_t, Source::Internal)
2702 .await
2703 .unwrap(),
2704 &LdapResultCode::NoSuchObject,
2705 );
2706
2707 let cr = CompareRequest {
2708 msgid: 1,
2709 entry: "invalidentry".to_string(),
2710 atype: Attribute::Name.to_string(),
2711 val: "other".to_string(),
2712 };
2713 assert!(&ldaps
2714 .do_compare(idms, &cr, &anon_t, Source::Internal)
2715 .await
2716 .is_err());
2717
2718 let cr = CompareRequest {
2719 msgid: 1,
2720 entry: "name=other,dc=example,dc=com".to_string(),
2721 atype: "invalid".to_string(),
2722 val: "other".to_string(),
2723 };
2724 assert_eq!(
2725 &ldaps
2726 .do_compare(idms, &cr, &anon_t, Source::Internal)
2727 .await
2728 .unwrap_err(),
2729 &OperationError::InvalidAttributeName("invalid".to_string()),
2730 );
2731 }
2732
2733 #[idm_test]
2734 async fn test_ldap_maximum_queryable_attributes(
2735 idms: &IdmServer,
2736 _idms_delayed: &IdmServerDelayed,
2737 ) {
2738 let mut server_txn = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2741
2742 let set_ldap_maximum_queryable_attrs = ModifyEvent::new_internal_invalid(
2743 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_DOMAIN_INFO))),
2744 ModifyList::new_purge_and_set(Attribute::LdapMaxQueryableAttrs, Value::Uint32(2)),
2745 );
2746 assert!(server_txn
2747 .qs_write
2748 .modify(&set_ldap_maximum_queryable_attrs)
2749 .and_then(|_| server_txn.commit())
2750 .is_ok());
2751
2752 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
2753
2754 let usr_uuid = Uuid::new_v4();
2755 let grp_uuid = Uuid::new_v4();
2756 let app_uuid = Uuid::new_v4();
2757 let app_name = "testapp1";
2758
2759 {
2761 let e1 = entry_init!(
2762 (Attribute::Class, EntryClass::Object.to_value()),
2763 (Attribute::Class, EntryClass::Account.to_value()),
2764 (Attribute::Class, EntryClass::Person.to_value()),
2765 (Attribute::Name, Value::new_iname("testperson1")),
2766 (Attribute::Uuid, Value::Uuid(usr_uuid)),
2767 (Attribute::Description, Value::new_utf8s("testperson1")),
2768 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2769 );
2770
2771 let e2 = entry_init!(
2772 (Attribute::Class, EntryClass::Object.to_value()),
2773 (Attribute::Class, EntryClass::Group.to_value()),
2774 (Attribute::Name, Value::new_iname("testgroup1")),
2775 (Attribute::Uuid, Value::Uuid(grp_uuid))
2776 );
2777
2778 let e3 = entry_init!(
2779 (Attribute::Class, EntryClass::Object.to_value()),
2780 (Attribute::Class, EntryClass::Account.to_value()),
2781 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
2782 (Attribute::Class, EntryClass::Application.to_value()),
2783 (Attribute::DisplayName, Value::new_utf8s("Application")),
2784 (Attribute::Name, Value::new_iname(app_name)),
2785 (Attribute::Uuid, Value::Uuid(app_uuid)),
2786 (Attribute::LinkedGroup, Value::Refer(grp_uuid))
2787 );
2788
2789 let ct = duration_from_epoch_now();
2790 let mut server_txn = idms.proxy_write(ct).await.unwrap();
2791 assert!(server_txn
2792 .qs_write
2793 .internal_create(vec![e1, e2, e3])
2794 .and_then(|_| server_txn.commit())
2795 .is_ok());
2796 }
2797
2798 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
2800 assert_eq!(
2801 anon_t.effective_session,
2802 LdapSession::UnixBind(UUID_ANONYMOUS)
2803 );
2804
2805 let invalid_search = SearchRequest {
2806 msgid: 1,
2807 base: "dc=example,dc=com".to_string(),
2808 scope: LdapSearchScope::Subtree,
2809 filter: LdapFilter::Present(Attribute::ObjectClass.to_string()),
2810 attrs: vec![
2811 "objectClass".to_string(),
2812 "cn".to_string(),
2813 "givenName".to_string(),
2814 ],
2815 };
2816
2817 let valid_search = SearchRequest {
2818 msgid: 1,
2819 base: "dc=example,dc=com".to_string(),
2820 scope: LdapSearchScope::Subtree,
2821 filter: LdapFilter::Present(Attribute::ObjectClass.to_string()),
2822 attrs: vec!["objectClass: person".to_string()],
2823 };
2824
2825 let invalid_res: Result<Vec<LdapMsg>, OperationError> = ldaps
2826 .do_search(idms, &invalid_search, &anon_t, Source::Internal)
2827 .await;
2828
2829 let valid_res: Result<Vec<LdapMsg>, OperationError> = ldaps
2830 .do_search(idms, &valid_search, &anon_t, Source::Internal)
2831 .await;
2832
2833 assert_eq!(invalid_res, Err(OperationError::ResourceLimit));
2834 assert!(valid_res.is_ok());
2835 }
2836}