1use std::{
3 collections::{HashMap, HashSet},
4 convert::TryFrom,
5 hash::{Hash, Hasher},
6};
7
8use educe::Educe;
9use oid::ObjectIdentifier;
10
11use enum_as_inner::EnumAsInner;
12
13#[cfg(feature = "chumsky")]
14use chumsky::{prelude::*, text::digits};
15
16#[cfg(feature = "chumsky")]
17use itertools::Itertools;
18
19#[cfg(feature = "chumsky")]
20use ariadne::{Color, Fmt, Label, Report, ReportKind, Source};
21
22#[cfg(feature = "serde")]
23use serde::{de::SeqAccess, ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer};
24
25#[cfg(feature = "diff")]
26use diff::Diff;
27
28#[cfg(feature = "chumsky")]
32#[derive(Debug)]
33pub struct ChumskyError {
34 pub description: String,
36 pub source: String,
38 pub errors: Vec<chumsky::error::Simple<char>>,
40}
41
42#[cfg(feature = "chumsky")]
43impl std::fmt::Display for ChumskyError {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 for e in &self.errors {
46 let msg = format!(
47 "While parsing {}: {}{}, expected {}",
48 self.description,
49 if e.found().is_some() {
50 "Unexpected token"
51 } else {
52 "Unexpected end of input"
53 },
54 if let Some(label) = e.label() {
55 format!(" while parsing {}", label)
56 } else {
57 String::new()
58 },
59 if e.expected().len() == 0 {
60 "end of input".to_string()
61 } else {
62 e.expected()
63 .map(|expected| match expected {
64 Some(expected) => expected.to_string(),
65 None => "end of input".to_string(),
66 })
67 .collect::<Vec<_>>()
68 .join(", ")
69 },
70 );
71
72 let report = Report::build(ReportKind::Error, e.span())
73 .with_code(3)
74 .with_message(msg)
75 .with_label(
76 Label::new(e.span())
77 .with_message(format!(
78 "Unexpected {}",
79 e.found()
80 .map(|c| format!("token {}", c.fg(Color::Red)))
81 .unwrap_or_else(|| "end of input".to_string())
82 ))
83 .with_color(Color::Red),
84 );
85
86 let report = match e.reason() {
87 chumsky::error::SimpleReason::Unclosed { span, delimiter } => report.with_label(
88 Label::new(span.clone())
89 .with_message(format!(
90 "Unclosed delimiter {}",
91 delimiter.fg(Color::Yellow)
92 ))
93 .with_color(Color::Yellow),
94 ),
95 chumsky::error::SimpleReason::Unexpected => report,
96 chumsky::error::SimpleReason::Custom(msg) => report.with_label(
97 Label::new(e.span())
98 .with_message(format!("{}", msg.fg(Color::Yellow)))
99 .with_color(Color::Yellow),
100 ),
101 };
102
103 let mut s: Vec<u8> = Vec::new();
104 report
105 .finish()
106 .write(Source::from(&self.source), &mut s)
107 .map_err(|_| <std::fmt::Error as std::default::Default>::default())?;
108 let s = std::str::from_utf8(&s).expect("Expected ariadne to generate valid UTF-8");
109 write!(f, "{}", s)?;
110 }
111 Ok(())
112 }
113}
114
115#[cfg(feature = "chumsky")]
116impl std::error::Error for ChumskyError {
117 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
118 None
119 }
120}
121
122#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
129pub struct RootDSE {
130 pub supported_ldap_version: String,
132 pub supported_controls: Vec<ObjectIdentifier>,
136 pub supported_extensions: Vec<ObjectIdentifier>,
140 pub supported_features: Vec<ObjectIdentifier>,
144 pub supported_sasl_mechanisms: Vec<String>,
148 pub config_context: String,
152 pub naming_contexts: Vec<String>,
159 pub subschema_subentry: String,
166}
167
168impl std::fmt::Debug for RootDSE {
169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
170 f.debug_struct("RootDSE")
171 .field("supported_ldap_version", &self.supported_ldap_version)
172 .field(
173 "supported_controls",
174 &self
175 .supported_controls
176 .iter()
177 .map(|x| x.into())
178 .collect::<Vec<String>>(),
179 )
180 .field(
181 "supported_extensions",
182 &self
183 .supported_extensions
184 .iter()
185 .map(|x| x.into())
186 .collect::<Vec<String>>(),
187 )
188 .field(
189 "supported_features",
190 &self
191 .supported_features
192 .iter()
193 .map(|x| x.into())
194 .collect::<Vec<String>>(),
195 )
196 .field("supported_sasl_mechanisms", &self.supported_sasl_mechanisms)
197 .field("config_context", &self.config_context)
198 .field("naming_contexts", &self.naming_contexts)
199 .field("subschema_subentry", &self.subschema_subentry)
200 .finish()
201 }
202}
203
204#[cfg(feature = "chumsky")]
206pub fn oid_parser() -> impl Parser<char, ObjectIdentifier, Error = Simple<char>> {
207 digits(10).separated_by(just('.')).try_map(|x, span| {
208 x.into_iter()
209 .join(".")
210 .try_into()
211 .map_err(|e| Simple::custom(span, format!("{:?}", e)))
212 })
213}
214
215#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug, Hash)]
218#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
219pub struct KeyString(pub String);
220
221impl std::fmt::Display for KeyString {
222 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
223 std::fmt::Display::fmt(&self.0, f)?;
224 Ok(())
225 }
226}
227
228impl KeyString {
229 pub fn describes_case_insensitive_match(&self) -> bool {
235 match self {
236 KeyString(s) if s == "objectIdentifierMatch" => true,
237 KeyString(s) if s == "caseIgnoreMatch" => true,
238 KeyString(s) if s == "caseIgnoreListMatch" => true,
239 KeyString(s) if s == "caseIgnoreIA5Match" => true,
240 KeyString(s) if s == "caseIgnoreListSubstringsMatch" => true,
241 KeyString(s) if s == "caseIgnoreSubstringsMatch" => true,
242 KeyString(s) if s == "caseIgnoreOrderingMatch" => true,
243 KeyString(s) if s == "caseIgnoreIA5SubstringsMatch" => true,
244 _ => false,
245 }
246 }
247
248 pub fn to_lowercase(&self) -> KeyString {
250 let KeyString(s) = self;
251 KeyString(s.to_lowercase())
252 }
253}
254
255impl TryFrom<KeyStringOrOID> for KeyString {
256 type Error = ();
257
258 fn try_from(value: KeyStringOrOID) -> Result<Self, Self::Error> {
259 match value {
260 KeyStringOrOID::KeyString(ks) => Ok(ks),
261 KeyStringOrOID::OID(_) => Err(()),
262 }
263 }
264}
265
266impl TryFrom<&KeyStringOrOID> for KeyString {
267 type Error = ();
268
269 fn try_from(value: &KeyStringOrOID) -> Result<Self, Self::Error> {
270 match value {
271 KeyStringOrOID::KeyString(ks) => Ok(ks.to_owned()),
272 KeyStringOrOID::OID(_) => Err(()),
273 }
274 }
275}
276
277#[cfg(feature = "chumsky")]
279pub fn keystring_parser() -> impl Parser<char, KeyString, Error = Simple<char>> {
280 filter(|c: &char| c.is_ascii_alphabetic())
281 .chain(filter(|c: &char| c.is_ascii_alphanumeric() || *c == '-' || *c == ';').repeated())
282 .collect::<String>()
283 .map(KeyString)
284}
285
286#[cfg(feature = "chumsky")]
288pub fn quoted_keystring_parser() -> impl Parser<char, KeyString, Error = Simple<char>> {
289 keystring_parser().delimited_by(just('\''), just('\''))
290}
291
292pub fn hash_oid<H: Hasher>(s: &ObjectIdentifier, state: &mut H) {
295 Hash::hash(&format!("{s:?}"), state);
296}
297
298#[derive(Clone, Debug, EnumAsInner, Educe)]
301#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
302#[educe(PartialEq, Eq, Hash)]
303pub enum KeyStringOrOID {
304 #[cfg_attr(feature = "serde", serde(rename = "key_string"))]
306 KeyString(KeyString),
307 #[cfg_attr(feature = "serde", serde(rename = "oid"))]
309 OID(#[educe(Hash(method = "hash_oid"))] ObjectIdentifier),
310}
311
312impl PartialOrd for KeyStringOrOID {
313 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
314 Some(self.cmp(other))
315 }
316}
317
318impl Ord for KeyStringOrOID {
319 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
320 match (self, other) {
321 (KeyStringOrOID::KeyString(s1), KeyStringOrOID::KeyString(s2)) => s1.cmp(s2),
322 (KeyStringOrOID::KeyString(_), KeyStringOrOID::OID(_)) => std::cmp::Ordering::Less,
323 (KeyStringOrOID::OID(_), KeyStringOrOID::KeyString(_)) => std::cmp::Ordering::Greater,
324 (KeyStringOrOID::OID(oid1), KeyStringOrOID::OID(oid2)) => {
325 let s1: String = oid1.into();
326 let s2: String = oid2.into();
327 s1.cmp(&s2)
328 }
329 }
330 }
331}
332
333impl std::fmt::Display for KeyStringOrOID {
334 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
335 match &self {
336 Self::KeyString(s) => {
337 std::fmt::Display::fmt(s, f)?;
338 Ok(())
339 }
340 Self::OID(oid) => {
341 let string_oid: String = oid.clone().into();
342 std::fmt::Display::fmt(&string_oid, f)?;
343 Ok(())
344 }
345 }
346 }
347}
348
349#[cfg(feature = "chumsky")]
350impl TryFrom<&str> for KeyStringOrOID {
351 type Error = ChumskyError;
352 fn try_from(value: &str) -> Result<Self, Self::Error> {
353 (keystring_or_oid_parser().then_ignore(chumsky::primitive::end()))
354 .parse(value)
355 .map_err(|e| ChumskyError {
356 description: "keystring or OID".to_string(),
357 source: value.to_string(),
358 errors: e,
359 })
360 }
361}
362
363#[cfg(feature = "chumsky")]
364impl TryFrom<String> for KeyStringOrOID {
365 type Error = ChumskyError;
366 fn try_from(value: String) -> Result<Self, Self::Error> {
367 (keystring_or_oid_parser().then_ignore(chumsky::primitive::end()))
368 .parse(value.to_owned())
369 .map_err(|e| ChumskyError {
370 description: "keystring or OID".to_string(),
371 source: value.to_string(),
372 errors: e,
373 })
374 }
375}
376
377#[cfg(feature = "chumsky")]
378impl TryFrom<&String> for KeyStringOrOID {
379 type Error = ChumskyError;
380 fn try_from(value: &String) -> Result<Self, Self::Error> {
381 (keystring_or_oid_parser().then_ignore(chumsky::primitive::end()))
382 .parse(value.to_owned())
383 .map_err(|e| ChumskyError {
384 description: "keystring or OID".to_string(),
385 source: value.to_string(),
386 errors: e,
387 })
388 }
389}
390
391#[cfg(feature = "chumsky")]
392impl std::str::FromStr for KeyStringOrOID {
393 type Err = ChumskyError;
394
395 fn from_str(s: &str) -> Result<Self, Self::Err> {
396 (keystring_or_oid_parser().then_ignore(chumsky::primitive::end()))
397 .parse(s.to_owned())
398 .map_err(|e| ChumskyError {
399 description: "keystring or OID".to_string(),
400 source: s.to_string(),
401 errors: e,
402 })
403 }
404}
405
406impl From<&KeyStringOrOID> for KeyStringOrOID {
407 fn from(value: &KeyStringOrOID) -> Self {
408 value.to_owned()
409 }
410}
411
412impl From<KeyString> for KeyStringOrOID {
413 fn from(value: KeyString) -> Self {
414 KeyStringOrOID::KeyString(value)
415 }
416}
417
418impl From<&KeyString> for KeyStringOrOID {
419 fn from(value: &KeyString) -> Self {
420 KeyStringOrOID::KeyString(value.to_owned())
421 }
422}
423
424impl From<ObjectIdentifier> for KeyStringOrOID {
425 fn from(value: ObjectIdentifier) -> Self {
426 KeyStringOrOID::OID(value)
427 }
428}
429
430impl From<&ObjectIdentifier> for KeyStringOrOID {
431 fn from(value: &ObjectIdentifier) -> Self {
432 KeyStringOrOID::OID(value.to_owned())
433 }
434}
435
436impl TryFrom<KeyStringOrOID> for ObjectIdentifier {
437 type Error = ();
438
439 fn try_from(value: KeyStringOrOID) -> Result<Self, Self::Error> {
440 match value {
441 KeyStringOrOID::OID(oid) => Ok(oid),
442 KeyStringOrOID::KeyString(_) => Err(()),
443 }
444 }
445}
446
447impl TryFrom<&KeyStringOrOID> for ObjectIdentifier {
448 type Error = ();
449
450 fn try_from(value: &KeyStringOrOID) -> Result<Self, Self::Error> {
451 match value {
452 KeyStringOrOID::OID(oid) => Ok(oid.to_owned()),
453 KeyStringOrOID::KeyString(_) => Err(()),
454 }
455 }
456}
457
458#[cfg(feature = "chumsky")]
460pub fn keystring_or_oid_parser() -> impl Parser<char, KeyStringOrOID, Error = Simple<char>> {
461 keystring_parser()
462 .map(KeyStringOrOID::KeyString)
463 .or(oid_parser().map(KeyStringOrOID::OID))
464}
465
466#[derive(Clone, Educe)]
469#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
470#[educe(PartialEq, Eq, Hash)]
471pub struct OIDWithLength {
472 #[educe(Hash(method = "hash_oid"))]
474 pub oid: ObjectIdentifier,
475 pub length: Option<usize>,
477}
478
479impl From<OIDWithLength> for ObjectIdentifier {
480 fn from(value: OIDWithLength) -> Self {
481 value.oid
482 }
483}
484
485impl From<&OIDWithLength> for ObjectIdentifier {
486 fn from(value: &OIDWithLength) -> Self {
487 value.oid.to_owned()
488 }
489}
490
491impl std::fmt::Debug for OIDWithLength {
492 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
493 let string_oid: String = self.oid.clone().into();
494 f.debug_struct("OIDWithLength")
495 .field("oid", &string_oid)
496 .field("length", &self.length)
497 .finish()
498 }
499}
500
501#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
507#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
508pub struct RelativeDistinguishedName {
509 #[cfg_attr(
511 feature = "serde",
512 serde(serialize_with = "serialize_rdn", deserialize_with = "deserialize_rdn")
513 )]
514 pub attributes: Vec<(KeyStringOrOID, Vec<u8>)>,
515}
516
517impl std::fmt::Display for RelativeDistinguishedName {
518 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
519 let mut first = true;
520 for (k, v) in &self.attributes {
521 if !first {
522 write!(f, "+")?;
523 } else {
524 first = false;
525 }
526 write!(f, "{}", k)?;
527 write!(f, "=")?;
528 if let Ok(s) = std::str::from_utf8(v) {
529 write!(f, "{}", s)?;
530 } else {
531 write!(f, "#{}", hex::encode(v))?;
532 }
533 }
534 Ok(())
535 }
536}
537
538#[cfg(feature = "chumsky")]
539impl TryFrom<&str> for RelativeDistinguishedName {
540 type Error = ChumskyError;
541
542 fn try_from(value: &str) -> Result<Self, Self::Error> {
543 (rdn_parser().then_ignore(chumsky::primitive::end()))
544 .parse(value)
545 .map_err(|e| ChumskyError {
546 description: "relative distinguished name".to_string(),
547 source: value.to_string(),
548 errors: e,
549 })
550 }
551}
552
553#[cfg(feature = "chumsky")]
554impl TryFrom<String> for RelativeDistinguishedName {
555 type Error = ChumskyError;
556
557 fn try_from(value: String) -> Result<Self, Self::Error> {
558 (rdn_parser().then_ignore(chumsky::primitive::end()))
559 .parse(value.to_owned())
560 .map_err(|e| ChumskyError {
561 description: "relative distinguished name".to_string(),
562 source: value.to_string(),
563 errors: e,
564 })
565 }
566}
567
568#[cfg(feature = "chumsky")]
569impl std::str::FromStr for RelativeDistinguishedName {
570 type Err = ChumskyError;
571
572 fn from_str(s: &str) -> Result<Self, Self::Err> {
573 (rdn_parser().then_ignore(chumsky::primitive::end()))
574 .parse(s)
575 .map_err(|e| ChumskyError {
576 description: "relative distinguished name".to_string(),
577 source: s.to_string(),
578 errors: e,
579 })
580 }
581}
582
583impl From<RelativeDistinguishedName> for String {
584 fn from(rdn: RelativeDistinguishedName) -> Self {
585 rdn.to_string()
586 }
587}
588
589#[cfg(feature = "serde")]
592pub fn serialize_rdn<S>(xs: &[(KeyStringOrOID, Vec<u8>)], s: S) -> Result<S::Ok, S::Error>
593where
594 S: Serializer,
595{
596 let mut seq = s.serialize_seq(Some(xs.len()))?;
597 for e @ (k, v) in xs.iter() {
598 if let Ok(s) = std::str::from_utf8(v) {
599 seq.serialize_element(&(k, s))?;
600 } else {
601 seq.serialize_element(e)?;
602 }
603 }
604 seq.end()
605}
606
607#[cfg(feature = "serde")]
609pub fn deserialize_rdn<'de, D>(d: D) -> Result<Vec<(KeyStringOrOID, Vec<u8>)>, D::Error>
610where
611 D: Deserializer<'de>,
612{
613 #[derive(Deserialize)]
615 #[serde(untagged)]
616 enum StringOrBytes {
617 String(String),
619 Bytes(Vec<u8>),
621 }
622
623 struct RDNVisitor;
625
626 impl<'de> serde::de::Visitor<'de> for RDNVisitor {
627 type Value = Vec<(KeyStringOrOID, StringOrBytes)>;
628
629 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
630 write!(formatter, "an array of tuples of attribute name and attribute value (either a string or a sequence of integers)")
631 }
632
633 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
634 where
635 A: SeqAccess<'de>,
636 {
637 let mut result = Vec::new();
638 while let Some(e) = seq.next_element()? {
639 result.push(e);
640 }
641 Ok(result)
642 }
643 }
644
645 let parse_result = d.deserialize_seq(RDNVisitor)?;
646 let mut results = Vec::new();
647 for (ref k, ref v) in parse_result {
648 match v {
649 StringOrBytes::String(s) => {
650 results.push((k.to_owned(), s.as_bytes().to_vec()));
651 }
652 StringOrBytes::Bytes(b) => results.push((k.to_owned(), b.to_vec())),
653 }
654 }
655 Ok(results)
656}
657
658#[cfg(feature = "chumsky")]
660pub fn hex_byte_parser() -> impl Parser<char, u8, Error = Simple<char>> {
661 filter(|c: &char| c.is_ascii_hexdigit())
662 .repeated()
663 .exactly(2)
664 .collect::<String>()
665 .try_map(|ds, span| {
666 hex::decode(ds.as_bytes()).map_err(|e| Simple::custom(span, format!("{:?}", e)))
667 })
668 .map(|v: Vec<u8>| v.first().unwrap().to_owned())
669}
670
671#[cfg(feature = "chumsky")]
673pub fn rdn_attribute_binary_value_parser() -> impl Parser<char, Vec<u8>, Error = Simple<char>> {
674 just('#').ignore_then(hex_byte_parser().repeated())
675}
676
677#[cfg(feature = "chumsky")]
679pub fn rdn_attribute_string_value_parser() -> impl Parser<char, Vec<u8>, Error = Simple<char>> {
680 none_of(",+\"\\<>;")
681 .or(just('\\').ignore_then(one_of(" ,+\"\\<>;")))
682 .or(just('\\').ignore_then(hex_byte_parser().map(|s| s as char)))
683 .repeated()
684 .collect::<String>()
685 .map(|s| s.as_bytes().to_vec())
686}
687
688#[cfg(feature = "chumsky")]
690pub fn rdn_attribute_value_parser() -> impl Parser<char, Vec<u8>, Error = Simple<char>> {
691 rdn_attribute_binary_value_parser().or(rdn_attribute_string_value_parser())
692}
693
694#[cfg(feature = "chumsky")]
696pub fn rdn_parser() -> impl Parser<char, RelativeDistinguishedName, Error = Simple<char>> {
697 keystring_or_oid_parser()
698 .then(just('=').ignore_then(rdn_attribute_value_parser()))
699 .separated_by(just('+'))
700 .at_least(1)
701 .map(|attributes| RelativeDistinguishedName { attributes })
702}
703
704#[derive(Debug, PartialEq, Eq, Clone, Hash)]
710#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
711pub struct DistinguishedName {
712 pub rdns: Vec<RelativeDistinguishedName>,
714}
715
716impl DistinguishedName {
717 pub fn is_empty(&self) -> bool {
719 self.rdns.is_empty()
720 }
721 pub fn parent(&self) -> Option<DistinguishedName> {
724 if self.is_empty() {
725 None
726 } else {
727 Some(DistinguishedName {
728 rdns: self.rdns.iter().skip(1).cloned().collect(),
729 })
730 }
731 }
732
733 pub fn is_ancestor_of(&self, other: &DistinguishedName) -> bool {
738 let mut it = self.rdns.iter().rev();
739 let mut other_it = other.rdns.iter().rev();
740 loop {
741 let e = it.next();
742 let other_e = other_it.next();
743 match (e, other_e) {
744 (None, None) => {
745 return false;
747 }
748 (Some(_), None) => {
749 return false;
751 }
752 (None, Some(_)) => {
753 return true;
756 }
757 (Some(e), Some(other_e)) => {
758 if e != other_e {
759 return false;
762 }
763 }
766 }
767 }
768 }
769
770 pub fn add_suffix(&self, other: &DistinguishedName) -> DistinguishedName {
772 DistinguishedName {
773 rdns: [self.rdns.to_vec(), other.rdns.to_vec()].concat(),
774 }
775 }
776
777 pub fn strip_suffix(&self, other: &DistinguishedName) -> Option<DistinguishedName> {
779 if !other.is_ancestor_of(self) {
780 None
781 } else {
782 let self_len = self.rdns.len();
783 let other_len = other.rdns.len();
784 Some(DistinguishedName {
785 rdns: self.rdns.split_at(self_len - other_len).0.to_vec(),
786 })
787 }
788 }
789}
790
791#[cfg(feature = "chumsky")]
792impl TryFrom<&str> for DistinguishedName {
793 type Error = ChumskyError;
794
795 fn try_from(value: &str) -> Result<Self, Self::Error> {
796 (dn_parser().then_ignore(chumsky::primitive::end()))
797 .parse(value)
798 .map_err(|e| ChumskyError {
799 description: "distinguished name".to_string(),
800 source: value.to_string(),
801 errors: e,
802 })
803 }
804}
805
806#[cfg(feature = "chumsky")]
807impl TryFrom<String> for DistinguishedName {
808 type Error = ChumskyError;
809
810 fn try_from(value: String) -> Result<Self, Self::Error> {
811 (dn_parser().then_ignore(chumsky::primitive::end()))
812 .parse(value.to_owned())
813 .map_err(|e| ChumskyError {
814 description: "distinguished name".to_string(),
815 source: value.to_string(),
816 errors: e,
817 })
818 }
819}
820
821#[cfg(feature = "chumsky")]
822impl std::str::FromStr for DistinguishedName {
823 type Err = ChumskyError;
824
825 fn from_str(s: &str) -> Result<Self, Self::Err> {
826 (dn_parser().then_ignore(chumsky::primitive::end()))
827 .parse(s)
828 .map_err(|e| ChumskyError {
829 description: "distinguished name".to_string(),
830 source: s.to_string(),
831 errors: e,
832 })
833 }
834}
835
836impl From<DistinguishedName> for String {
837 fn from(dn: DistinguishedName) -> Self {
838 dn.to_string()
839 }
840}
841
842impl std::fmt::Display for DistinguishedName {
843 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
844 let mut first = true;
845 for rdn in &self.rdns {
846 if !first {
847 write!(f, ",")?;
848 } else {
849 first = false;
850 }
851 write!(f, "{}", rdn)?;
852 }
853 Ok(())
854 }
855}
856
857impl PartialOrd for DistinguishedName {
858 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
859 Some(self.cmp(other))
860 }
861}
862
863impl Ord for DistinguishedName {
864 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
865 self.rdns
866 .iter()
867 .rev()
868 .zip(other.rdns.iter().rev())
869 .map(|(a, b)| a.cmp(b))
870 .fold(std::cmp::Ordering::Equal, |acc, e| acc.then(e))
871 .then(self.rdns.len().cmp(&other.rdns.len()))
872 }
873}
874
875#[cfg(feature = "chumsky")]
877pub fn dn_parser() -> impl Parser<char, DistinguishedName, Error = Simple<char>> {
878 rdn_parser()
879 .separated_by(just(','))
880 .map(|rdns| DistinguishedName { rdns })
881}
882
883#[derive(Debug, Clone, PartialEq, Eq)]
887#[cfg_attr(feature = "diff", derive(Diff))]
888#[cfg_attr(feature = "diff", diff(attr(#[derive(Debug)] #[allow(missing_docs)])))]
889#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
890pub struct LDAPEntry {
891 pub dn: String,
893 pub attrs: HashMap<String, Vec<String>>,
895 pub bin_attrs: HashMap<String, Vec<Vec<u8>>>,
897}
898
899impl LDAPEntry {
900 pub fn combined_attrs(&self) -> Vec<(Vec<u8>, HashSet<Vec<u8>>)> {
902 let mut result: HashMap<Vec<u8>, HashSet<Vec<u8>>> = HashMap::new();
903 for (attr_name, attr_values) in &self.attrs {
904 let attr_name = attr_name.as_bytes().to_vec();
905 let attr_values = attr_values.iter().map(|x| x.as_bytes().to_vec()).collect();
906 if let Some(values) = result.get_mut(&attr_name) {
907 values.extend(attr_values);
908 } else {
909 result.insert(attr_name, attr_values);
910 }
911 }
912 for (attr_name, attr_values) in &self.bin_attrs {
913 let attr_name = attr_name.as_bytes().to_vec();
914 let attr_values = attr_values.iter().map(|x| x.to_vec()).collect();
915 if let Some(values) = result.get_mut(&attr_name) {
916 values.extend(attr_values);
917 } else {
918 result.insert(attr_name, attr_values);
919 }
920 }
921 result.into_iter().collect()
922 }
923}
924
925#[cfg(feature = "ldap3")]
926impl From<ldap3::SearchEntry> for LDAPEntry {
927 fn from(entry: ldap3::SearchEntry) -> Self {
928 Self {
929 dn: entry.dn,
930 attrs: entry.attrs,
931 bin_attrs: entry.bin_attrs,
932 }
933 }
934}
935
936#[cfg(feature = "ldap3")]
937impl From<LDAPEntry> for ldap3::SearchEntry {
938 fn from(entry: LDAPEntry) -> Self {
939 Self {
940 dn: entry.dn,
941 attrs: entry.attrs,
942 bin_attrs: entry.bin_attrs,
943 }
944 }
945}
946
947#[derive(Debug, Clone, EnumAsInner)]
951#[cfg(feature = "ldap3")]
952pub enum LDAPOperation {
953 Add(LDAPEntry),
955 Delete {
957 dn: String,
959 },
960 Modify {
962 dn: String,
964 mods: Vec<ldap3::Mod<String>>,
966 bin_mods: Vec<ldap3::Mod<Vec<u8>>>,
968 },
969}
970
971#[cfg(feature = "ldap3")]
972impl LDAPOperation {
973 #[cfg(feature = "chumsky")]
975 pub fn operation_apply_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
976 match (self, other) {
977 (
978 LDAPOperation::Add(entry1 @ LDAPEntry { .. }),
979 LDAPOperation::Add(entry2 @ LDAPEntry { .. }),
980 ) => {
981 let parsed_dn1: Result<DistinguishedName, _> =
982 dn_parser().parse(entry1.dn.to_owned());
983 let parsed_dn2: Result<DistinguishedName, _> =
984 dn_parser().parse(entry2.dn.to_owned());
985 if let (Ok(parsed_dn1), Ok(parsed_dn2)) = (parsed_dn1, parsed_dn2) {
986 Some(parsed_dn1.cmp(&parsed_dn2))
987 } else {
988 None
989 }
990 }
991 (op1 @ LDAPOperation::Delete { .. }, op2 @ LDAPOperation::Delete { .. }) => {
992 let parsed_dn1: Result<DistinguishedName, _> =
993 dn_parser().parse(op1.as_delete().unwrap().to_owned());
994 let parsed_dn2: Result<DistinguishedName, _> =
995 dn_parser().parse(op2.as_delete().unwrap().to_owned());
996 if let (Ok(parsed_dn1), Ok(parsed_dn2)) = (parsed_dn1, parsed_dn2) {
997 Some(parsed_dn1.cmp(&parsed_dn2))
998 } else {
999 None
1000 }
1001 }
1002 _ => None,
1003 }
1004 }
1005}
1006
1007#[cfg(test)]
1008mod test {
1009 use super::*;
1010 use pretty_assertions::assert_eq;
1011
1012 #[cfg(feature = "chumsky")]
1013 #[test]
1014 fn test_parse_oid() {
1015 assert!(oid_parser().parse("1.2.3.4").is_ok());
1016 }
1017
1018 #[cfg(feature = "chumsky")]
1019 #[test]
1020 fn test_parse_oid_value() {
1021 assert_eq!(
1022 oid_parser().parse("1.2.3.4"),
1023 Ok("1.2.3.4".to_string().try_into().unwrap())
1024 );
1025 }
1026
1027 #[cfg(feature = "chumsky")]
1028 #[test]
1029 fn test_dn_parser_empty_dn() {
1030 assert_eq!(
1031 dn_parser().parse(""),
1032 Ok(DistinguishedName { rdns: vec![] })
1033 )
1034 }
1035
1036 #[cfg(feature = "chumsky")]
1037 #[test]
1038 fn test_dn_parser_single_rdn_single_string_attribute() {
1039 assert_eq!(
1040 dn_parser().parse("cn=Foobar"),
1041 Ok(DistinguishedName {
1042 rdns: vec![RelativeDistinguishedName {
1043 attributes: vec![(
1044 KeyStringOrOID::KeyString(KeyString("cn".to_string())),
1045 "Foobar".as_bytes().to_vec()
1046 )]
1047 }]
1048 })
1049 )
1050 }
1051
1052 #[cfg(feature = "chumsky")]
1053 #[test]
1054 fn test_dn_parser_single_rdn_single_string_attribute_with_escaped_comma() {
1055 assert_eq!(
1056 dn_parser().parse("cn=Foo\\,bar"),
1057 Ok(DistinguishedName {
1058 rdns: vec![RelativeDistinguishedName {
1059 attributes: vec![(
1060 KeyStringOrOID::KeyString(KeyString("cn".to_string())),
1061 "Foo,bar".as_bytes().to_vec()
1062 )]
1063 }]
1064 })
1065 )
1066 }
1067
1068 #[cfg(feature = "chumsky")]
1069 #[test]
1070 fn test_dn_parser_single_rdn_single_binary_attribute() {
1071 assert_eq!(
1072 dn_parser().parse("cn=#466f6f626172"),
1073 Ok(DistinguishedName {
1074 rdns: vec![RelativeDistinguishedName {
1075 attributes: vec![(
1076 KeyStringOrOID::KeyString(KeyString("cn".to_string())),
1077 "Foobar".as_bytes().to_vec()
1078 )]
1079 }]
1080 })
1081 )
1082 }
1083
1084 #[cfg(feature = "chumsky")]
1085 #[test]
1086 fn test_dn_parser_single_rdn_multiple_string_attributes() {
1087 assert_eq!(
1088 dn_parser().parse("cn=Foo\\,bar+uid=foobar"),
1089 Ok(DistinguishedName {
1090 rdns: vec![RelativeDistinguishedName {
1091 attributes: vec![
1092 (
1093 KeyStringOrOID::KeyString(KeyString("cn".to_string())),
1094 "Foo,bar".as_bytes().to_vec()
1095 ),
1096 (
1097 KeyStringOrOID::KeyString(KeyString("uid".to_string())),
1098 "foobar".as_bytes().to_vec()
1099 ),
1100 ]
1101 }]
1102 })
1103 )
1104 }
1105
1106 #[cfg(feature = "chumsky")]
1107 #[test]
1108 fn test_dn_parser_multiple_rdns() {
1109 assert_eq!(
1110 dn_parser().parse("cn=Foo\\,bar,uid=foobar"),
1111 Ok(DistinguishedName {
1112 rdns: vec![
1113 RelativeDistinguishedName {
1114 attributes: vec![(
1115 KeyStringOrOID::KeyString(KeyString("cn".to_string())),
1116 "Foo,bar".as_bytes().to_vec()
1117 )]
1118 },
1119 RelativeDistinguishedName {
1120 attributes: vec![(
1121 KeyStringOrOID::KeyString(KeyString("uid".to_string())),
1122 "foobar".as_bytes().to_vec()
1123 )]
1124 },
1125 ]
1126 })
1127 )
1128 }
1129
1130 #[test]
1131 fn test_dn_cmp() {
1132 assert_eq!(
1133 DistinguishedName { rdns: vec![] }.cmp(&DistinguishedName {
1134 rdns: vec![RelativeDistinguishedName {
1135 attributes: vec![(
1136 KeyStringOrOID::KeyString(KeyString("cn".to_string())),
1137 "Foo,bar".as_bytes().to_vec()
1138 )]
1139 }]
1140 }),
1141 std::cmp::Ordering::Less
1142 )
1143 }
1144
1145 #[cfg(feature = "serde")]
1146 #[test]
1147 fn test_serialize_json_oid() -> Result<(), Box<dyn std::error::Error>> {
1148 let oid: ObjectIdentifier = "1.2.3.4".to_string().try_into().unwrap();
1149 let result = serde_json::to_string(&oid)?;
1150 assert_eq!(result, "\"1.2.3.4\"".to_string());
1151 Ok(())
1152 }
1153
1154 #[cfg(feature = "serde")]
1155 #[test]
1156 fn test_deserialize_json_oid() -> Result<(), Box<dyn std::error::Error>> {
1157 let expected: ObjectIdentifier = "1.2.3.4".to_string().try_into().unwrap();
1158 let result: ObjectIdentifier = serde_json::from_str("\"1.2.3.4\"")?;
1159 assert_eq!(result, expected);
1160 Ok(())
1161 }
1162
1163 #[cfg(feature = "serde")]
1164 #[test]
1165 fn test_serialize_json_keystring() -> Result<(), Box<dyn std::error::Error>> {
1166 let ks: KeyString = KeyString("foo".to_string());
1167 let result = serde_json::to_string(&ks)?;
1168 assert_eq!(result, "\"foo\"".to_string());
1169 Ok(())
1170 }
1171
1172 #[cfg(feature = "serde")]
1173 #[test]
1174 fn test_deserialize_json_keystring() -> Result<(), Box<dyn std::error::Error>> {
1175 let expected: KeyString = KeyString("foo".to_string());
1176 let result: KeyString = serde_json::from_str("\"foo\"")?;
1177 assert_eq!(result, expected);
1178 Ok(())
1179 }
1180
1181 #[cfg(feature = "serde")]
1182 #[test]
1183 fn test_serialize_json_keystring_or_oid_keystring() -> Result<(), Box<dyn std::error::Error>> {
1184 let ks: KeyStringOrOID = KeyStringOrOID::KeyString(KeyString("foo".to_string()));
1185 let result = serde_json::to_string(&ks)?;
1186 assert_eq!(result, "{\"key_string\":\"foo\"}".to_string());
1187 Ok(())
1188 }
1189
1190 #[cfg(feature = "serde")]
1191 #[test]
1192 fn test_deserialize_json_keystring_or_oid_keystring() -> Result<(), Box<dyn std::error::Error>>
1193 {
1194 let expected: KeyStringOrOID = KeyStringOrOID::KeyString(KeyString("foo".to_string()));
1195 let result: KeyStringOrOID = serde_json::from_str("{\"key_string\":\"foo\"}")?;
1196 assert_eq!(result, expected);
1197 Ok(())
1198 }
1199
1200 #[cfg(feature = "serde")]
1201 #[test]
1202 fn test_serialize_json_keystring_or_oid_oid() -> Result<(), Box<dyn std::error::Error>> {
1203 let ks: KeyStringOrOID = KeyStringOrOID::OID("1.2.3.4".to_string().try_into().unwrap());
1204 let result = serde_json::to_string(&ks)?;
1205 assert_eq!(result, "{\"oid\":\"1.2.3.4\"}".to_string());
1206 Ok(())
1207 }
1208
1209 #[cfg(feature = "serde")]
1210 #[test]
1211 fn test_deserialize_json_keystring_or_oid_oid() -> Result<(), Box<dyn std::error::Error>> {
1212 let expected: KeyStringOrOID =
1213 KeyStringOrOID::OID("1.2.3.4".to_string().try_into().unwrap());
1214 let result: KeyStringOrOID = serde_json::from_str("{\"oid\":\"1.2.3.4\"}")?;
1215 assert_eq!(result, expected);
1216 Ok(())
1217 }
1218
1219 #[cfg(feature = "serde")]
1220 #[test]
1221 fn test_serialize_json_rdn() -> Result<(), Box<dyn std::error::Error>> {
1222 let rdn: RelativeDistinguishedName = RelativeDistinguishedName {
1223 attributes: vec![(
1224 KeyStringOrOID::KeyString(KeyString("cn".to_string())),
1225 "Foobar".as_bytes().to_vec(),
1226 )],
1227 };
1228 let result = serde_json::to_string(&rdn)?;
1229 assert_eq!(
1230 result,
1231 "{\"attributes\":[[{\"key_string\":\"cn\"},\"Foobar\"]]}".to_string()
1232 );
1233 Ok(())
1234 }
1235
1236 #[cfg(feature = "serde")]
1237 #[test]
1238 fn test_deserialize_json_rdn_string() -> Result<(), Box<dyn std::error::Error>> {
1239 let expected: RelativeDistinguishedName = RelativeDistinguishedName {
1240 attributes: vec![(
1241 KeyStringOrOID::KeyString(KeyString("cn".to_string())),
1242 "Foobar".as_bytes().to_vec(),
1243 )],
1244 };
1245 let result: RelativeDistinguishedName =
1246 serde_json::from_str("{\"attributes\":[[{\"key_string\":\"cn\"},\"Foobar\"]]}")?;
1247 assert_eq!(result, expected);
1248 Ok(())
1249 }
1250
1251 #[cfg(feature = "serde")]
1252 #[test]
1253 fn test_deserialize_json_rdn_integers() -> Result<(), Box<dyn std::error::Error>> {
1254 let expected: RelativeDistinguishedName = RelativeDistinguishedName {
1255 attributes: vec![(
1256 KeyStringOrOID::KeyString(KeyString("cn".to_string())),
1257 "Foobar".as_bytes().to_vec(),
1258 )],
1259 };
1260 let result: RelativeDistinguishedName = serde_json::from_str(
1261 "{\"attributes\":[[{\"key_string\":\"cn\"},[70, 111, 111, 98, 97, 114]]]}",
1262 )?;
1263 assert_eq!(result, expected);
1264 Ok(())
1265 }
1266
1267 #[cfg(feature = "serde")]
1268 #[test]
1269 fn test_serialize_json_dn() -> Result<(), Box<dyn std::error::Error>> {
1270 let dn: DistinguishedName = DistinguishedName {
1271 rdns: vec![RelativeDistinguishedName {
1272 attributes: vec![
1273 (
1274 KeyStringOrOID::KeyString(KeyString("cn".to_string())),
1275 "Foo,bar".as_bytes().to_vec(),
1276 ),
1277 (
1278 KeyStringOrOID::KeyString(KeyString("uid".to_string())),
1279 "foobar".as_bytes().to_vec(),
1280 ),
1281 ],
1282 }],
1283 };
1284 let result = serde_json::to_string(&dn)?;
1285 assert_eq!(
1286 result,
1287 "{\"rdns\":[{\"attributes\":[[{\"key_string\":\"cn\"},\"Foo,bar\"],[{\"key_string\":\"uid\"},\"foobar\"]]}]}".to_string()
1288 );
1289 Ok(())
1290 }
1291
1292 #[cfg(feature = "serde")]
1293 #[test]
1294 fn test_deserialize_json_dn() -> Result<(), Box<dyn std::error::Error>> {
1295 let expected: DistinguishedName = DistinguishedName {
1296 rdns: vec![RelativeDistinguishedName {
1297 attributes: vec![
1298 (
1299 KeyStringOrOID::KeyString(KeyString("cn".to_string())),
1300 "Foo,bar".as_bytes().to_vec(),
1301 ),
1302 (
1303 KeyStringOrOID::KeyString(KeyString("uid".to_string())),
1304 "foobar".as_bytes().to_vec(),
1305 ),
1306 ],
1307 }],
1308 };
1309 let result : DistinguishedName = serde_json::from_str("{\"rdns\":[{\"attributes\":[[{\"key_string\":\"cn\"},\"Foo,bar\"],[{\"key_string\":\"uid\"},\"foobar\"]]}]}")?;
1310 assert_eq!(result, expected);
1311 Ok(())
1312 }
1313}