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
614/// The SUBSTR matching rule for the attribute type or subtype.
615#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
616#[non_exhaustive]
617pub struct SubstringFilter {
618    /// The type to match against.
619    pub r#type: AttributeDescription,
620    /// Substrings to match against.
621    pub substrings: SequenceOf<SubstringChoice>,
622}
623
624impl SubstringFilter {
625    /// SubstringFilter constructor
626    pub fn new(r#type: AttributeDescription, substrings: SequenceOf<SubstringChoice>) -> Self {
627        Self { r#type, substrings }
628    }
629}
630
631/// Which part of the substring to match against.
632#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
633#[rasn(choice)]
634#[non_exhaustive]
635pub enum SubstringChoice {
636    /// The start of a substrings filter, **must** be the first element.
637    #[rasn(tag(0))]
638    Initial(AssertionValue),
639    /// An assertion in the middle of a substrings filter, **must not** be the
640    /// first or last element.
641    #[rasn(tag(1))]
642    Any(AssertionValue),
643    /// The end of a substrings filter, **must** be the last element.
644    #[rasn(tag(2))]
645    Final(AssertionValue),
646}
647
648/// Extensible match rule assertion.
649#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
650#[non_exhaustive]
651pub struct MatchingRuleAssertion {
652    /// If the `matching_rule` is absent, the `type` field **MUST** be
653    /// present, and an equality match is performed for that type.
654    #[rasn(tag(1))]
655    pub matching_rule: Option<MatchingRuleId>,
656    /// The type match against if `matching_rule` is absent.
657    #[rasn(tag(2))]
658    pub r#type: Option<AttributeDescription>,
659    /// The value to match against.
660    ///
661    /// If the `type` field is absent and the `matching_rule` is present,
662    /// `match_value` is compared against all attributes in an entry that
663    /// support that `matching_rule`.
664    ///
665    /// If the `type` field is present and the `matching_rule` is present, the
666    /// `match_value` is compared against the specified attribute type and
667    /// its subtypes.
668    #[rasn(tag(3))]
669    pub match_value: AssertionValue,
670    /// If the dnAttributes field is set to `true`, the match is additionally
671    /// applied against all the AttributeValueAssertions in an entry's
672    /// distinguished name, and it evaluates to TRUE if there is at least one
673    /// attribute or subtype in the distinguished name for which the filter
674    /// item evaluates to `true`. The dnAttributes field is present to
675    /// alleviate the need for multiple versions of generic matching rules
676    /// (such as word matching), where one applies to entries and another
677    /// applies to entries and DN attributes as well.
678    #[rasn(tag(4), default)]
679    pub dn_attributes: bool,
680}
681
682impl MatchingRuleAssertion {
683    /// MatchingRuleAssertion constructor
684    pub fn new(
685        matching_rule: Option<MatchingRuleId>,
686        r#type: Option<AttributeDescription>,
687        match_value: AssertionValue,
688        dn_attributes: bool,
689    ) -> Self {
690        Self {
691            matching_rule,
692            r#type,
693            match_value,
694            dn_attributes,
695        }
696    }
697}
698
699/// An entry found during the search.
700#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, Eq, Hash)]
701#[rasn(tag(application, 4))]
702#[non_exhaustive]
703pub struct SearchResultEntry {
704    /// The name of the object found.
705    pub object_name: LdapDn,
706    /// The attributes associated with the object.
707    pub attributes: PartialAttributeList,
708}
709
710impl SearchResultEntry {
711    /// SearchResultEntry constructor
712    pub fn new(object_name: LdapDn, attributes: PartialAttributeList) -> Self {
713        Self {
714            object_name,
715            attributes,
716        }
717    }
718}
719
720/// Reference of servers containing the data required to continue the search.
721#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
722#[rasn(tag(application, 19))]
723#[rasn(delegate)]
724pub struct SearchResultReference(pub SequenceOf<Uri>);
725
726/// The result of a search operation.
727#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
728#[rasn(tag(application, 5))]
729#[rasn(delegate)]
730pub struct SearchResultDone(pub LdapResult);
731
732/// Allows a client to request that a modification of an entry be performed on
733/// its behalf by a server.
734#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, Eq, Hash)]
735#[rasn(tag(application, 6))]
736pub struct ModifyRequest {
737    /// The name of the entry to be modified.
738    pub object: LdapDn,
739    /// A list of modifications to be performed on the entry. The entire list of
740    /// modifications **must** be performed in the order they are listed as a
741    /// single atomic operation. While individual modifications may violate
742    /// certain aspects of the directory schema (such as the object class
743    /// definition and Directory Information Tree (DIT) content rule), the
744    /// resulting entry after the entire list of modifications is performed
745    /// **must** conform to the requirements of the directory model and
746    /// controlling schema. See [RFC 4512]
747    pub changes: SequenceOf<ModifyRequestChanges>,
748}
749
750/// Modifications to be performed on an LDAP entry.
751#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, Eq, Hash)]
752pub struct ModifyRequestChanges {
753    /// The type of modification being performed.
754    pub operation: ChangeOperation,
755    /// The attribute type or attribute type and values being modified.
756    pub modification: PartialAttribute,
757}
758
759/// The type of modification being performed on an LDAP entry.
760#[derive(AsnType, Encode, Decode, Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
761#[rasn(enumerated)]
762pub enum ChangeOperation {
763    /// Add values listed to the modification attribute, creating the attribute
764    /// if necessary.
765    Add = 0,
766    /// Delete values listed from the modification attribute. If no values are
767    /// listed, or if all current values of the attribute are listed, the entire
768    /// attribute is removed.
769    Delete = 1,
770    /// Replace all existing values of the modification attribute with the new
771    /// values listed, creating the attribute if it did not already exist. A
772    /// replace with no value will delete the entire attribute if it exists, and
773    /// it is ignored if the attribute does not exist.
774    Replace = 2,
775}
776
777/// The result of a [`ModifyRequest`] operation.
778///
779/// Due to the requirement for atomicity in applying the list of modifications
780/// in the [`ModifyRequest`], the client may expect that no modifications of the
781/// DIT have been performed if the [`ModifyResponse`] received indicates any
782/// sort of error, and that all requested modifications have been performed if
783/// the [`ModifyResponse`] indicates successful completion of the modify
784/// operation. Whether or not the modification was applied cannot be determined
785/// by the client if the [`ModifyResponse`] was not received (e.g., the LDAP
786/// session was terminated or the modify operation was abandoned).
787#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
788#[rasn(delegate, tag(application, 7))]
789pub struct ModifyResponse(pub LdapResult);
790
791/// Allows a client to request the addition of LDAP an entry into the directory.
792#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, Eq, Hash)]
793#[rasn(tag(application, 8))]
794pub struct AddRequest {
795    /// The name of the entry to be added.
796    pub entry: LdapDn,
797    /// The list of attributes that, along with those from the RDN, make up the
798    /// content of the entry being added.  Clients *may* or *may not* include
799    /// the RDN attribute(s) in this list. Clients **must not** supply
800    /// `NO-USER-MODIFICATION` attributes such as the `createTimestamp` or
801    /// `creatorsName` attributes, since the server maintains
802    /// these automatically.
803    pub attributes: AttributeList,
804}
805
806/// The result of a [`AddRequest`] operation.
807///
808/// A response of success indicates that the new entry has been added to
809/// the Directory.
810#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
811#[rasn(delegate, tag(application, 9))]
812pub struct AddResponse(pub LdapResult);
813
814/// Allows a client to request the removal of an LDAP entry from the directory.
815///
816/// The request contains the name of the entry to be deleted. Only leaf entries
817/// (those with no subordinate entries) can be deleted with this operation.
818#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
819#[rasn(delegate, tag(application, 10))]
820pub struct DelRequest(pub LdapDn);
821
822/// The result of a [`DelRequest`] operation.
823#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
824#[rasn(delegate, tag(application, 11))]
825pub struct DelResponse(pub LdapResult);
826
827/// Allows a client to change the Relative Distinguished Name (RDN) of an LDAP
828/// entry in the directory and/or to move a subtree of entries to a new location
829/// in the directory.
830#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
831#[rasn(tag(application, 12))]
832pub struct ModifyDnRequest {
833    /// The name of the entry to be changed.
834    pub entry: LdapDn,
835    /// The new RDN of the entry. The value of the old RDN is supplied when
836    /// moving the entry to a new superior without changing its RDN. Attribute
837    /// values of the new RDN not matching any attribute value of the entry are
838    /// added to the entry, and an appropriate error is returned if this fails.
839    pub new_rdn: RelativeLdapDn,
840    /// Controls whether the old RDN attribute values are to be retained as
841    /// attributes of the entry or deleted from the entry.
842    pub delete_old_rdn: bool,
843    /// If present, this is the name of an existing object entry that becomes
844    /// the immediate superior (parent) of the existing entry.
845    #[rasn(tag(0))]
846    pub new_superior: Option<LdapDn>,
847}
848
849/// The result of a [`ModifyDnRequest`] operation.
850#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
851#[rasn(delegate, tag(application, 13))]
852pub struct ModifyDnResponse(pub LdapResult);
853
854/// Allows a client to compare an assertion value with the values of a
855/// particular attribute in a particular entry in the directory.
856#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
857#[rasn(tag(application, 14))]
858pub struct CompareRequest {
859    /// The name of the entry to be compared.
860    pub entry: LdapDn,
861    /// The assertion to compare with.
862    pub ava: AttributeValueAssertion,
863}
864
865/// The result of a [`CompareResponse`] operation.
866///
867/// The result code is set to [`CompareTrue`][ResultCode::CompareTrue],
868/// [`CompareFalse`][ResultCode::CompareFalse], or an appropriate error.
869/// `CompareTrue` indicates that the assertion value in the `ava` field matches
870/// a value of the attribute or subtype according to the attribute's `EQUALITY`
871/// matching rule. `CompareFalse` indicates that the assertion value in the
872/// `ava` field and the values of the attribute or subtype did not match. Other
873/// result codes indicate either that the result of the comparison was
874/// "undefined", or that some error occurred.
875#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
876#[rasn(delegate, tag(application, 15))]
877pub struct CompareResponse(pub LdapResult);
878
879/// Allows a client to request that the server abandon an uncompleted operation.
880///
881/// The [`MessageId`] is that of an operation that was requested earlier at this
882/// LDAP message layer. The [`AbandonRequest`] itself has its own `MessageId`.
883/// This is distinct from the `MessageId` of the earlier operation
884/// being abandoned.
885///
886/// There is no response defined in the Abandon operation.  Upon receipt of an
887/// [`AbandonRequest`], the server *may* abandon the operation identified by the
888/// `MessageId`. Since the client cannot tell the difference between a
889/// successfully abandoned operation and an uncompleted operation, the
890/// application of the `Abandon` operation is limited to uses where the client
891/// does not require an indication of its outcome.
892///
893/// "Abandon", "Bind", "Unbind", and "StartTLS" operations cannot be abandoned.
894///
895/// In the event that a server receives an `AbandonRequest` on a search
896/// operation in the midst of transmitting responses to the search, that server
897/// **must** cease transmitting entry responses to the abandoned request
898/// immediately, and it **must not** send the `SearchResultDone`. Of course, the
899/// server **must** ensure that only properly encoded [`LdapMessage`] PDUs
900/// are transmitted.
901///
902/// The ability to abandon other (particularly update) operations is at the
903/// discretion of the server.
904///
905/// Clients should not send Abandon requests for the same operation multiple
906/// times, and they **must** also be prepared to receive results from operations
907/// they have abandoned (since these might have been in transit when the
908/// abandon was requested or might not be able to be abandoned).
909///
910/// Servers MUST discard Abandon requests for messageIDs they do not recognize,
911/// for operations that cannot be abandoned, and for operations that have
912/// already been abandoned.
913#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
914#[rasn(delegate, tag(application, 16))]
915pub struct AbandonRequest(pub MessageId);
916
917/// Allows additional operations to be defined for services not already
918/// available in the protocol.
919///
920/// The Extended operation allows clients to make requests and receive responses
921/// with predefined syntaxes and semantics.  These may be defined in RFCs or be
922/// private to particular implementations.
923#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
924#[rasn(tag(application, 23))]
925pub struct ExtendedRequest {
926    /// The unique [`LdapOid`] corresponding to the extended operation. Where
927    /// the request name is not recognized, the server
928    /// returns [`ResultCode::ProtocolError`].
929    #[rasn(tag(0))]
930    pub request_name: LdapOid,
931    /// Information specific to the extended operation.
932    #[rasn(tag(1))]
933    pub request_value: Option<OctetString>,
934}
935
936/// The result of a [`CompareResponse`] operation.
937///
938#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
939#[rasn(tag(application, 24))]
940pub struct ExtendedResponse {
941    pub result_code: ResultCode,
942    pub matched_dn: LdapDn,
943    pub diagnostic_message: LdapString,
944    #[rasn(tag(3))]
945    pub referral: Option<Referral>,
946    /// When present, contains an [`LdapOid`] that is unique for this extended
947    /// operation or response. Will be absent whenever the server is unable or
948    /// unwilling to determine the appropriate [`LdapOid`] to return, for
949    /// instance, when the requestName cannot be parsed or its value is
950    /// not recognized.
951    #[rasn(tag(10))]
952    pub response_name: Option<LdapOid>,
953    /// Information specific to the extended operation.
954    #[rasn(tag(11))]
955    pub response_value: Option<OctetString>,
956}
957
958/// Provides a general mechanism for defining single-request/multiple-response
959/// operations in LDAP.
960///
961/// This message is intended to be used in conjunction with the Extended
962/// operation to define new single-request/multiple-response operations or in
963/// conjunction with a control when extending existing LDAP operations in a way
964/// that requires them to return [`IntermediateResponse`] information.
965#[derive(AsnType, Encode, Decode, Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
966#[rasn(tag(application, 25))]
967pub struct IntermediateResponse {
968    /// The unique [`LdapOid`] corresponding to the extended operation.
969    #[rasn(tag(0))]
970    pub response_name: Option<LdapOid>,
971    /// Information specific to the extended operation.
972    #[rasn(tag(1))]
973    pub response_value: Option<OctetString>,
974}
975
976mod tests {
977
978    #[test]
979    fn test_ldpa_string() {
980        use super::{LdapString, ToString};
981        let ldap_string = LdapString("test".to_string());
982        let ldap_string2 = LdapString("test".into());
983        assert_eq!(ldap_string, ldap_string2);
984        let encoded = rasn::ber::encode(&ldap_string).unwrap();
985        assert_eq!(encoded, alloc::vec![0x04, 0x04, 0x74, 0x65, 0x73, 0x74]);
986        let decoded: LdapString = rasn::ber::decode(&encoded).unwrap();
987        assert_eq!(ldap_string, decoded);
988        let invalid_utf8: &[u8] = &[0x04, 0x04, 0x80, 0xC0, 0xF5, 0xFF];
989        let decoded = rasn::ber::decode::<LdapString>(invalid_utf8);
990        assert!(decoded.is_err());
991    }
992}