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 ]
817}
818
819#[inline]
820pub(crate) fn ldap_vattr_map(input: &str) -> Option<&str> {
821 match input {
828 ATTR_CN | ATTR_UID | LDAP_ATTR_ENTRYDN | LDAP_ATTR_DN => Some(ATTR_NAME),
834 ATTR_GECOS => Some(ATTR_DISPLAYNAME),
835 ATTR_EMAIL => Some(ATTR_MAIL),
836 ATTR_LDAP_EMAIL_ADDRESS => Some(ATTR_MAIL),
837 LDAP_ATTR_EMAIL_ALTERNATIVE => Some(ATTR_MAIL),
838 LDAP_ATTR_EMAIL_PRIMARY => Some(ATTR_MAIL),
839 LDAP_ATTR_ENTRYUUID => Some(ATTR_UUID),
840 LDAP_ATTR_KEYS => Some(ATTR_SSH_PUBLICKEY),
841 LDAP_ATTR_MAIL_ALTERNATIVE => Some(ATTR_MAIL),
842 LDAP_ATTR_MAIL_PRIMARY => Some(ATTR_MAIL),
843 ATTR_OBJECTCLASS => Some(ATTR_CLASS),
844 ATTR_LDAP_SSHPUBLICKEY => Some(ATTR_SSH_PUBLICKEY), ATTR_UIDNUMBER => Some(ATTR_GIDNUMBER), _ => None,
847 }
848}
849
850#[inline]
851pub(crate) fn ldap_attr_filter_map(input: &str) -> Attribute {
852 let a_lower = input.to_lowercase();
853 Attribute::from(ldap_vattr_map(&a_lower).unwrap_or(a_lower.as_str()))
854}
855
856#[cfg(test)]
857mod tests {
858 use crate::prelude::*;
859
860 use compact_jwt::{dangernoverify::JwsDangerReleaseWithoutVerify, JwsVerifier};
861 use hashbrown::HashSet;
862 use kanidm_proto::internal::ApiToken;
863 use ldap3_proto::proto::{
864 LdapFilter, LdapMsg, LdapOp, LdapResultCode, LdapSearchScope, LdapSubstringFilter,
865 };
866 use ldap3_proto::simple::*;
867
868 use super::{LdapServer, LdapSession};
869 use crate::idm::application::GenerateApplicationPasswordEvent;
870 use crate::idm::event::{LdapApplicationAuthEvent, UnixPasswordChangeEvent};
871 use crate::idm::serviceaccount::GenerateApiTokenEvent;
872
873 const TEST_PASSWORD: &str = "ntaoeuntnaoeuhraohuercahu😍";
874
875 #[idm_test]
876 async fn test_ldap_simple_bind(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
877 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
878
879 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
880 let me_posix = ModifyEvent::new_internal_invalid(
882 filter!(f_eq(Attribute::Name, PartialValue::new_iname("admin"))),
883 ModifyList::new_list(vec![
884 Modify::Present(Attribute::Class, EntryClass::PosixAccount.into()),
885 Modify::Present(Attribute::GidNumber, Value::new_uint32(2001)),
886 ]),
887 );
888 assert!(idms_prox_write.qs_write.modify(&me_posix).is_ok());
889
890 let pce = UnixPasswordChangeEvent::new_internal(UUID_ADMIN, TEST_PASSWORD);
891
892 assert!(idms_prox_write.set_unix_account_password(&pce).is_ok());
893 assert!(idms_prox_write.commit().is_ok()); let admin_t = ldaps
898 .do_bind(idms, "admin", TEST_PASSWORD)
899 .await
900 .unwrap()
901 .unwrap();
902 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
903 let admin_t = ldaps
904 .do_bind(idms, "admin@example.com", TEST_PASSWORD)
905 .await
906 .unwrap()
907 .unwrap();
908 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
909
910 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
913 let disallow_unix_pw_flag = ModifyEvent::new_internal_invalid(
914 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_DOMAIN_INFO))),
915 ModifyList::new_purge_and_set(Attribute::LdapAllowUnixPwBind, Value::Bool(false)),
916 );
917 assert!(idms_prox_write
918 .qs_write
919 .modify(&disallow_unix_pw_flag)
920 .is_ok());
921 assert!(idms_prox_write.commit().is_ok());
922 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
923 assert_eq!(
924 anon_t.effective_session,
925 LdapSession::UnixBind(UUID_ANONYMOUS)
926 );
927 assert!(
928 ldaps.do_bind(idms, "", "test").await.unwrap_err() == OperationError::NotAuthenticated
929 );
930 let admin_t = ldaps.do_bind(idms, "admin", TEST_PASSWORD).await.unwrap();
931 assert!(admin_t.is_none());
932
933 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
935 let allow_unix_pw_flag = ModifyEvent::new_internal_invalid(
936 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_DOMAIN_INFO))),
937 ModifyList::new_purge_and_set(Attribute::LdapAllowUnixPwBind, Value::Bool(true)),
938 );
939 assert!(idms_prox_write.qs_write.modify(&allow_unix_pw_flag).is_ok());
940 assert!(idms_prox_write.commit().is_ok());
941
942 let admin_t = ldaps
944 .do_bind(idms, "admin", TEST_PASSWORD)
945 .await
946 .unwrap()
947 .unwrap();
948 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
949 let admin_t = ldaps
950 .do_bind(idms, "admin@example.com", TEST_PASSWORD)
951 .await
952 .unwrap()
953 .unwrap();
954 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
955 let admin_t = ldaps
956 .do_bind(idms, STR_UUID_ADMIN, TEST_PASSWORD)
957 .await
958 .unwrap()
959 .unwrap();
960 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
961 let admin_t = ldaps
962 .do_bind(idms, "name=admin,dc=example,dc=com", TEST_PASSWORD)
963 .await
964 .unwrap()
965 .unwrap();
966 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
967 let admin_t = ldaps
968 .do_bind(
969 idms,
970 "spn=admin@example.com,dc=example,dc=com",
971 TEST_PASSWORD,
972 )
973 .await
974 .unwrap()
975 .unwrap();
976 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
977 let admin_t = ldaps
978 .do_bind(
979 idms,
980 format!("uuid={STR_UUID_ADMIN},dc=example,dc=com").as_str(),
981 TEST_PASSWORD,
982 )
983 .await
984 .unwrap()
985 .unwrap();
986 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
987
988 let admin_t = ldaps
989 .do_bind(idms, "name=admin", TEST_PASSWORD)
990 .await
991 .unwrap()
992 .unwrap();
993 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
994 let admin_t = ldaps
995 .do_bind(idms, "spn=admin@example.com", TEST_PASSWORD)
996 .await
997 .unwrap()
998 .unwrap();
999 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
1000 let admin_t = ldaps
1001 .do_bind(
1002 idms,
1003 format!("uuid={STR_UUID_ADMIN}").as_str(),
1004 TEST_PASSWORD,
1005 )
1006 .await
1007 .unwrap()
1008 .unwrap();
1009 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
1010
1011 let admin_t = ldaps
1012 .do_bind(idms, "admin,dc=example,dc=com", TEST_PASSWORD)
1013 .await
1014 .unwrap()
1015 .unwrap();
1016 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
1017 let admin_t = ldaps
1018 .do_bind(idms, "admin@example.com,dc=example,dc=com", TEST_PASSWORD)
1019 .await
1020 .unwrap()
1021 .unwrap();
1022 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
1023 let admin_t = ldaps
1024 .do_bind(
1025 idms,
1026 format!("{STR_UUID_ADMIN},dc=example,dc=com").as_str(),
1027 TEST_PASSWORD,
1028 )
1029 .await
1030 .unwrap()
1031 .unwrap();
1032 assert_eq!(admin_t.effective_session, LdapSession::UnixBind(UUID_ADMIN));
1033
1034 assert!(ldaps
1036 .do_bind(idms, "admin", "test")
1037 .await
1038 .unwrap()
1039 .is_none());
1040
1041 assert!(ldaps
1043 .do_bind(
1044 idms,
1045 "spn=admin@example.com,dc=clownshoes,dc=example,dc=com",
1046 TEST_PASSWORD
1047 )
1048 .await
1049 .is_err());
1050 assert!(ldaps
1051 .do_bind(
1052 idms,
1053 "spn=claire@example.com,dc=example,dc=com",
1054 TEST_PASSWORD
1055 )
1056 .await
1057 .is_err());
1058 assert!(ldaps
1059 .do_bind(idms, ",dc=example,dc=com", TEST_PASSWORD)
1060 .await
1061 .is_err());
1062 assert!(ldaps
1063 .do_bind(idms, "dc=example,dc=com", TEST_PASSWORD)
1064 .await
1065 .is_err());
1066
1067 assert!(ldaps.do_bind(idms, "claire", "test").await.is_err());
1068 }
1069
1070 #[idm_test]
1071 async fn test_ldap_application_dnre(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
1072 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
1073
1074 let testdn = format!("app=app1,{0}", ldaps.basedn);
1075 let captures = ldaps.dnre.captures(testdn.as_str()).unwrap();
1076 assert!(captures.name("app").is_some());
1077 assert!(captures.name("attr").is_none());
1078 assert!(captures.name("val").is_none());
1079
1080 let testdn = format!("uid=foo,app=app1,{0}", ldaps.basedn);
1081 let captures = ldaps.dnre.captures(testdn.as_str()).unwrap();
1082 assert!(captures.name("app").is_some());
1083 assert!(captures.name("attr").is_some());
1084 assert!(captures.name("val").is_some());
1085
1086 let testdn = format!("uid=foo,{0}", ldaps.basedn);
1087 let captures = ldaps.dnre.captures(testdn.as_str()).unwrap();
1088 assert!(captures.name("app").is_none());
1089 assert!(captures.name("attr").is_some());
1090 assert!(captures.name("val").is_some());
1091 }
1092
1093 #[idm_test]
1094 async fn test_ldap_application_search(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
1095 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
1096
1097 let usr_uuid = Uuid::new_v4();
1098 let grp_uuid = Uuid::new_v4();
1099 let app_uuid = Uuid::new_v4();
1100 let app_name = "testapp1";
1101
1102 {
1104 let e1 = entry_init!(
1105 (Attribute::Class, EntryClass::Object.to_value()),
1106 (Attribute::Class, EntryClass::Account.to_value()),
1107 (Attribute::Class, EntryClass::Person.to_value()),
1108 (Attribute::Name, Value::new_iname("testperson1")),
1109 (Attribute::Uuid, Value::Uuid(usr_uuid)),
1110 (Attribute::Description, Value::new_utf8s("testperson1")),
1111 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
1112 );
1113
1114 let e2 = entry_init!(
1115 (Attribute::Class, EntryClass::Object.to_value()),
1116 (Attribute::Class, EntryClass::Group.to_value()),
1117 (Attribute::Name, Value::new_iname("testgroup1")),
1118 (Attribute::Uuid, Value::Uuid(grp_uuid))
1119 );
1120
1121 let e3 = entry_init!(
1122 (Attribute::Class, EntryClass::Object.to_value()),
1123 (Attribute::Class, EntryClass::Account.to_value()),
1124 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
1125 (Attribute::Class, EntryClass::Application.to_value()),
1126 (Attribute::DisplayName, Value::new_utf8s("Application")),
1127 (Attribute::Name, Value::new_iname(app_name)),
1128 (Attribute::Uuid, Value::Uuid(app_uuid)),
1129 (Attribute::LinkedGroup, Value::Refer(grp_uuid))
1130 );
1131
1132 let ct = duration_from_epoch_now();
1133 let mut server_txn = idms.proxy_write(ct).await.unwrap();
1134 assert!(server_txn
1135 .qs_write
1136 .internal_create(vec![e1, e2, e3])
1137 .and_then(|_| server_txn.commit())
1138 .is_ok());
1139 }
1140
1141 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
1143 assert_eq!(
1144 anon_t.effective_session,
1145 LdapSession::UnixBind(UUID_ANONYMOUS)
1146 );
1147
1148 let sr = SearchRequest {
1150 msgid: 1,
1151 base: format!("app={app_name},dc=example,dc=com"),
1152 scope: LdapSearchScope::Subtree,
1153 filter: LdapFilter::Present(Attribute::ObjectClass.to_string()),
1154 attrs: vec!["*".to_string()],
1155 };
1156
1157 let r1 = ldaps
1158 .do_search(idms, &sr, &anon_t, Source::Internal)
1159 .await
1160 .unwrap();
1161
1162 let sr = SearchRequest {
1163 msgid: 1,
1164 base: "dc=example,dc=com".to_string(),
1165 scope: LdapSearchScope::Subtree,
1166 filter: LdapFilter::Present(Attribute::ObjectClass.to_string()),
1167 attrs: vec!["*".to_string()],
1168 };
1169
1170 let r2 = ldaps
1171 .do_search(idms, &sr, &anon_t, Source::Internal)
1172 .await
1173 .unwrap();
1174 assert!(!r1.is_empty());
1175 assert_eq!(r1.len(), r2.len());
1176 }
1177
1178 #[idm_test]
1179 async fn test_ldap_spn_search(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
1180 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
1181
1182 let usr_uuid = Uuid::new_v4();
1183 let usr_name = "panko";
1184
1185 {
1187 let e1: Entry<EntryInit, EntryNew> = entry_init!(
1188 (Attribute::Class, EntryClass::Object.to_value()),
1189 (Attribute::Class, EntryClass::Account.to_value()),
1190 (Attribute::Class, EntryClass::PosixAccount.to_value()),
1191 (Attribute::Class, EntryClass::Person.to_value()),
1192 (Attribute::Name, Value::new_iname(usr_name)),
1193 (Attribute::Uuid, Value::Uuid(usr_uuid)),
1194 (Attribute::DisplayName, Value::new_utf8s(usr_name))
1195 );
1196
1197 let ct = duration_from_epoch_now();
1198 let mut server_txn = idms.proxy_write(ct).await.unwrap();
1199
1200 server_txn
1202 .qs_write
1203 .internal_modify_uuid(
1204 UUID_IDM_UNIX_AUTHENTICATION_READ,
1205 &ModifyList::new_append(Attribute::Member, Value::Refer(UUID_ANONYMOUS)),
1206 )
1207 .expect("Unable to modify UNIX_AUTHENTICATION_READ group");
1208
1209 assert!(server_txn
1210 .qs_write
1211 .internal_create(vec![e1])
1212 .and_then(|_| server_txn.commit())
1213 .is_ok());
1214 }
1215
1216 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
1218 assert_eq!(
1219 anon_t.effective_session,
1220 LdapSession::UnixBind(UUID_ANONYMOUS)
1221 );
1222
1223 let sr = SearchRequest {
1225 msgid: 1,
1226 base: "dc=example,dc=com".to_string(),
1227 scope: LdapSearchScope::Subtree,
1228 filter: LdapFilter::Or(vec![
1229 LdapFilter::Equality(Attribute::Name.to_string(), usr_name.to_string()),
1230 LdapFilter::Equality(Attribute::Spn.to_string(), usr_name.to_string()),
1231 ]),
1232 attrs: vec!["*".to_string()],
1233 };
1234
1235 let result = ldaps
1236 .do_search(idms, &sr, &anon_t, Source::Internal)
1237 .await
1238 .map(|r| {
1239 r.into_iter()
1240 .filter(|r| matches!(r.op, LdapOp::SearchResultEntry(_)))
1241 .collect::<Vec<_>>()
1242 })
1243 .unwrap();
1244
1245 assert!(!result.is_empty());
1246
1247 let sr = SearchRequest {
1248 msgid: 1,
1249 base: "dc=example,dc=com".to_string(),
1250 scope: LdapSearchScope::Subtree,
1251 filter: LdapFilter::And(vec![
1252 LdapFilter::Equality(Attribute::Name.to_string(), usr_name.to_string()),
1253 LdapFilter::Equality(Attribute::Spn.to_string(), usr_name.to_string()),
1254 ]),
1255 attrs: vec!["*".to_string()],
1256 };
1257
1258 let empty_result = ldaps
1259 .do_search(idms, &sr, &anon_t, Source::Internal)
1260 .await
1261 .map(|r| {
1262 r.into_iter()
1263 .filter(|r| matches!(r.op, LdapOp::SearchResultEntry(_)))
1264 .collect::<Vec<_>>()
1265 })
1266 .unwrap();
1267
1268 assert!(empty_result.is_empty());
1269 }
1270
1271 #[idm_test]
1272 async fn test_ldap_application_bind(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
1273 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
1274
1275 let usr_uuid = Uuid::new_v4();
1276 let grp_uuid = Uuid::new_v4();
1277 let app_uuid = Uuid::new_v4();
1278
1279 {
1281 let e1 = entry_init!(
1282 (Attribute::Class, EntryClass::Object.to_value()),
1283 (Attribute::Class, EntryClass::Account.to_value()),
1284 (Attribute::Class, EntryClass::Person.to_value()),
1285 (Attribute::Name, Value::new_iname("testperson1")),
1286 (Attribute::Uuid, Value::Uuid(usr_uuid)),
1287 (Attribute::Description, Value::new_utf8s("testperson1")),
1288 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
1289 );
1290
1291 let e2 = entry_init!(
1292 (Attribute::Class, EntryClass::Object.to_value()),
1293 (Attribute::Class, EntryClass::Group.to_value()),
1294 (Attribute::Name, Value::new_iname("testgroup1")),
1295 (Attribute::Uuid, Value::Uuid(grp_uuid))
1296 );
1297
1298 let e3 = entry_init!(
1299 (Attribute::Class, EntryClass::Object.to_value()),
1300 (Attribute::Class, EntryClass::Account.to_value()),
1301 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
1302 (Attribute::Class, EntryClass::Application.to_value()),
1303 (Attribute::DisplayName, Value::new_utf8s("Application")),
1304 (Attribute::Name, Value::new_iname("testapp1")),
1305 (Attribute::Uuid, Value::Uuid(app_uuid)),
1306 (Attribute::LinkedGroup, Value::Refer(grp_uuid))
1307 );
1308
1309 let ct = duration_from_epoch_now();
1310 let mut server_txn = idms.proxy_write(ct).await.unwrap();
1311 assert!(server_txn
1312 .qs_write
1313 .internal_create(vec![e1, e2, e3])
1314 .and_then(|_| server_txn.commit())
1315 .is_ok());
1316 }
1317
1318 let res = ldaps
1320 .do_bind(idms, "spn=testperson1,app=testapp1,dc=example,dc=com", "")
1321 .await;
1322 assert!(res.is_ok());
1323 assert!(res.unwrap().is_none());
1324
1325 {
1326 let ml = ModifyList::new_append(Attribute::Member, Value::Refer(usr_uuid));
1327 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
1328 assert!(idms_prox_write
1329 .qs_write
1330 .internal_modify_uuid(grp_uuid, &ml)
1331 .is_ok());
1332 assert!(idms_prox_write.commit().is_ok());
1333 }
1334
1335 let res = ldaps
1337 .do_bind(idms, "spn=testperson1,app=testapp1,dc=example,dc=com", "")
1338 .await;
1339 assert!(res.is_ok());
1340 assert!(res.unwrap().is_none());
1341
1342 let pass1: String;
1343 let pass2: String;
1344 let pass3: String;
1345 {
1346 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
1347
1348 let ev = GenerateApplicationPasswordEvent::new_internal(
1349 usr_uuid,
1350 app_uuid,
1351 "apppwd1".to_string(),
1352 );
1353 (pass1, _) = idms_prox_write
1354 .generate_application_password(&ev)
1355 .expect("Failed to generate application password");
1356
1357 let ev = GenerateApplicationPasswordEvent::new_internal(
1358 usr_uuid,
1359 app_uuid,
1360 "apppwd2".to_string(),
1361 );
1362 (pass2, _) = idms_prox_write
1363 .generate_application_password(&ev)
1364 .expect("Failed to generate application password");
1365
1366 assert!(idms_prox_write.commit().is_ok());
1367
1368 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
1370 let ev = GenerateApplicationPasswordEvent::new_internal(
1371 usr_uuid,
1372 app_uuid,
1373 "apppwd2".to_string(),
1374 );
1375 (pass3, _) = idms_prox_write
1376 .generate_application_password(&ev)
1377 .expect("Failed to generate application password");
1378 assert!(idms_prox_write.commit().is_ok());
1379 }
1380
1381 let res = ldaps
1383 .do_bind(
1384 idms,
1385 "spn=testperson1,app=testapp1,dc=example,dc=com",
1386 pass1.as_str(),
1387 )
1388 .await;
1389 assert!(res.is_ok());
1390 assert!(res.unwrap().is_some());
1391
1392 let res = ldaps
1394 .do_bind(
1395 idms,
1396 "spn=testperson1,app=testapp1,dc=example,dc=com",
1397 pass2.as_str(),
1398 )
1399 .await;
1400 assert!(res.is_ok());
1401 assert!(res.unwrap().is_none());
1402
1403 let res = ldaps
1405 .do_bind(
1406 idms,
1407 "spn=testperson1,app=testapp1,dc=example,dc=com",
1408 pass3.as_str(),
1409 )
1410 .await;
1411 assert!(res.is_ok());
1412 assert!(res.unwrap().is_some());
1413
1414 let res = ldaps
1416 .do_bind(
1417 idms,
1418 "spn=testperson1,app=testapp1,dc=example,dc=com",
1419 "FOO",
1420 )
1421 .await;
1422 assert!(res.is_ok());
1423 assert!(res.unwrap().is_none());
1424 }
1425
1426 #[idm_test]
1427 async fn test_ldap_application_linked_group(
1428 idms: &IdmServer,
1429 _idms_delayed: &IdmServerDelayed,
1430 ) {
1431 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
1432
1433 let usr_uuid = Uuid::new_v4();
1434 let usr_name = "testuser1";
1435
1436 let grp1_uuid = Uuid::new_v4();
1437 let grp1_name = "testgroup1";
1438 let grp2_uuid = Uuid::new_v4();
1439 let grp2_name = "testgroup2";
1440
1441 let app1_uuid = Uuid::new_v4();
1442 let app1_name = "testapp1";
1443 let app2_uuid = Uuid::new_v4();
1444 let app2_name = "testapp2";
1445
1446 {
1448 let e1 = entry_init!(
1449 (Attribute::Class, EntryClass::Object.to_value()),
1450 (Attribute::Class, EntryClass::Account.to_value()),
1451 (Attribute::Class, EntryClass::Person.to_value()),
1452 (Attribute::Name, Value::new_iname(usr_name)),
1453 (Attribute::Uuid, Value::Uuid(usr_uuid)),
1454 (Attribute::Description, Value::new_utf8s(usr_name)),
1455 (Attribute::DisplayName, Value::new_utf8s(usr_name))
1456 );
1457
1458 let e2 = entry_init!(
1459 (Attribute::Class, EntryClass::Object.to_value()),
1460 (Attribute::Class, EntryClass::Group.to_value()),
1461 (Attribute::Name, Value::new_iname(grp1_name)),
1462 (Attribute::Uuid, Value::Uuid(grp1_uuid)),
1463 (Attribute::Member, Value::Refer(usr_uuid))
1464 );
1465
1466 let e3 = entry_init!(
1467 (Attribute::Class, EntryClass::Object.to_value()),
1468 (Attribute::Class, EntryClass::Group.to_value()),
1469 (Attribute::Name, Value::new_iname(grp2_name)),
1470 (Attribute::Uuid, Value::Uuid(grp2_uuid))
1471 );
1472
1473 let e4 = entry_init!(
1474 (Attribute::Class, EntryClass::Object.to_value()),
1475 (Attribute::Class, EntryClass::Account.to_value()),
1476 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
1477 (Attribute::Class, EntryClass::Application.to_value()),
1478 (Attribute::DisplayName, Value::new_utf8s("Application")),
1479 (Attribute::Name, Value::new_iname(app1_name)),
1480 (Attribute::Uuid, Value::Uuid(app1_uuid)),
1481 (Attribute::LinkedGroup, Value::Refer(grp1_uuid))
1482 );
1483
1484 let e5 = entry_init!(
1485 (Attribute::Class, EntryClass::Object.to_value()),
1486 (Attribute::Class, EntryClass::Account.to_value()),
1487 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
1488 (Attribute::Class, EntryClass::Application.to_value()),
1489 (Attribute::DisplayName, Value::new_utf8s("Application")),
1490 (Attribute::Name, Value::new_iname(app2_name)),
1491 (Attribute::Uuid, Value::Uuid(app2_uuid)),
1492 (Attribute::LinkedGroup, Value::Refer(grp2_uuid))
1493 );
1494
1495 let ct = duration_from_epoch_now();
1496 let mut server_txn = idms.proxy_write(ct).await.unwrap();
1497 assert!(server_txn
1498 .qs_write
1499 .internal_create(vec![e1, e2, e3, e4, e5])
1500 .and_then(|_| server_txn.commit())
1501 .is_ok());
1502 }
1503
1504 let pass_app1: String;
1505 let pass_app2: String;
1506 {
1507 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
1508
1509 let ev = GenerateApplicationPasswordEvent::new_internal(
1510 usr_uuid,
1511 app1_uuid,
1512 "label".to_string(),
1513 );
1514 (pass_app1, _) = idms_prox_write
1515 .generate_application_password(&ev)
1516 .expect("Failed to generate application password");
1517
1518 let ev = GenerateApplicationPasswordEvent::new_internal(
1521 usr_uuid,
1522 app2_uuid,
1523 "label".to_string(),
1524 );
1525 (pass_app2, _) = idms_prox_write
1526 .generate_application_password(&ev)
1527 .expect("Failed to generate application password");
1528
1529 assert!(idms_prox_write.commit().is_ok());
1530 }
1531
1532 let res = ldaps
1534 .do_bind(
1535 idms,
1536 format!("spn={usr_name},app={app1_name},dc=example,dc=com").as_str(),
1537 pass_app1.as_str(),
1538 )
1539 .await;
1540 assert!(res.is_ok());
1541 assert!(res.unwrap().is_some());
1542
1543 let res = ldaps
1545 .do_bind(
1546 idms,
1547 format!("spn={usr_name},app={app2_name},dc=example,dc=com").as_str(),
1548 pass_app2.as_str(),
1549 )
1550 .await;
1551 assert!(res.is_ok());
1552 assert!(res.unwrap().is_none());
1553
1554 {
1556 let ml = ModifyList::new_append(Attribute::Member, Value::Refer(usr_uuid));
1557 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
1558 assert!(idms_prox_write
1559 .qs_write
1560 .internal_modify_uuid(grp2_uuid, &ml)
1561 .is_ok());
1562 assert!(idms_prox_write.commit().is_ok());
1563 }
1564
1565 let res = ldaps
1567 .do_bind(
1568 idms,
1569 format!("spn={usr_name},app={app2_name},dc=example,dc=com").as_str(),
1570 pass_app2.as_str(),
1571 )
1572 .await;
1573 assert!(res.is_ok());
1574 assert!(res.unwrap().is_some());
1575
1576 let res = ldaps
1578 .do_bind(
1579 idms,
1580 format!("spn={usr_name},app={app1_name},dc=example,dc=com").as_str(),
1581 pass_app2.as_str(),
1582 )
1583 .await;
1584 assert!(res.is_ok());
1585 assert!(res.unwrap().is_none());
1586
1587 {
1589 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
1590 let de = DeleteEvent::new_internal_invalid(filter!(f_eq(
1591 Attribute::Uuid,
1592 PartialValue::Uuid(app2_uuid)
1593 )));
1594 assert!(idms_prox_write.qs_write.delete(&de).is_ok());
1595 assert!(idms_prox_write.commit().is_ok());
1596 }
1597
1598 let res = ldaps
1599 .do_bind(
1600 idms,
1601 format!("spn={usr_name},app={app2_name},dc=example,dc=com").as_str(),
1602 pass_app2.as_str(),
1603 )
1604 .await;
1605 assert!(res.is_err());
1606 }
1607
1608 const TEST_CURRENT_TIME: u64 = 6000;
1612 const TEST_NOT_YET_VALID_TIME: u64 = TEST_CURRENT_TIME - 240;
1613 const TEST_VALID_FROM_TIME: u64 = TEST_CURRENT_TIME - 120;
1614 const TEST_EXPIRE_TIME: u64 = TEST_CURRENT_TIME + 120;
1615 const TEST_AFTER_EXPIRY: u64 = TEST_CURRENT_TIME + 240;
1616
1617 async fn set_account_valid_time(idms: &IdmServer, acct: Uuid) {
1618 let mut idms_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
1619
1620 let v_valid_from = Value::new_datetime_epoch(Duration::from_secs(TEST_VALID_FROM_TIME));
1621 let v_expire = Value::new_datetime_epoch(Duration::from_secs(TEST_EXPIRE_TIME));
1622
1623 let me = ModifyEvent::new_internal_invalid(
1624 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(acct))),
1625 ModifyList::new_list(vec![
1626 Modify::Present(Attribute::AccountExpire, v_expire),
1627 Modify::Present(Attribute::AccountValidFrom, v_valid_from),
1628 ]),
1629 );
1630 assert!(idms_write.qs_write.modify(&me).is_ok());
1631 idms_write.commit().expect("Must not fail");
1632 }
1633
1634 #[idm_test]
1635 async fn test_ldap_application_valid_from_expire(
1636 idms: &IdmServer,
1637 _idms_delayed: &IdmServerDelayed,
1638 ) {
1639 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
1640
1641 let usr_uuid = Uuid::new_v4();
1642 let usr_name = "testuser1";
1643
1644 let grp1_uuid = Uuid::new_v4();
1645 let grp1_name = "testgroup1";
1646
1647 let app1_uuid = Uuid::new_v4();
1648 let app1_name = "testapp1";
1649
1650 let pass_app1: String;
1651
1652 {
1654 let e1 = entry_init!(
1655 (Attribute::Class, EntryClass::Object.to_value()),
1656 (Attribute::Class, EntryClass::Account.to_value()),
1657 (Attribute::Class, EntryClass::Person.to_value()),
1658 (Attribute::Name, Value::new_iname(usr_name)),
1659 (Attribute::Uuid, Value::Uuid(usr_uuid)),
1660 (Attribute::Description, Value::new_utf8s(usr_name)),
1661 (Attribute::DisplayName, Value::new_utf8s(usr_name))
1662 );
1663
1664 let e2 = entry_init!(
1665 (Attribute::Class, EntryClass::Object.to_value()),
1666 (Attribute::Class, EntryClass::Group.to_value()),
1667 (Attribute::Name, Value::new_iname(grp1_name)),
1668 (Attribute::Uuid, Value::Uuid(grp1_uuid)),
1669 (Attribute::Member, Value::Refer(usr_uuid))
1670 );
1671
1672 let e3 = entry_init!(
1673 (Attribute::Class, EntryClass::Object.to_value()),
1674 (Attribute::Class, EntryClass::Account.to_value()),
1675 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
1676 (Attribute::Class, EntryClass::Application.to_value()),
1677 (Attribute::DisplayName, Value::new_utf8s("Application")),
1678 (Attribute::Name, Value::new_iname(app1_name)),
1679 (Attribute::Uuid, Value::Uuid(app1_uuid)),
1680 (Attribute::LinkedGroup, Value::Refer(grp1_uuid))
1681 );
1682
1683 let ct = duration_from_epoch_now();
1684 let mut server_txn = idms.proxy_write(ct).await.unwrap();
1685 assert!(server_txn
1686 .qs_write
1687 .internal_create(vec![e1, e2, e3])
1688 .and_then(|_| server_txn.commit())
1689 .is_ok());
1690
1691 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
1692
1693 let ev = GenerateApplicationPasswordEvent::new_internal(
1694 usr_uuid,
1695 app1_uuid,
1696 "label".to_string(),
1697 );
1698 (pass_app1, _) = idms_prox_write
1699 .generate_application_password(&ev)
1700 .expect("Failed to generate application password");
1701
1702 assert!(idms_prox_write.commit().is_ok());
1703 }
1704
1705 let res = ldaps
1707 .do_bind(
1708 idms,
1709 format!("spn={usr_name},app={app1_name},dc=example,dc=com").as_str(),
1710 pass_app1.as_str(),
1711 )
1712 .await;
1713 assert!(res.is_ok());
1714 assert!(res.unwrap().is_some());
1715
1716 set_account_valid_time(idms, usr_uuid).await;
1720
1721 let time_low = Duration::from_secs(TEST_NOT_YET_VALID_TIME);
1722 let time = Duration::from_secs(TEST_CURRENT_TIME);
1723 let time_high = Duration::from_secs(TEST_AFTER_EXPIRY);
1724
1725 let mut idms_auth = idms.auth().await.unwrap();
1726 let lae = LdapApplicationAuthEvent::new(app1_name, usr_uuid, pass_app1)
1727 .expect("Failed to build auth event");
1728
1729 let r1 = idms_auth
1730 .application_auth_ldap(&lae, time_low)
1731 .await
1732 .expect_err("Authentication succeeded");
1733 assert_eq!(r1, OperationError::SessionExpired);
1734
1735 let r1 = idms_auth
1736 .application_auth_ldap(&lae, time)
1737 .await
1738 .expect("Failed auth");
1739 assert!(r1.is_some());
1740
1741 let r1 = idms_auth
1742 .application_auth_ldap(&lae, time_high)
1743 .await
1744 .expect_err("Authentication succeeded");
1745 assert_eq!(r1, OperationError::SessionExpired);
1746 }
1747
1748 macro_rules! assert_entry_contains {
1749 (
1750 $entry:expr,
1751 $dn:expr,
1752 $($item:expr),*
1753 ) => {{
1754 assert_eq!($entry.dn, $dn);
1755 let mut attrs = HashSet::new();
1757 for a in $entry.attributes.iter() {
1758 for v in a.vals.iter() {
1759 attrs.insert((a.atype.as_str(), v.as_slice()));
1760 }
1761 };
1762 info!(?attrs);
1763 $(
1764 warn!("{}", $item.0);
1765 assert!(attrs.contains(&(
1766 $item.0.as_ref(), $item.1.as_bytes()
1767 )));
1768 )*
1769
1770 }};
1771 }
1772
1773 #[idm_test]
1774 async fn test_ldap_virtual_attribute_generation(
1775 idms: &IdmServer,
1776 _idms_delayed: &IdmServerDelayed,
1777 ) {
1778 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
1779
1780 let ssh_ed25519 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAeGW1P6Pc2rPq0XqbRaDKBcXZUPRklo0L1EyR30CwoP william@amethyst";
1781
1782 {
1784 let e1 = entry_init!(
1785 (Attribute::Class, EntryClass::Object.to_value()),
1786 (Attribute::Class, EntryClass::Person.to_value()),
1787 (Attribute::Class, EntryClass::Account.to_value()),
1788 (Attribute::Class, EntryClass::PosixAccount.to_value()),
1789 (Attribute::Name, Value::new_iname("testperson1")),
1790 (
1791 Attribute::Uuid,
1792 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1793 ),
1794 (Attribute::Description, Value::new_utf8s("testperson1")),
1795 (Attribute::DisplayName, Value::new_utf8s("testperson1")),
1796 (Attribute::GidNumber, Value::new_uint32(12345)),
1797 (Attribute::LoginShell, Value::new_iutf8("/bin/zsh")),
1798 (
1799 Attribute::SshPublicKey,
1800 Value::new_sshkey_str("test", ssh_ed25519).expect("Invalid ssh key")
1801 )
1802 );
1803
1804 let mut server_txn = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
1805
1806 server_txn
1808 .qs_write
1809 .internal_modify_uuid(
1810 UUID_IDM_UNIX_AUTHENTICATION_READ,
1811 &ModifyList::new_append(Attribute::Member, Value::Refer(UUID_ANONYMOUS)),
1812 )
1813 .expect("Unable to modify UNIX_AUTHENTICATION_READ group");
1814
1815 let ce = CreateEvent::new_internal(vec![e1]);
1816 assert!(server_txn
1817 .qs_write
1818 .create(&ce)
1819 .and_then(|_| server_txn.commit())
1820 .is_ok());
1821 }
1822
1823 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
1825 assert_eq!(
1826 anon_t.effective_session,
1827 LdapSession::UnixBind(UUID_ANONYMOUS)
1828 );
1829
1830 let sr = SearchRequest {
1832 msgid: 1,
1833 base: "dc=example,dc=com".to_string(),
1834 scope: LdapSearchScope::Subtree,
1835 filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
1836 attrs: vec!["*".to_string()],
1837 };
1838 let r1 = ldaps
1839 .do_search(idms, &sr, &anon_t, Source::Internal)
1840 .await
1841 .unwrap();
1842
1843 assert_eq!(r1.len(), 2);
1845 match &r1[0].op {
1846 LdapOp::SearchResultEntry(lsre) => {
1847 assert_entry_contains!(
1848 lsre,
1849 "spn=testperson1@example.com,dc=example,dc=com",
1850 (Attribute::Class, EntryClass::Object.to_string()),
1851 (Attribute::Class, EntryClass::Person.to_string()),
1852 (Attribute::Class, EntryClass::Account.to_string()),
1853 (Attribute::Class, EntryClass::PosixAccount.to_string()),
1854 (Attribute::DisplayName, "testperson1"),
1855 (Attribute::Name, "testperson1"),
1856 (Attribute::GidNumber, "12345"),
1857 (Attribute::LoginShell, "/bin/zsh"),
1858 (Attribute::SshPublicKey, ssh_ed25519),
1859 (Attribute::Uuid, "cc8e95b4-c24f-4d68-ba54-8bed76f63930")
1860 );
1861 }
1862 _ => panic!("Oh no"),
1863 };
1864
1865 let sr = SearchRequest {
1867 msgid: 1,
1868 base: "dc=example,dc=com".to_string(),
1869 scope: LdapSearchScope::Subtree,
1870 filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
1871 attrs: vec!["+".to_string()],
1872 };
1873 let r1 = ldaps
1874 .do_search(idms, &sr, &anon_t, Source::Internal)
1875 .await
1876 .unwrap();
1877
1878 assert_eq!(r1.len(), 2);
1880 match &r1[0].op {
1881 LdapOp::SearchResultEntry(lsre) => {
1882 assert_entry_contains!(
1883 lsre,
1884 "spn=testperson1@example.com,dc=example,dc=com",
1885 (Attribute::ObjectClass, EntryClass::Object.as_ref()),
1886 (Attribute::ObjectClass, EntryClass::Person.as_ref()),
1887 (Attribute::ObjectClass, EntryClass::Account.as_ref()),
1888 (Attribute::ObjectClass, EntryClass::PosixAccount.as_ref()),
1889 (Attribute::DisplayName, "testperson1"),
1890 (Attribute::Name, "testperson1"),
1891 (Attribute::GidNumber, "12345"),
1892 (Attribute::LoginShell, "/bin/zsh"),
1893 (Attribute::SshPublicKey, ssh_ed25519),
1894 (Attribute::EntryUuid, "cc8e95b4-c24f-4d68-ba54-8bed76f63930"),
1895 (
1896 Attribute::EntryDn,
1897 "spn=testperson1@example.com,dc=example,dc=com"
1898 ),
1899 (Attribute::UidNumber, "12345"),
1900 (Attribute::Cn, "testperson1"),
1901 (Attribute::LdapKeys, ssh_ed25519)
1902 );
1903 }
1904 _ => panic!("Oh no"),
1905 };
1906
1907 let sr = SearchRequest {
1909 msgid: 1,
1910 base: "dc=example,dc=com".to_string(),
1911 scope: LdapSearchScope::Subtree,
1912 filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
1913 attrs: vec![
1914 LDAP_ATTR_NAME.to_string(),
1915 Attribute::EntryDn.to_string(),
1916 ATTR_LDAP_KEYS.to_string(),
1917 Attribute::UidNumber.to_string(),
1918 ],
1919 };
1920 let r1 = ldaps
1921 .do_search(idms, &sr, &anon_t, Source::Internal)
1922 .await
1923 .unwrap();
1924
1925 assert_eq!(r1.len(), 2);
1927 match &r1[0].op {
1928 LdapOp::SearchResultEntry(lsre) => {
1929 assert_entry_contains!(
1930 lsre,
1931 "spn=testperson1@example.com,dc=example,dc=com",
1932 (Attribute::Name, "testperson1"),
1933 (
1934 Attribute::EntryDn,
1935 "spn=testperson1@example.com,dc=example,dc=com"
1936 ),
1937 (Attribute::UidNumber, "12345"),
1938 (Attribute::LdapKeys, ssh_ed25519)
1939 );
1940 }
1941 _ => panic!("Oh no"),
1942 };
1943 }
1944
1945 #[idm_test]
1946 async fn test_ldap_token_privilege_granting(
1947 idms: &IdmServer,
1948 _idms_delayed: &IdmServerDelayed,
1949 ) {
1950 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
1952
1953 let sr = SearchRequest {
1955 msgid: 1,
1956 base: "dc=example,dc=com".to_string(),
1957 scope: LdapSearchScope::Subtree,
1958 filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
1959 attrs: vec![
1960 LDAP_ATTR_NAME,
1961 LDAP_ATTR_MAIL,
1962 LDAP_ATTR_MAIL_PRIMARY,
1963 LDAP_ATTR_MAIL_ALTERNATIVE,
1964 LDAP_ATTR_EMAIL_PRIMARY,
1965 LDAP_ATTR_EMAIL_ALTERNATIVE,
1966 ]
1967 .into_iter()
1968 .map(|s| s.to_string())
1969 .collect(),
1970 };
1971
1972 let sa_uuid = uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930");
1973
1974 let apitoken = {
1977 let e1 = entry_init!(
1980 (Attribute::Class, EntryClass::Object.to_value()),
1981 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
1982 (Attribute::Class, EntryClass::Account.to_value()),
1983 (Attribute::Uuid, Value::Uuid(sa_uuid)),
1984 (Attribute::Name, Value::new_iname("service_permission_test")),
1985 (
1986 Attribute::DisplayName,
1987 Value::new_utf8s("service_permission_test")
1988 )
1989 );
1990
1991 let e2 = entry_init!(
1993 (Attribute::Class, EntryClass::Object.to_value()),
1994 (Attribute::Class, EntryClass::Person.to_value()),
1995 (Attribute::Class, EntryClass::Account.to_value()),
1996 (Attribute::Class, EntryClass::PosixAccount.to_value()),
1997 (Attribute::Name, Value::new_iname("testperson1")),
1998 (
1999 Attribute::Mail,
2000 Value::EmailAddress("testperson1@example.com".to_string(), true)
2001 ),
2002 (
2003 Attribute::Mail,
2004 Value::EmailAddress("testperson1.alternative@example.com".to_string(), false)
2005 ),
2006 (Attribute::Description, Value::new_utf8s("testperson1")),
2007 (Attribute::DisplayName, Value::new_utf8s("testperson1")),
2008 (Attribute::GidNumber, Value::new_uint32(12345)),
2009 (Attribute::LoginShell, Value::new_iutf8("/bin/zsh"))
2010 );
2011
2012 let ct = duration_from_epoch_now();
2013
2014 let mut server_txn = idms.proxy_write(ct).await.unwrap();
2015
2016 let ce = CreateEvent::new_internal(vec![e1, e2]);
2017 assert!(server_txn.qs_write.create(&ce).is_ok());
2018
2019 server_txn
2021 .qs_write
2022 .internal_modify_uuid(
2023 UUID_IDM_ACCOUNT_MAIL_READ,
2024 &ModifyList::new_append(Attribute::Member, Value::Refer(sa_uuid)),
2025 )
2026 .expect("Unable to modify UNIX_AUTHENTICATION_READ group");
2027
2028 server_txn
2030 .qs_write
2031 .internal_modify_uuid(
2032 UUID_IDM_UNIX_AUTHENTICATION_READ,
2033 &ModifyList::new_append(Attribute::Member, Value::Refer(UUID_ANONYMOUS)),
2034 )
2035 .expect("Unable to modify UNIX_AUTHENTICATION_READ group");
2036
2037 let gte = GenerateApiTokenEvent::new_internal(sa_uuid, "TestToken", None);
2041
2042 let apitoken = server_txn
2043 .service_account_generate_api_token(>e, ct)
2044 .expect("Failed to create new apitoken");
2045
2046 assert!(server_txn.commit().is_ok());
2047
2048 apitoken
2049 };
2050
2051 let anon_lbt = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
2056 assert_eq!(
2057 anon_lbt.effective_session,
2058 LdapSession::UnixBind(UUID_ANONYMOUS)
2059 );
2060
2061 let r1 = ldaps
2062 .do_search(idms, &sr, &anon_lbt, Source::Internal)
2063 .await
2064 .unwrap();
2065 assert_eq!(r1.len(), 2);
2066 match &r1[0].op {
2067 LdapOp::SearchResultEntry(lsre) => {
2068 assert_entry_contains!(
2069 lsre,
2070 "spn=testperson1@example.com,dc=example,dc=com",
2071 (Attribute::Name, "testperson1")
2072 );
2073 }
2074 _ => panic!("Oh no"),
2075 };
2076
2077 let jws_verifier = JwsDangerReleaseWithoutVerify::default();
2079
2080 let apitoken_inner = jws_verifier
2081 .verify(&apitoken)
2082 .unwrap()
2083 .from_json::<ApiToken>()
2084 .unwrap();
2085
2086 let sa_lbt = ldaps
2088 .do_bind(idms, "dn=token", &apitoken.to_string())
2089 .await
2090 .unwrap()
2091 .unwrap();
2092 assert_eq!(
2093 sa_lbt.effective_session,
2094 LdapSession::ApiToken(apitoken_inner.clone())
2095 );
2096
2097 let sa_lbt = ldaps
2099 .do_bind(idms, "", &apitoken.to_string())
2100 .await
2101 .unwrap()
2102 .unwrap();
2103 assert_eq!(
2104 sa_lbt.effective_session,
2105 LdapSession::ApiToken(apitoken_inner)
2106 );
2107
2108 let r1 = ldaps
2110 .do_search(idms, &sr, &sa_lbt, Source::Internal)
2111 .await
2112 .unwrap();
2113 assert_eq!(r1.len(), 2);
2114 match &r1[0].op {
2115 LdapOp::SearchResultEntry(lsre) => {
2116 assert_entry_contains!(
2117 lsre,
2118 "spn=testperson1@example.com,dc=example,dc=com",
2119 (Attribute::Name, "testperson1"),
2120 (Attribute::Mail, "testperson1@example.com"),
2121 (Attribute::Mail, "testperson1.alternative@example.com"),
2122 (LDAP_ATTR_MAIL_PRIMARY, "testperson1@example.com"),
2123 (
2124 LDAP_ATTR_MAIL_ALTERNATIVE,
2125 "testperson1.alternative@example.com"
2126 ),
2127 (LDAP_ATTR_EMAIL_PRIMARY, "testperson1@example.com"),
2128 (
2129 LDAP_ATTR_EMAIL_ALTERNATIVE,
2130 "testperson1.alternative@example.com"
2131 )
2132 );
2133 }
2134 _ => panic!("Oh no"),
2135 };
2136
2137 let sr = SearchRequest {
2140 msgid: 2,
2141 base: "dc=example,dc=com".to_string(),
2142 scope: LdapSearchScope::Subtree,
2143 filter: LdapFilter::And(vec![
2144 LdapFilter::Equality(Attribute::Class.to_string(), "posixAccount".to_string()),
2145 LdapFilter::Substring(
2146 LDAP_ATTR_MAIL.to_string(),
2147 LdapSubstringFilter {
2148 initial: None,
2149 any: vec![],
2150 final_: Some("@example.com".to_string()),
2151 },
2152 ),
2153 ]),
2154 attrs: vec![
2155 LDAP_ATTR_NAME,
2156 LDAP_ATTR_MAIL,
2157 LDAP_ATTR_MAIL_PRIMARY,
2158 LDAP_ATTR_MAIL_ALTERNATIVE,
2159 ]
2160 .into_iter()
2161 .map(|s| s.to_string())
2162 .collect(),
2163 };
2164
2165 let r1 = ldaps
2166 .do_search(idms, &sr, &sa_lbt, Source::Internal)
2167 .await
2168 .unwrap();
2169
2170 assert_eq!(r1.len(), 2);
2171 match &r1[0].op {
2172 LdapOp::SearchResultEntry(lsre) => {
2173 assert_entry_contains!(
2174 lsre,
2175 "spn=testperson1@example.com,dc=example,dc=com",
2176 (Attribute::Name, "testperson1"),
2177 (Attribute::Mail, "testperson1@example.com"),
2178 (Attribute::Mail, "testperson1.alternative@example.com"),
2179 (LDAP_ATTR_MAIL_PRIMARY, "testperson1@example.com"),
2180 (
2181 LDAP_ATTR_MAIL_ALTERNATIVE,
2182 "testperson1.alternative@example.com"
2183 )
2184 );
2185 }
2186 _ => panic!("Oh no"),
2187 };
2188 }
2189
2190 #[idm_test]
2191 async fn test_ldap_virtual_attribute_with_all_attr_search(
2192 idms: &IdmServer,
2193 _idms_delayed: &IdmServerDelayed,
2194 ) {
2195 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
2196
2197 let acct_uuid = uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930");
2198
2199 {
2201 let e1 = entry_init!(
2202 (Attribute::Class, EntryClass::Person.to_value()),
2203 (Attribute::Class, EntryClass::Account.to_value()),
2204 (Attribute::Class, EntryClass::PosixAccount.to_value()),
2205 (Attribute::Name, Value::new_iname("testperson1")),
2206 (Attribute::Uuid, Value::Uuid(acct_uuid)),
2207 (Attribute::Description, Value::new_utf8s("testperson1")),
2208 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2209 );
2210
2211 let mut server_txn = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2212
2213 server_txn
2215 .qs_write
2216 .internal_modify_uuid(
2217 UUID_IDM_UNIX_AUTHENTICATION_READ,
2218 &ModifyList::new_append(Attribute::Member, Value::Refer(UUID_ANONYMOUS)),
2219 )
2220 .expect("Unable to modify UNIX_AUTHENTICATION_READ group");
2221
2222 assert!(server_txn
2223 .qs_write
2224 .internal_create(vec![e1])
2225 .and_then(|_| server_txn.commit())
2226 .is_ok());
2227 }
2228
2229 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
2231 assert_eq!(
2232 anon_t.effective_session,
2233 LdapSession::UnixBind(UUID_ANONYMOUS)
2234 );
2235
2236 let sr = SearchRequest {
2238 msgid: 1,
2239 base: "dc=example,dc=com".to_string(),
2240 scope: LdapSearchScope::Subtree,
2241 filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
2242 attrs: vec![
2243 "*".to_string(),
2244 LDAP_ATTR_NAME.to_string(),
2246 Attribute::EntryUuid.to_string(),
2248 ],
2249 };
2250 let r1 = ldaps
2251 .do_search(idms, &sr, &anon_t, Source::Internal)
2252 .await
2253 .unwrap();
2254
2255 assert_eq!(r1.len(), 2);
2257 match &r1[0].op {
2258 LdapOp::SearchResultEntry(lsre) => {
2259 assert_entry_contains!(
2260 lsre,
2261 "spn=testperson1@example.com,dc=example,dc=com",
2262 (Attribute::Name, "testperson1"),
2263 (Attribute::DisplayName, "testperson1"),
2264 (Attribute::Uuid, "cc8e95b4-c24f-4d68-ba54-8bed76f63930"),
2265 (Attribute::EntryUuid, "cc8e95b4-c24f-4d68-ba54-8bed76f63930")
2266 );
2267 }
2268 _ => panic!("Oh no"),
2269 };
2270 }
2271
2272 #[idm_test]
2274 async fn test_ldap_one_dot_one_attribute(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2275 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
2276
2277 let acct_uuid = uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930");
2278
2279 {
2281 let e1 = entry_init!(
2282 (Attribute::Class, EntryClass::Person.to_value()),
2283 (Attribute::Class, EntryClass::Account.to_value()),
2284 (Attribute::Class, EntryClass::PosixAccount.to_value()),
2285 (Attribute::Name, Value::new_iname("testperson1")),
2286 (Attribute::Uuid, Value::Uuid(acct_uuid)),
2287 (Attribute::Description, Value::new_utf8s("testperson1")),
2288 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2289 );
2290
2291 let mut server_txn = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2292
2293 server_txn
2295 .qs_write
2296 .internal_modify_uuid(
2297 UUID_IDM_UNIX_AUTHENTICATION_READ,
2298 &ModifyList::new_append(Attribute::Member, Value::Refer(UUID_ANONYMOUS)),
2299 )
2300 .expect("Unable to modify UNIX_AUTHENTICATION_READ group");
2301
2302 assert!(server_txn
2303 .qs_write
2304 .internal_create(vec![e1])
2305 .and_then(|_| server_txn.commit())
2306 .is_ok());
2307 }
2308
2309 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
2311 assert_eq!(
2312 anon_t.effective_session,
2313 LdapSession::UnixBind(UUID_ANONYMOUS)
2314 );
2315
2316 let sr = SearchRequest {
2318 msgid: 1,
2319 base: "dc=example,dc=com".to_string(),
2320 scope: LdapSearchScope::Subtree,
2321 filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
2322 attrs: vec!["1.1".to_string()],
2323 };
2324 let r1 = ldaps
2325 .do_search(idms, &sr, &anon_t, Source::Internal)
2326 .await
2327 .unwrap();
2328
2329 assert_eq!(r1.len(), 2);
2331 match &r1[0].op {
2332 LdapOp::SearchResultEntry(lsre) => {
2333 assert_eq!(
2334 lsre.dn.as_str(),
2335 "spn=testperson1@example.com,dc=example,dc=com"
2336 );
2337 assert!(lsre.attributes.is_empty());
2338 }
2339 _ => panic!("Oh no"),
2340 };
2341
2342 let sr = SearchRequest {
2344 msgid: 1,
2345 base: "dc=example,dc=com".to_string(),
2346 scope: LdapSearchScope::Subtree,
2347 filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
2348 attrs: vec![
2349 "1.1".to_string(),
2350 Attribute::EntryUuid.to_string(),
2352 ],
2353 };
2354 let r1 = ldaps
2355 .do_search(idms, &sr, &anon_t, Source::Internal)
2356 .await
2357 .unwrap();
2358
2359 assert_eq!(r1.len(), 2);
2361 match &r1[0].op {
2362 LdapOp::SearchResultEntry(lsre) => {
2363 assert_entry_contains!(
2364 lsre,
2365 "spn=testperson1@example.com,dc=example,dc=com",
2366 (Attribute::EntryUuid, "cc8e95b4-c24f-4d68-ba54-8bed76f63930")
2367 );
2368 }
2369 _ => panic!("Oh no"),
2370 };
2371 }
2372
2373 #[idm_test]
2374 async fn test_ldap_rootdse_basedn_change(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2375 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
2376
2377 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
2378 assert_eq!(
2379 anon_t.effective_session,
2380 LdapSession::UnixBind(UUID_ANONYMOUS)
2381 );
2382
2383 let sr = SearchRequest {
2384 msgid: 1,
2385 base: "".to_string(),
2386 scope: LdapSearchScope::Base,
2387 filter: LdapFilter::Present(Attribute::ObjectClass.to_string()),
2388 attrs: vec!["*".to_string()],
2389 };
2390 let r1 = ldaps
2391 .do_search(idms, &sr, &anon_t, Source::Internal)
2392 .await
2393 .unwrap();
2394
2395 trace!(?r1);
2396
2397 assert_eq!(r1.len(), 2);
2399 match &r1[0].op {
2400 LdapOp::SearchResultEntry(lsre) => {
2401 assert_entry_contains!(
2402 lsre,
2403 "",
2404 (Attribute::ObjectClass, "top"),
2405 ("vendorname", "Kanidm Project"),
2406 ("supportedldapversion", "3"),
2407 ("defaultnamingcontext", "dc=example,dc=com")
2408 );
2409 }
2410 _ => panic!("Oh no"),
2411 };
2412
2413 drop(ldaps);
2414
2415 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2418 let me_posix = ModifyEvent::new_internal_invalid(
2420 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_DOMAIN_INFO))),
2421 ModifyList::new_purge_and_set(
2422 Attribute::DomainLdapBasedn,
2423 Value::new_iutf8("o=kanidmproject"),
2424 ),
2425 );
2426 assert!(idms_prox_write.qs_write.modify(&me_posix).is_ok());
2427
2428 assert!(idms_prox_write.commit().is_ok());
2429
2430 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
2432
2433 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
2434 assert_eq!(
2435 anon_t.effective_session,
2436 LdapSession::UnixBind(UUID_ANONYMOUS)
2437 );
2438
2439 let sr = SearchRequest {
2440 msgid: 1,
2441 base: "".to_string(),
2442 scope: LdapSearchScope::Base,
2443 filter: LdapFilter::Present(Attribute::ObjectClass.to_string()),
2444 attrs: vec!["*".to_string()],
2445 };
2446 let r1 = ldaps
2447 .do_search(idms, &sr, &anon_t, Source::Internal)
2448 .await
2449 .unwrap();
2450
2451 trace!(?r1);
2452
2453 assert_eq!(r1.len(), 2);
2455 match &r1[0].op {
2456 LdapOp::SearchResultEntry(lsre) => {
2457 assert_entry_contains!(
2458 lsre,
2459 "",
2460 (Attribute::ObjectClass, "top"),
2461 ("vendorname", "Kanidm Project"),
2462 ("supportedldapversion", "3"),
2463 ("defaultnamingcontext", "o=kanidmproject")
2464 );
2465 }
2466 _ => panic!("Oh no"),
2467 };
2468 }
2469
2470 #[idm_test]
2471 async fn test_ldap_sssd_compat(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2472 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
2473
2474 let acct_uuid = uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930");
2475
2476 {
2478 let e1 = entry_init!(
2479 (Attribute::Class, EntryClass::Person.to_value()),
2480 (Attribute::Class, EntryClass::Account.to_value()),
2481 (Attribute::Class, EntryClass::PosixAccount.to_value()),
2482 (Attribute::Name, Value::new_iname("testperson1")),
2483 (Attribute::Uuid, Value::Uuid(acct_uuid)),
2484 (Attribute::GidNumber, Value::Uint32(12345)),
2485 (Attribute::Description, Value::new_utf8s("testperson1")),
2486 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2487 );
2488
2489 let mut server_txn = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2490
2491 server_txn
2493 .qs_write
2494 .internal_modify_uuid(
2495 UUID_IDM_UNIX_AUTHENTICATION_READ,
2496 &ModifyList::new_append(Attribute::Member, Value::Refer(UUID_ANONYMOUS)),
2497 )
2498 .expect("Unable to modify UNIX_AUTHENTICATION_READ group");
2499
2500 assert!(server_txn
2501 .qs_write
2502 .internal_create(vec![e1])
2503 .and_then(|_| server_txn.commit())
2504 .is_ok());
2505 }
2506
2507 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
2509 assert_eq!(
2510 anon_t.effective_session,
2511 LdapSession::UnixBind(UUID_ANONYMOUS)
2512 );
2513
2514 let sr = SearchRequest {
2516 msgid: 1,
2517 base: "dc=example,dc=com".to_string(),
2518 scope: LdapSearchScope::Subtree,
2519 filter: LdapFilter::And(vec![
2520 LdapFilter::Equality(Attribute::Class.to_string(), "sudohost".to_string()),
2521 LdapFilter::Substring(
2522 Attribute::SudoHost.to_string(),
2523 LdapSubstringFilter {
2524 initial: Some("a".to_string()),
2525 any: vec!["x".to_string()],
2526 final_: Some("z".to_string()),
2527 },
2528 ),
2529 ]),
2530 attrs: vec![
2531 "*".to_string(),
2532 LDAP_ATTR_NAME.to_string(),
2534 Attribute::EntryUuid.to_string(),
2536 ],
2537 };
2538 let r1 = ldaps
2539 .do_search(idms, &sr, &anon_t, Source::Internal)
2540 .await
2541 .unwrap();
2542
2543 assert_eq!(r1.len(), 1);
2545
2546 let sr = SearchRequest {
2549 msgid: 1,
2550 base: "dc=example,dc=com".to_string(),
2551 scope: LdapSearchScope::Subtree,
2552 filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
2553 attrs: vec![
2554 "uid".to_string(),
2555 "uidNumber".to_string(),
2556 "gidNumber".to_string(),
2557 "gecos".to_string(),
2558 "cn".to_string(),
2559 "entryuuid".to_string(),
2560 ],
2561 };
2562 let r1 = ldaps
2563 .do_search(idms, &sr, &anon_t, Source::Internal)
2564 .await
2565 .unwrap();
2566
2567 trace!(?r1);
2568
2569 assert_eq!(r1.len(), 2);
2571 match &r1[0].op {
2572 LdapOp::SearchResultEntry(lsre) => {
2573 assert_entry_contains!(
2574 lsre,
2575 "spn=testperson1@example.com,dc=example,dc=com",
2576 (Attribute::Uid, "testperson1"),
2577 (Attribute::Cn, "testperson1"),
2578 (Attribute::Gecos, "testperson1"),
2579 (Attribute::UidNumber, "12345"),
2580 (Attribute::GidNumber, "12345"),
2581 (Attribute::EntryUuid, "cc8e95b4-c24f-4d68-ba54-8bed76f63930")
2582 );
2583 }
2584 _ => panic!("Oh no"),
2585 };
2586 }
2587
2588 #[idm_test]
2589 async fn test_ldap_compare_request(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
2590 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
2591
2592 {
2594 let acct_uuid = uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930");
2595
2596 let e1 = entry_init!(
2597 (Attribute::Class, EntryClass::Person.to_value()),
2598 (Attribute::Class, EntryClass::Account.to_value()),
2599 (Attribute::Class, EntryClass::PosixAccount.to_value()),
2600 (Attribute::Name, Value::new_iname("testperson1")),
2601 (Attribute::Uuid, Value::Uuid(acct_uuid)),
2602 (Attribute::GidNumber, Value::Uint32(12345)),
2603 (Attribute::Description, Value::new_utf8s("testperson1")),
2604 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2605 );
2606
2607 let mut server_txn = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2608
2609 server_txn
2611 .qs_write
2612 .internal_modify_uuid(
2613 UUID_IDM_UNIX_AUTHENTICATION_READ,
2614 &ModifyList::new_append(Attribute::Member, Value::Refer(UUID_ANONYMOUS)),
2615 )
2616 .expect("Unable to modify UNIX_AUTHENTICATION_READ group");
2617
2618 assert!(server_txn
2619 .qs_write
2620 .internal_create(vec![e1])
2621 .and_then(|_| server_txn.commit())
2622 .is_ok());
2623 }
2624
2625 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
2627 assert_eq!(
2628 anon_t.effective_session,
2629 LdapSession::UnixBind(UUID_ANONYMOUS)
2630 );
2631
2632 #[track_caller]
2633 fn assert_compare_result(r: &[LdapMsg], code: &LdapResultCode) {
2634 assert_eq!(r.len(), 1);
2635 match &r[0].op {
2636 LdapOp::CompareResult(lcr) => {
2637 assert_eq!(&lcr.code, code);
2638 }
2639 _ => panic!("Oh no"),
2640 };
2641 }
2642
2643 let cr = CompareRequest {
2644 msgid: 1,
2645 entry: "name=testperson1,dc=example,dc=com".to_string(),
2646 atype: Attribute::Name.to_string(),
2647 val: "testperson1".to_string(),
2648 };
2649
2650 assert_compare_result(
2651 &ldaps
2652 .do_compare(idms, &cr, &anon_t, Source::Internal)
2653 .await
2654 .unwrap(),
2655 &LdapResultCode::CompareTrue,
2656 );
2657
2658 let cr = CompareRequest {
2659 msgid: 1,
2660 entry: "name=testperson1,dc=example,dc=com".to_string(),
2661 atype: Attribute::GidNumber.to_string(),
2662 val: "12345".to_string(),
2663 };
2664
2665 assert_compare_result(
2666 &ldaps
2667 .do_compare(idms, &cr, &anon_t, Source::Internal)
2668 .await
2669 .unwrap(),
2670 &LdapResultCode::CompareTrue,
2671 );
2672
2673 let cr = CompareRequest {
2674 msgid: 1,
2675 entry: "name=testperson1,dc=example,dc=com".to_string(),
2676 atype: Attribute::Name.to_string(),
2677 val: "other".to_string(),
2678 };
2679 assert_compare_result(
2680 &ldaps
2681 .do_compare(idms, &cr, &anon_t, Source::Internal)
2682 .await
2683 .unwrap(),
2684 &LdapResultCode::CompareFalse,
2685 );
2686
2687 let cr = CompareRequest {
2688 msgid: 1,
2689 entry: "name=other,dc=example,dc=com".to_string(),
2690 atype: Attribute::Name.to_string(),
2691 val: "other".to_string(),
2692 };
2693 assert_compare_result(
2694 &ldaps
2695 .do_compare(idms, &cr, &anon_t, Source::Internal)
2696 .await
2697 .unwrap(),
2698 &LdapResultCode::NoSuchObject,
2699 );
2700
2701 let cr = CompareRequest {
2702 msgid: 1,
2703 entry: "invalidentry".to_string(),
2704 atype: Attribute::Name.to_string(),
2705 val: "other".to_string(),
2706 };
2707 assert!(&ldaps
2708 .do_compare(idms, &cr, &anon_t, Source::Internal)
2709 .await
2710 .is_err());
2711
2712 let cr = CompareRequest {
2713 msgid: 1,
2714 entry: "name=other,dc=example,dc=com".to_string(),
2715 atype: "invalid".to_string(),
2716 val: "other".to_string(),
2717 };
2718 assert_eq!(
2719 &ldaps
2720 .do_compare(idms, &cr, &anon_t, Source::Internal)
2721 .await
2722 .unwrap_err(),
2723 &OperationError::InvalidAttributeName("invalid".to_string()),
2724 );
2725 }
2726
2727 #[idm_test]
2728 async fn test_ldap_maximum_queryable_attributes(
2729 idms: &IdmServer,
2730 _idms_delayed: &IdmServerDelayed,
2731 ) {
2732 let mut server_txn = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
2735
2736 let set_ldap_maximum_queryable_attrs = ModifyEvent::new_internal_invalid(
2737 filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_DOMAIN_INFO))),
2738 ModifyList::new_purge_and_set(Attribute::LdapMaxQueryableAttrs, Value::Uint32(2)),
2739 );
2740 assert!(server_txn
2741 .qs_write
2742 .modify(&set_ldap_maximum_queryable_attrs)
2743 .and_then(|_| server_txn.commit())
2744 .is_ok());
2745
2746 let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
2747
2748 let usr_uuid = Uuid::new_v4();
2749 let grp_uuid = Uuid::new_v4();
2750 let app_uuid = Uuid::new_v4();
2751 let app_name = "testapp1";
2752
2753 {
2755 let e1 = entry_init!(
2756 (Attribute::Class, EntryClass::Object.to_value()),
2757 (Attribute::Class, EntryClass::Account.to_value()),
2758 (Attribute::Class, EntryClass::Person.to_value()),
2759 (Attribute::Name, Value::new_iname("testperson1")),
2760 (Attribute::Uuid, Value::Uuid(usr_uuid)),
2761 (Attribute::Description, Value::new_utf8s("testperson1")),
2762 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2763 );
2764
2765 let e2 = entry_init!(
2766 (Attribute::Class, EntryClass::Object.to_value()),
2767 (Attribute::Class, EntryClass::Group.to_value()),
2768 (Attribute::Name, Value::new_iname("testgroup1")),
2769 (Attribute::Uuid, Value::Uuid(grp_uuid))
2770 );
2771
2772 let e3 = entry_init!(
2773 (Attribute::Class, EntryClass::Object.to_value()),
2774 (Attribute::Class, EntryClass::Account.to_value()),
2775 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
2776 (Attribute::Class, EntryClass::Application.to_value()),
2777 (Attribute::DisplayName, Value::new_utf8s("Application")),
2778 (Attribute::Name, Value::new_iname(app_name)),
2779 (Attribute::Uuid, Value::Uuid(app_uuid)),
2780 (Attribute::LinkedGroup, Value::Refer(grp_uuid))
2781 );
2782
2783 let ct = duration_from_epoch_now();
2784 let mut server_txn = idms.proxy_write(ct).await.unwrap();
2785 assert!(server_txn
2786 .qs_write
2787 .internal_create(vec![e1, e2, e3])
2788 .and_then(|_| server_txn.commit())
2789 .is_ok());
2790 }
2791
2792 let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
2794 assert_eq!(
2795 anon_t.effective_session,
2796 LdapSession::UnixBind(UUID_ANONYMOUS)
2797 );
2798
2799 let invalid_search = SearchRequest {
2800 msgid: 1,
2801 base: "dc=example,dc=com".to_string(),
2802 scope: LdapSearchScope::Subtree,
2803 filter: LdapFilter::Present(Attribute::ObjectClass.to_string()),
2804 attrs: vec![
2805 "objectClass".to_string(),
2806 "cn".to_string(),
2807 "givenName".to_string(),
2808 ],
2809 };
2810
2811 let valid_search = SearchRequest {
2812 msgid: 1,
2813 base: "dc=example,dc=com".to_string(),
2814 scope: LdapSearchScope::Subtree,
2815 filter: LdapFilter::Present(Attribute::ObjectClass.to_string()),
2816 attrs: vec!["objectClass: person".to_string()],
2817 };
2818
2819 let invalid_res: Result<Vec<LdapMsg>, OperationError> = ldaps
2820 .do_search(idms, &invalid_search, &anon_t, Source::Internal)
2821 .await;
2822
2823 let valid_res: Result<Vec<LdapMsg>, OperationError> = ldaps
2824 .do_search(idms, &valid_search, &anon_t, Source::Internal)
2825 .await;
2826
2827 assert_eq!(invalid_res, Err(OperationError::ResourceLimit));
2828 assert!(valid_res.is_ok());
2829 }
2830}