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}