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}