rasn_ldap/lib.rs
1#![doc = include_str!("../README.md")]
2#![no_std]
3
4extern crate alloc;
5
6use alloc::string::{String, ToString};
7use rasn::prelude::*;
8
9/// ID value of a corresponding request [`LdapMessage`].
10///
11/// The messageID of a request MUST have a non-zero value different from
12/// the messageID of any other request in progress in the same LDAP
13/// session. The zero value is reserved for the unsolicited notification
14/// message.
15///
16/// Typical clients increment a counter for each request.
17///
18/// A client MUST NOT send a request with the same messageID as an
19/// earlier request in the same LDAP session unless it can be determined
20/// that the server is no longer servicing the earlier request (e.g.,
21/// after the final response is received, or a subsequent Bind
22/// completes). Otherwise, the behavior is undefined. For this purpose,
23/// note that Abandon and successfully abandoned operations do not send
24/// responses.
25pub type MessageId = u32;
26
27/// A notational convenience to indicate that, for `LdapString`, the
28/// ISO10646 character set (a superset of
29/// Unicode) is being used, encoded following the UTF-8 RFC3629 algorithm.
30///
31/// We can use Rust `String` type to represent this type, see
32/// <https://github.com/librasn/rasn/issues/304> and <https://www.unicode.org/faq/unicode_iso.html>
33#[derive(AsnType, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
34#[rasn(tag(universal, 4))]
35pub struct LdapString(pub String);
36
37impl core::ops::Deref for LdapString {
38 type Target = String;
39
40 fn deref(&self) -> &Self::Target {
41 &self.0
42 }
43}
44impl core::ops::DerefMut for LdapString {
45 fn deref_mut(&mut self) -> &mut Self::Target {
46 &mut self.0
47 }
48}
49impl From<String> for LdapString {
50 fn from(s: String) -> Self {
51 Self(s)
52 }
53}
54impl From<&str> for LdapString {
55 fn from(s: &str) -> Self {
56 Self(s.to_string())
57 }
58}
59
60#[allow(clippy::mutable_key_type)]
61impl rasn::Encode for LdapString {
62 fn encode_with_tag_and_constraints<'encoder, EN: rasn::Encoder<'encoder>>(
63 &self,
64 encoder: &mut EN,
65 tag: rasn::types::Tag,
66 constraints: rasn::types::Constraints,
67 identifier: Identifier,
68 ) -> core::result::Result<(), EN::Error> {
69 encoder.encode_octet_string(tag, constraints, self.0.as_bytes(), identifier)?;
70 Ok(())
71 }
72}
73impl rasn::Decode for LdapString {
74 fn decode_with_tag_and_constraints<D: rasn::Decoder>(
75 decoder: &mut D,
76 tag: rasn::types::Tag,
77 constraints: rasn::types::Constraints,
78 ) -> core::result::Result<Self, D::Error> {
79 String::from_utf8(decoder.decode_octet_string(tag, constraints)?)
80 .map_err(|error| {
81 rasn::de::Error::custom(
82 alloc::format!("LdapString not valid UTF-8: {error}"),
83 decoder.codec(),
84 )
85 })
86 .map(Self::from)
87 }
88}
89
90/// A notational convenience to indicate that the permitted value of this string
91/// is a (UTF-8 encoded) dotted-decimal representation of an `ObjectIdentifier`.
92pub type LdapOid = OctetString;
93
94/// The representation of a Distinguished Name (DN).
95pub type LdapDn = LdapString;
96/// The representation of a Relative Distinguished Name (RDN).
97pub type RelativeLdapDn = LdapString;
98/// An attribute type and zero or more options.
99pub type AttributeDescription = LdapString;
100/// An encoded attribute value. T
101///
102/// he attribute value is encoded according to the
103/// LDAP-specific encoding definition of its corresponding syntax. The
104/// LDAP-specific encoding definitions for different syntaxes and attribute
105/// types may be found in other documents and in particular [RFC 4517].
106pub type AttributeValue = OctetString;
107/// The value to compare in the assertion. The syntax of the `AssertionValue`
108/// depends on the context of the LDAP operation being performed.
109pub type AssertionValue = OctetString;
110/// An identifier for a "Matching Rule".
111pub type MatchingRuleId = LdapString;
112/// references to one or more servers or services that may be accessed via LDAP
113/// or other protocols.
114pub type Referral = SequenceOf<Uri>;
115/// An LDAP String restricted to URL characters.
116pub type Uri = LdapString;
117/// List of LDAP [`Control`]s.
118pub type Controls = SequenceOf<Control>;
119pub type AttributeSelection = SequenceOf<LdapString>;
120pub type PartialAttributeList = SequenceOf<PartialAttribute>;
121pub type AttributeList = SequenceOf<Attribute>;
122
123/// The envelope for all LDAP operations.
124#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, Eq, Hash)]
125#[non_exhaustive]
126pub struct LdapMessage {
127 pub message_id: MessageId,
128 pub protocol_op: ProtocolOp,
129 #[rasn(tag(0))]
130 pub controls: Option<Controls>,
131}
132
133impl LdapMessage {
134 /// LdapMessage constructor
135 pub fn new(message_id: MessageId, protocol_op: ProtocolOp) -> Self {
136 LdapMessage {
137 message_id,
138 protocol_op,
139 controls: None,
140 }
141 }
142}
143
144/// The kind of operation in the [`LdapMessage`].
145#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, Eq, Hash)]
146#[rasn(choice)]
147#[non_exhaustive]
148pub enum ProtocolOp {
149 BindRequest(BindRequest),
150 BindResponse(BindResponse),
151 UnbindRequest(UnbindRequest),
152 SearchRequest(SearchRequest),
153 SearchResEntry(SearchResultEntry),
154 SearchResDone(SearchResultDone),
155 SearchResRef(SearchResultReference),
156 ModifyRequest(ModifyRequest),
157 ModifyResponse(ModifyResponse),
158 AddRequest(AddRequest),
159 AddResponse(AddResponse),
160 DelRequest(DelRequest),
161 DelResponse(DelResponse),
162 ModDnRequest(ModifyDnRequest),
163 ModDnResponse(ModifyDnResponse),
164 CompareRequest(CompareRequest),
165 CompareResponse(CompareResponse),
166 AbandonRequest(AbandonRequest),
167 ExtendedReq(ExtendedRequest),
168 ExtendedResp(ExtendedResponse),
169 IntermediateResponse(IntermediateResponse),
170}
171
172#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
173#[non_exhaustive]
174pub struct AttributeValueAssertion {
175 pub attribute_desc: AttributeDescription,
176 pub assertion_value: AssertionValue,
177}
178
179impl AttributeValueAssertion {
180 /// AttributeValueAssertion constructor
181 pub fn new(attribute_desc: AttributeDescription, assertion_value: AssertionValue) -> Self {
182 Self {
183 attribute_desc,
184 assertion_value,
185 }
186 }
187}
188
189#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, Eq, Hash)]
190#[non_exhaustive]
191pub struct PartialAttribute {
192 pub r#type: AttributeDescription,
193 pub vals: SetOf<AttributeValue>,
194}
195
196impl PartialAttribute {
197 /// PartialAttribute constructor
198 pub fn new(r#type: AttributeDescription, vals: SetOf<AttributeValue>) -> Self {
199 Self { r#type, vals }
200 }
201}
202
203#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, Eq, Hash)]
204#[non_exhaustive]
205pub struct Attribute {
206 pub r#type: AttributeDescription,
207 pub vals: SetOf<AttributeValue>,
208}
209
210impl Attribute {
211 /// Attribute constructor
212 pub fn new(r#type: AttributeDescription, vals: SetOf<AttributeValue>) -> Self {
213 Self { r#type, vals }
214 }
215}
216
217/// The envelope for the result of any operation.
218#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
219#[non_exhaustive]
220pub struct LdapResult {
221 /// The code indicating the status of the operation.
222 pub result_code: ResultCode,
223 pub matched_dn: LdapDn,
224 pub diagnostic_message: LdapString,
225 #[rasn(tag(3))]
226 pub referral: Option<Referral>,
227}
228
229impl LdapResult {
230 /// LdapResult constructor
231 pub fn new(
232 result_code: ResultCode,
233 matched_dn: LdapDn,
234 diagnostic_message: LdapString,
235 ) -> Self {
236 Self {
237 result_code,
238 matched_dn,
239 diagnostic_message,
240 referral: None,
241 }
242 }
243}
244
245#[derive(AsnType, Encode, Decode, Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
246#[rasn(enumerated)]
247#[non_exhaustive]
248pub enum ResultCode {
249 Success = 0,
250 OperationsError = 1,
251 ProtocolError = 2,
252 TimeLimitExceeded = 3,
253 SizeLimitExceeded = 4,
254 CompareFalse = 5,
255 CompareTrue = 6,
256 AuthMethodNotSupported = 7,
257 StrongerAuthRequired = 8,
258 Referral = 10,
259 AdminLimitExceeded = 11,
260 UnavailableCriticalExtension = 12,
261 ConfidentialityRequired = 13,
262 SaslBindInProgress = 14,
263 NoSuchAttribute = 16,
264 UndefinedAttributeType = 17,
265 InappropriateMatching = 18,
266 ConstraintViolation = 19,
267 AttributeOrValueExists = 20,
268 InvalidAttributeSyntax = 21,
269 NoSuchObject = 32,
270 AliasProblem = 33,
271 InvalidDnSyntax = 34,
272 AliasDereferencingProblem = 36,
273 InappropriateAuthentication = 48,
274 InvalidCredentials = 49,
275 InsufficientAccessRights = 50,
276 Busy = 51,
277 Unavailable = 52,
278 UnwillingToPerform = 53,
279 LoopDetect = 54,
280 NamingViolation = 64,
281 ObjectClassViolation = 65,
282 NotAllowedOnNonLeaf = 66,
283 NotAllowedOnRdn = 67,
284 EntryAlreadyExists = 68,
285 ObjectClassModsProhibited = 69,
286 AffectsMultipleDsas = 71,
287 Other = 80,
288}
289
290#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
291#[non_exhaustive]
292pub struct Control {
293 pub control_type: LdapOid,
294 #[rasn(default)]
295 pub criticality: bool,
296 pub control_value: Option<OctetString>,
297}
298
299impl Control {
300 /// Control constructor
301 pub fn new(
302 control_type: LdapOid,
303 criticality: bool,
304 control_value: Option<OctetString>,
305 ) -> Self {
306 Self {
307 control_type,
308 criticality,
309 control_value,
310 }
311 }
312}
313
314/// Allow authentication information to be exchanged between the client
315/// and server.
316///
317/// The Bind operation should be thought of as the "authenticate" operation.
318/// Operational, authentication, and security-related semantics of this
319/// operation are given in [RFC 4513].
320#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
321#[non_exhaustive]
322#[rasn(tag(application, 0))]
323pub struct BindRequest {
324 /// A version number indicating the version of the protocol to be used at
325 /// the LDAP message layer. This document describes version 3 of the
326 /// protocol. There is no version negotiation. The client sets this field to
327 /// the version it desires. If the server does not support the specified
328 /// version, it **must** respond with a [`BindResponse`]
329 /// containing [`ResultCode::ProtocolError`].
330 pub version: u8,
331 /// If not empty, the name of the Directory object that the client wishes to
332 /// bind as. This field may take on a null value (a zero-length string) for
333 /// the purposes of anonymous binds or when using SASL authentication. Where
334 /// the server attempts to locate the named object, it **shall not** perform
335 /// alias dereferencing.
336 pub name: LdapDn,
337 /// Information used in authentication.
338 pub authentication: AuthenticationChoice,
339}
340
341impl BindRequest {
342 /// BindRequest constructor
343 pub fn new(version: u8, name: LdapDn, authentication: AuthenticationChoice) -> Self {
344 Self {
345 version,
346 name,
347 authentication,
348 }
349 }
350}
351
352/// Information used in authentication.
353///
354/// This type is extensible. Servers that do not support a choice supplied
355/// by a client return a [`BindResponse`]
356/// with [`ResultCode::AuthMethodNotSupported`].
357#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
358#[rasn(choice)]
359#[non_exhaustive]
360pub enum AuthenticationChoice {
361 #[rasn(tag(0))]
362 Simple(OctetString),
363 // 1 and 2 are reserved.
364 #[rasn(tag(3))]
365 Sasl(SaslCredentials),
366}
367
368#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
369#[non_exhaustive]
370pub struct SaslCredentials {
371 pub mechanism: LdapString,
372 pub credentials: Option<OctetString>,
373}
374
375impl SaslCredentials {
376 /// SaslCredentials constructor
377 pub fn new(mechanism: LdapString, credentials: Option<OctetString>) -> Self {
378 Self {
379 mechanism,
380 credentials,
381 }
382 }
383}
384
385/// An indication from the server of the status of the client's request
386/// for authentication.
387///
388///
389#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
390#[non_exhaustive]
391#[rasn(tag(application, 1))]
392pub struct BindResponse {
393 pub result_code: ResultCode,
394 pub matched_dn: LdapDn,
395 pub diagnostic_message: LdapString,
396 #[rasn(tag(3))]
397 pub referral: Option<Referral>,
398 #[rasn(tag(7))]
399 pub server_sasl_creds: Option<OctetString>,
400}
401
402impl BindResponse {
403 /// BindResponse constructor
404 pub fn new(
405 result_code: ResultCode,
406 matched_dn: LdapDn,
407 diagnostic_message: LdapString,
408 referral: Option<Referral>,
409 server_sasl_creds: Option<OctetString>,
410 ) -> Self {
411 Self {
412 result_code,
413 matched_dn,
414 diagnostic_message,
415 referral,
416 server_sasl_creds,
417 }
418 }
419}
420
421/// Request to terminate an LDAP session.
422///
423/// The Unbind operation is not the antithesis of the Bind operation as the name
424/// implies. The naming of these operations are historical. The Unbind operation
425/// should be thought of as the "quit" operation.
426#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
427#[rasn(tag(application, 2))]
428pub struct UnbindRequest;
429
430/// Used to request a server to return, subject to access controls and other
431/// restrictions, a set of entries matching a complex search criterion.
432///
433/// This can be used to read attributes from a single entry, from entries immediately
434/// subordinate to a particular entry, or from a whole subtree of entries.
435#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, Eq, Hash)]
436#[rasn(tag(application, 3))]
437#[non_exhaustive]
438pub struct SearchRequest {
439 /// The name of the base object entry (or possibly the root) relative to
440 /// which the search is to be performed.
441 pub base_object: LdapDn,
442 /// The scope of the search to be performed.
443 pub scope: SearchRequestScope,
444 /// An indicator as to whether or not alias entries are to be dereferenced
445 /// during stages of the search operation.
446 pub deref_aliases: SearchRequestDerefAliases,
447 /// The maximum number of entries to be returned as a result of the search.
448 ///
449 /// A value of zero in this field indicates that no client-requested time
450 /// limit restrictions are in effect for the Search. Servers may also
451 /// enforce a maximum number of entries to return.
452 pub size_limit: u32,
453 /// The maximum time (in seconds) allowed for a search.
454 ///
455 /// A value of zero in this field indicates that no client-requested time
456 /// limit restrictions are in effect for the Search. Servers may also
457 /// enforce a maximum time limit for the Search.
458 pub time_limit: u32,
459 /// Whether search results are to contain both attribute descriptions and
460 /// values, or just attribute descriptions.
461 ///
462 /// Setting this field to [`true`] causes only attribute descriptions (and
463 /// not values) to be returned. Setting this field to [`false`] causes both
464 /// attribute descriptions and values to be returned.
465 pub types_only: bool,
466 /// Defines the conditions that must be fulfilled in order for the search to
467 /// match a given entry.
468 pub filter: Filter,
469 /// A selection list of the attributes to be returned from each entry that
470 /// matches the search filter.
471 ///
472 /// Attributes that are subtypes of listed attributes are
473 /// implicitly included.
474 pub attributes: AttributeSelection,
475}
476
477impl SearchRequest {
478 /// SearchRequest constructor
479 #[allow(clippy::too_many_arguments)]
480 pub fn new(
481 base_object: LdapDn,
482 scope: SearchRequestScope,
483 deref_aliases: SearchRequestDerefAliases,
484 size_limit: u32,
485 time_limit: u32,
486 types_only: bool,
487 filter: Filter,
488 attributes: AttributeSelection,
489 ) -> Self {
490 Self {
491 base_object,
492 scope,
493 deref_aliases,
494 size_limit,
495 time_limit,
496 types_only,
497 filter,
498 attributes,
499 }
500 }
501}
502
503/// The scope of the search to be performed.
504#[derive(AsnType, Encode, Decode, Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
505#[rasn(enumerated)]
506#[non_exhaustive]
507pub enum SearchRequestScope {
508 /// The scope is constrained to the entry named by `base_object`.
509 BaseObject = 0,
510 /// The scope is constrained to the immediate subordinates of the entry
511 /// named by `base_object`.
512 SingleLevel = 1,
513 /// The scope is constrained to the entry named by `base_object` and to all
514 /// its subordinates.
515 WholeSubtree = 2,
516}
517
518/// An indicator as to whether or not alias entries are to be dereferenced
519/// during stages of the search operation.
520///
521/// The act of dereferencing an alias includes recursively dereferencing
522/// aliases that refer to aliases.
523///
524/// Servers **must** detect looping while dereferencing aliases in order to
525/// prevent denial-of-service attacks of this nature.
526#[derive(AsnType, Encode, Decode, Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
527#[rasn(enumerated)]
528#[non_exhaustive]
529pub enum SearchRequestDerefAliases {
530 /// Do not dereference aliases in searching or in locating the base object
531 /// of the search.
532 NeverDerefAliases = 0,
533 /// While searching subordinates of the base object, dereference any alias
534 /// within the search scope. Dereferenced objects become the vertices of
535 /// further search scopes where the search operation is also applied.
536 ///
537 /// If the search scope is [`SearchRequestScope::WholeSubtree`], the search
538 /// continues in the subtree(s) of any dereferenced object.
539 ///
540 /// If the search scope is [`SearchRequestScope::SingleLevel`], the search
541 /// is applied to any dereferenced objects and is not applied to
542 /// their subordinates.
543 ///
544 /// Servers *should* eliminate duplicate entries that arise due to alias
545 /// dereferencing while searching.
546 DerefInSearching = 1,
547 /// Dereference aliases in locating the base object of the search, but not
548 /// when searching subordinates of the base object.
549 DerefFindingBaseObj = 2,
550 /// Dereference aliases both in searching and in locating the base object of
551 /// the search.
552 DerefAlways = 3,
553}
554
555/// Defines the conditions that must be fulfilled in order for the search to
556/// match a given entry.
557#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, Eq, Hash)]
558#[rasn(choice)]
559#[non_exhaustive]
560pub enum Filter {
561 /// All matching rules must evaluate to `true`.
562 #[rasn(tag(0))]
563 And(SetOf<Filter>),
564 /// Any matching rules must evaluate to `true`.
565 #[rasn(tag(1))]
566 Or(SetOf<Filter>),
567 /// The matching rule must evaluate to `false`.
568 #[rasn(tag(2))]
569 Not(alloc::boxed::Box<Filter>),
570 /// The filter is `true` when the EQUALITY rule returns `true` as applied to
571 /// the attribute or subtype and the asserted value.
572 #[rasn(tag(3))]
573 EqualityMatch(AttributeValueAssertion),
574 /// The filter is `true` when the SUBSTR rule returns `true` as applied to
575 /// the attribute or subtype and the asserted value.
576 ///
577 /// Note that the AssertionValue in a substrings filter item conforms to the
578 /// assertion syntax of the EQUALITY matching rule for the attribute type
579 /// rather than to the assertion syntax of the SUBSTR matching rule for the
580 /// attribute type. Conceptually, the entire SubstringFilter is converted
581 /// into an assertion value of the substrings matching rule prior to
582 /// applying the rule.
583 #[rasn(tag(4))]
584 Substrings(SubstringFilter),
585 /// The filter is `true` when the "ORDERING" rule returns `false` as applied
586 /// to the attribute or subtype and the asserted value.
587 #[rasn(tag(5))]
588 GreaterOrEqual(AttributeValueAssertion),
589 /// The filter is `true` when either the "ORDERING" or "EQUALITY" rule
590 /// returns `true` as applied to the attribute or subtype and the
591 /// asserted value.
592 #[rasn(tag(6))]
593 LessOrEqual(AttributeValueAssertion),
594 /// The filter is `true` when there is an attribute or subtype of the
595 /// specified attribute description present in an entry, `false` when no
596 /// attribute or subtype of the specified attribute description is present
597 /// in an entry, and "Undefined" otherwise.
598 #[rasn(tag(7))]
599 Present(AttributeDescription),
600 /// The filter is `true` when there is a value of the attribute type or
601 /// subtype for which some locally-defined approximate matching
602 /// algorithm (e.g., spelling variations, phonetic match, etc.) returns
603 /// `true`. If a value matches for equality, it also satisfies an
604 /// approximate match. If approximate matching is not supported for the
605 /// attribute, this filter item should be treated as an `EqualityMatch`.
606 #[rasn(tag(8))]
607 ApproxMatch(AttributeValueAssertion),
608 /// The filter is evaluated as follows:
609 ///
610 #[rasn(tag(9))]
611 ExtensibleMatch(MatchingRuleAssertion),
612}
613
614impl core::ops::Not for Filter {
615 type Output = Self;
616 fn not(self) -> Self {
617 Self::Not(alloc::boxed::Box::new(self))
618 }
619}
620
621/// The SUBSTR matching rule for the attribute type or subtype.
622#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
623#[non_exhaustive]
624pub struct SubstringFilter {
625 /// The type to match against.
626 pub r#type: AttributeDescription,
627 /// Substrings to match against.
628 pub substrings: SequenceOf<SubstringChoice>,
629}
630
631impl SubstringFilter {
632 /// SubstringFilter constructor
633 pub fn new(r#type: AttributeDescription, substrings: SequenceOf<SubstringChoice>) -> Self {
634 Self { r#type, substrings }
635 }
636}
637
638/// Which part of the substring to match against.
639#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
640#[rasn(choice)]
641#[non_exhaustive]
642pub enum SubstringChoice {
643 /// The start of a substrings filter, **must** be the first element.
644 #[rasn(tag(0))]
645 Initial(AssertionValue),
646 /// An assertion in the middle of a substrings filter, **must not** be the
647 /// first or last element.
648 #[rasn(tag(1))]
649 Any(AssertionValue),
650 /// The end of a substrings filter, **must** be the last element.
651 #[rasn(tag(2))]
652 Final(AssertionValue),
653}
654
655/// Extensible match rule assertion.
656#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
657#[non_exhaustive]
658pub struct MatchingRuleAssertion {
659 /// If the `matching_rule` is absent, the `type` field **MUST** be
660 /// present, and an equality match is performed for that type.
661 #[rasn(tag(1))]
662 pub matching_rule: Option<MatchingRuleId>,
663 /// The type match against if `matching_rule` is absent.
664 #[rasn(tag(2))]
665 pub r#type: Option<AttributeDescription>,
666 /// The value to match against.
667 ///
668 /// If the `type` field is absent and the `matching_rule` is present,
669 /// `match_value` is compared against all attributes in an entry that
670 /// support that `matching_rule`.
671 ///
672 /// If the `type` field is present and the `matching_rule` is present, the
673 /// `match_value` is compared against the specified attribute type and
674 /// its subtypes.
675 #[rasn(tag(3))]
676 pub match_value: AssertionValue,
677 /// If the dnAttributes field is set to `true`, the match is additionally
678 /// applied against all the AttributeValueAssertions in an entry's
679 /// distinguished name, and it evaluates to TRUE if there is at least one
680 /// attribute or subtype in the distinguished name for which the filter
681 /// item evaluates to `true`. The dnAttributes field is present to
682 /// alleviate the need for multiple versions of generic matching rules
683 /// (such as word matching), where one applies to entries and another
684 /// applies to entries and DN attributes as well.
685 #[rasn(tag(4), default)]
686 pub dn_attributes: bool,
687}
688
689impl MatchingRuleAssertion {
690 /// MatchingRuleAssertion constructor
691 pub fn new(
692 matching_rule: Option<MatchingRuleId>,
693 r#type: Option<AttributeDescription>,
694 match_value: AssertionValue,
695 dn_attributes: bool,
696 ) -> Self {
697 Self {
698 matching_rule,
699 r#type,
700 match_value,
701 dn_attributes,
702 }
703 }
704}
705
706/// An entry found during the search.
707#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, Eq, Hash)]
708#[rasn(tag(application, 4))]
709#[non_exhaustive]
710pub struct SearchResultEntry {
711 /// The name of the object found.
712 pub object_name: LdapDn,
713 /// The attributes associated with the object.
714 pub attributes: PartialAttributeList,
715}
716
717impl SearchResultEntry {
718 /// SearchResultEntry constructor
719 pub fn new(object_name: LdapDn, attributes: PartialAttributeList) -> Self {
720 Self {
721 object_name,
722 attributes,
723 }
724 }
725}
726
727/// Reference of servers containing the data required to continue the search.
728#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
729#[rasn(tag(application, 19))]
730#[rasn(delegate)]
731pub struct SearchResultReference(pub SequenceOf<Uri>);
732
733/// The result of a search operation.
734#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
735#[rasn(tag(application, 5))]
736#[rasn(delegate)]
737pub struct SearchResultDone(pub LdapResult);
738
739/// Allows a client to request that a modification of an entry be performed on
740/// its behalf by a server.
741#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, Eq, Hash)]
742#[rasn(tag(application, 6))]
743pub struct ModifyRequest {
744 /// The name of the entry to be modified.
745 pub object: LdapDn,
746 /// A list of modifications to be performed on the entry. The entire list of
747 /// modifications **must** be performed in the order they are listed as a
748 /// single atomic operation. While individual modifications may violate
749 /// certain aspects of the directory schema (such as the object class
750 /// definition and Directory Information Tree (DIT) content rule), the
751 /// resulting entry after the entire list of modifications is performed
752 /// **must** conform to the requirements of the directory model and
753 /// controlling schema. See [RFC 4512]
754 pub changes: SequenceOf<ModifyRequestChanges>,
755}
756
757/// Modifications to be performed on an LDAP entry.
758#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, Eq, Hash)]
759pub struct ModifyRequestChanges {
760 /// The type of modification being performed.
761 pub operation: ChangeOperation,
762 /// The attribute type or attribute type and values being modified.
763 pub modification: PartialAttribute,
764}
765
766/// The type of modification being performed on an LDAP entry.
767#[derive(AsnType, Encode, Decode, Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
768#[rasn(enumerated)]
769pub enum ChangeOperation {
770 /// Add values listed to the modification attribute, creating the attribute
771 /// if necessary.
772 Add = 0,
773 /// Delete values listed from the modification attribute. If no values are
774 /// listed, or if all current values of the attribute are listed, the entire
775 /// attribute is removed.
776 Delete = 1,
777 /// Replace all existing values of the modification attribute with the new
778 /// values listed, creating the attribute if it did not already exist. A
779 /// replace with no value will delete the entire attribute if it exists, and
780 /// it is ignored if the attribute does not exist.
781 Replace = 2,
782}
783
784/// The result of a [`ModifyRequest`] operation.
785///
786/// Due to the requirement for atomicity in applying the list of modifications
787/// in the [`ModifyRequest`], the client may expect that no modifications of the
788/// DIT have been performed if the [`ModifyResponse`] received indicates any
789/// sort of error, and that all requested modifications have been performed if
790/// the [`ModifyResponse`] indicates successful completion of the modify
791/// operation. Whether or not the modification was applied cannot be determined
792/// by the client if the [`ModifyResponse`] was not received (e.g., the LDAP
793/// session was terminated or the modify operation was abandoned).
794#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
795#[rasn(delegate, tag(application, 7))]
796pub struct ModifyResponse(pub LdapResult);
797
798/// Allows a client to request the addition of LDAP an entry into the directory.
799#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, Eq, Hash)]
800#[rasn(tag(application, 8))]
801pub struct AddRequest {
802 /// The name of the entry to be added.
803 pub entry: LdapDn,
804 /// The list of attributes that, along with those from the RDN, make up the
805 /// content of the entry being added. Clients *may* or *may not* include
806 /// the RDN attribute(s) in this list. Clients **must not** supply
807 /// `NO-USER-MODIFICATION` attributes such as the `createTimestamp` or
808 /// `creatorsName` attributes, since the server maintains
809 /// these automatically.
810 pub attributes: AttributeList,
811}
812
813/// The result of a [`AddRequest`] operation.
814///
815/// A response of success indicates that the new entry has been added to
816/// the Directory.
817#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
818#[rasn(delegate, tag(application, 9))]
819pub struct AddResponse(pub LdapResult);
820
821/// Allows a client to request the removal of an LDAP entry from the directory.
822///
823/// The request contains the name of the entry to be deleted. Only leaf entries
824/// (those with no subordinate entries) can be deleted with this operation.
825#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
826#[rasn(delegate, tag(application, 10))]
827pub struct DelRequest(pub LdapDn);
828
829/// The result of a [`DelRequest`] operation.
830#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
831#[rasn(delegate, tag(application, 11))]
832pub struct DelResponse(pub LdapResult);
833
834/// Allows a client to change the Relative Distinguished Name (RDN) of an LDAP
835/// entry in the directory and/or to move a subtree of entries to a new location
836/// in the directory.
837#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
838#[rasn(tag(application, 12))]
839pub struct ModifyDnRequest {
840 /// The name of the entry to be changed.
841 pub entry: LdapDn,
842 /// The new RDN of the entry. The value of the old RDN is supplied when
843 /// moving the entry to a new superior without changing its RDN. Attribute
844 /// values of the new RDN not matching any attribute value of the entry are
845 /// added to the entry, and an appropriate error is returned if this fails.
846 pub new_rdn: RelativeLdapDn,
847 /// Controls whether the old RDN attribute values are to be retained as
848 /// attributes of the entry or deleted from the entry.
849 pub delete_old_rdn: bool,
850 /// If present, this is the name of an existing object entry that becomes
851 /// the immediate superior (parent) of the existing entry.
852 #[rasn(tag(0))]
853 pub new_superior: Option<LdapDn>,
854}
855
856/// The result of a [`ModifyDnRequest`] operation.
857#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
858#[rasn(delegate, tag(application, 13))]
859pub struct ModifyDnResponse(pub LdapResult);
860
861/// Allows a client to compare an assertion value with the values of a
862/// particular attribute in a particular entry in the directory.
863#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
864#[rasn(tag(application, 14))]
865pub struct CompareRequest {
866 /// The name of the entry to be compared.
867 pub entry: LdapDn,
868 /// The assertion to compare with.
869 pub ava: AttributeValueAssertion,
870}
871
872/// The result of a [`CompareResponse`] operation.
873///
874/// The result code is set to [`CompareTrue`][ResultCode::CompareTrue],
875/// [`CompareFalse`][ResultCode::CompareFalse], or an appropriate error.
876/// `CompareTrue` indicates that the assertion value in the `ava` field matches
877/// a value of the attribute or subtype according to the attribute's `EQUALITY`
878/// matching rule. `CompareFalse` indicates that the assertion value in the
879/// `ava` field and the values of the attribute or subtype did not match. Other
880/// result codes indicate either that the result of the comparison was
881/// "undefined", or that some error occurred.
882#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
883#[rasn(delegate, tag(application, 15))]
884pub struct CompareResponse(pub LdapResult);
885
886/// Allows a client to request that the server abandon an uncompleted operation.
887///
888/// The [`MessageId`] is that of an operation that was requested earlier at this
889/// LDAP message layer. The [`AbandonRequest`] itself has its own `MessageId`.
890/// This is distinct from the `MessageId` of the earlier operation
891/// being abandoned.
892///
893/// There is no response defined in the Abandon operation. Upon receipt of an
894/// [`AbandonRequest`], the server *may* abandon the operation identified by the
895/// `MessageId`. Since the client cannot tell the difference between a
896/// successfully abandoned operation and an uncompleted operation, the
897/// application of the `Abandon` operation is limited to uses where the client
898/// does not require an indication of its outcome.
899///
900/// "Abandon", "Bind", "Unbind", and "StartTLS" operations cannot be abandoned.
901///
902/// In the event that a server receives an `AbandonRequest` on a search
903/// operation in the midst of transmitting responses to the search, that server
904/// **must** cease transmitting entry responses to the abandoned request
905/// immediately, and it **must not** send the `SearchResultDone`. Of course, the
906/// server **must** ensure that only properly encoded [`LdapMessage`] PDUs
907/// are transmitted.
908///
909/// The ability to abandon other (particularly update) operations is at the
910/// discretion of the server.
911///
912/// Clients should not send Abandon requests for the same operation multiple
913/// times, and they **must** also be prepared to receive results from operations
914/// they have abandoned (since these might have been in transit when the
915/// abandon was requested or might not be able to be abandoned).
916///
917/// Servers MUST discard Abandon requests for messageIDs they do not recognize,
918/// for operations that cannot be abandoned, and for operations that have
919/// already been abandoned.
920#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
921#[rasn(delegate, tag(application, 16))]
922pub struct AbandonRequest(pub MessageId);
923
924/// Allows additional operations to be defined for services not already
925/// available in the protocol.
926///
927/// The Extended operation allows clients to make requests and receive responses
928/// with predefined syntaxes and semantics. These may be defined in RFCs or be
929/// private to particular implementations.
930#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
931#[rasn(tag(application, 23))]
932pub struct ExtendedRequest {
933 /// The unique [`LdapOid`] corresponding to the extended operation. Where
934 /// the request name is not recognized, the server
935 /// returns [`ResultCode::ProtocolError`].
936 #[rasn(tag(0))]
937 pub request_name: LdapOid,
938 /// Information specific to the extended operation.
939 #[rasn(tag(1))]
940 pub request_value: Option<OctetString>,
941}
942
943/// The result of a [`CompareResponse`] operation.
944///
945#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
946#[rasn(tag(application, 24))]
947pub struct ExtendedResponse {
948 pub result_code: ResultCode,
949 pub matched_dn: LdapDn,
950 pub diagnostic_message: LdapString,
951 #[rasn(tag(3))]
952 pub referral: Option<Referral>,
953 /// When present, contains an [`LdapOid`] that is unique for this extended
954 /// operation or response. Will be absent whenever the server is unable or
955 /// unwilling to determine the appropriate [`LdapOid`] to return, for
956 /// instance, when the requestName cannot be parsed or its value is
957 /// not recognized.
958 #[rasn(tag(10))]
959 pub response_name: Option<LdapOid>,
960 /// Information specific to the extended operation.
961 #[rasn(tag(11))]
962 pub response_value: Option<OctetString>,
963}
964
965/// Provides a general mechanism for defining single-request/multiple-response
966/// operations in LDAP.
967///
968/// This message is intended to be used in conjunction with the Extended
969/// operation to define new single-request/multiple-response operations or in
970/// conjunction with a control when extending existing LDAP operations in a way
971/// that requires them to return [`IntermediateResponse`] information.
972#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
973#[rasn(tag(application, 25))]
974pub struct IntermediateResponse {
975 /// The unique [`LdapOid`] corresponding to the extended operation.
976 #[rasn(tag(0))]
977 pub response_name: Option<LdapOid>,
978 /// Information specific to the extended operation.
979 #[rasn(tag(1))]
980 pub response_value: Option<OctetString>,
981}
982
983mod tests {
984
985 #[test]
986 fn test_ldpa_string() {
987 use super::{LdapString, ToString};
988 let ldap_string = LdapString("test".to_string());
989 let ldap_string2 = LdapString("test".into());
990 assert_eq!(ldap_string, ldap_string2);
991 let encoded = rasn::ber::encode(&ldap_string).unwrap();
992 assert_eq!(encoded, alloc::vec![0x04, 0x04, 0x74, 0x65, 0x73, 0x74]);
993 let decoded: LdapString = rasn::ber::decode(&encoded).unwrap();
994 assert_eq!(ldap_string, decoded);
995 let invalid_utf8: &[u8] = &[0x04, 0x04, 0x80, 0xC0, 0xF5, 0xFF];
996 let decoded = rasn::ber::decode::<LdapString>(invalid_utf8);
997 assert!(decoded.is_err());
998 }
999}