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