Skip to main content

ldap_client_proto/
controls.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3use ldap_client_ber::tag::Tag;
4use ldap_client_ber::{BerReader, BerWriter};
5
6use crate::ProtoError;
7use crate::result_code::ResultCode;
8
9pub const PAGED_RESULTS_OID: &str = "1.2.840.113556.1.4.319";
10pub const MANAGE_DSA_IT_OID: &str = "2.16.840.1.113730.3.4.2";
11pub const SERVER_SORT_REQUEST_OID: &str = "1.2.840.113556.1.4.473";
12pub const SERVER_SORT_RESPONSE_OID: &str = "1.2.840.113556.1.4.474";
13pub const DOMAIN_SCOPE_OID: &str = "1.2.840.113556.1.4.1339";
14
15// Keep old name for backwards compat
16pub const SERVER_SORT_OID: &str = SERVER_SORT_REQUEST_OID;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct Control {
20    pub oid: String,
21    pub critical: bool,
22    pub value: Option<Vec<u8>>,
23}
24
25/// Simple paged results control (RFC 2696).
26#[derive(Debug, Clone)]
27pub struct PagedResultsControl {
28    pub size: i32,
29    pub cookie: Vec<u8>,
30}
31
32impl PagedResultsControl {
33    pub fn new(size: i32) -> Self {
34        Self {
35            size,
36            cookie: Vec::new(),
37        }
38    }
39
40    pub fn with_cookie(mut self, cookie: Vec<u8>) -> Self {
41        self.cookie = cookie;
42        self
43    }
44
45    pub fn to_control(&self) -> Control {
46        let mut w = BerWriter::new();
47        w.write_sequence(Tag::sequence(), |inner| {
48            inner.write_integer(self.size as i64);
49            inner.write_bytes(&self.cookie);
50        });
51        Control {
52            oid: PAGED_RESULTS_OID.to_string(),
53            critical: false,
54            value: Some(w.into_bytes()),
55        }
56    }
57
58    pub fn from_control(control: &Control) -> Result<Self, ProtoError> {
59        let value = control
60            .value
61            .as_deref()
62            .ok_or_else(|| ProtoError::Protocol("paged results control missing value".into()))?;
63        let mut r = BerReader::new(value);
64        r.read_sequence(Tag::sequence(), |inner| {
65            let size = inner.read_integer()? as i32;
66            let cookie = inner.read_octet_string()?.to_vec();
67            Ok(Self { size, cookie })
68        })
69        .map_err(Into::into)
70    }
71}
72
73#[derive(Debug, Clone)]
74pub struct ManageDsaItControl {
75    pub critical: bool,
76}
77
78impl ManageDsaItControl {
79    pub fn new(critical: bool) -> Self {
80        Self { critical }
81    }
82
83    pub fn to_control(&self) -> Control {
84        Control {
85            oid: MANAGE_DSA_IT_OID.to_string(),
86            critical: self.critical,
87            value: None,
88        }
89    }
90}
91
92#[derive(Debug, Clone)]
93pub struct DomainScopeControl {
94    pub critical: bool,
95}
96
97impl DomainScopeControl {
98    pub fn new(critical: bool) -> Self {
99        Self { critical }
100    }
101
102    pub fn to_control(&self) -> Control {
103        Control {
104            oid: DOMAIN_SCOPE_OID.to_string(),
105            critical: self.critical,
106            value: None,
107        }
108    }
109}
110
111/// A single sort key for server-side sort (RFC 2891).
112#[derive(Debug, Clone)]
113pub struct SortKey {
114    pub attribute_type: String,
115    pub ordering_rule: Option<String>,
116    pub reverse_order: bool,
117}
118
119impl SortKey {
120    pub fn new(attribute_type: impl Into<String>) -> Self {
121        Self {
122            attribute_type: attribute_type.into(),
123            ordering_rule: None,
124            reverse_order: false,
125        }
126    }
127
128    pub fn reverse(mut self) -> Self {
129        self.reverse_order = true;
130        self
131    }
132
133    pub fn ordering_rule(mut self, rule: impl Into<String>) -> Self {
134        self.ordering_rule = Some(rule.into());
135        self
136    }
137}
138
139/// Server-side sort request control value (RFC 2891).
140#[derive(Debug, Clone)]
141pub struct SortKeyList {
142    pub keys: Vec<SortKey>,
143}
144
145impl SortKeyList {
146    pub fn new(keys: Vec<SortKey>) -> Self {
147        Self { keys }
148    }
149
150    pub fn to_control(&self, critical: bool) -> Control {
151        let mut w = BerWriter::new();
152        w.write_sequence(Tag::sequence(), |seq| {
153            for key in &self.keys {
154                seq.write_sequence(Tag::sequence(), |inner| {
155                    inner.write_bytes(key.attribute_type.as_bytes());
156                    if let Some(rule) = &key.ordering_rule {
157                        inner.write_octet_string(Tag::context(0), rule.as_bytes());
158                    }
159                    if key.reverse_order {
160                        inner.write_octet_string(Tag::context(1), &[0xFF]);
161                    }
162                });
163            }
164        });
165        Control {
166            oid: SERVER_SORT_REQUEST_OID.to_string(),
167            critical,
168            value: Some(w.into_bytes()),
169        }
170    }
171}
172
173/// Server-side sort response control value (RFC 2891).
174#[derive(Debug, Clone)]
175pub struct SortResult {
176    pub result_code: ResultCode,
177    pub attribute_type: Option<String>,
178}
179
180impl SortResult {
181    pub fn from_control(control: &Control) -> Result<Self, ProtoError> {
182        let value = control
183            .value
184            .as_deref()
185            .ok_or_else(|| ProtoError::Protocol("sort response control missing value".into()))?;
186        let mut r = BerReader::new(value);
187        r.read_sequence(Tag::sequence(), |inner| {
188            let code = ResultCode::from_i64(inner.read_enumerated()?);
189            let attr = if !inner.is_empty() {
190                let tag = inner.peek_tag()?;
191                if tag.class == ldap_client_ber::Class::Context && tag.number == 0 {
192                    Some(
193                        String::from_utf8_lossy(inner.read_tagged_implicit_octet_string(0)?)
194                            .into_owned(),
195                    )
196                } else {
197                    None
198                }
199            } else {
200                None
201            };
202            Ok(Self {
203                result_code: code,
204                attribute_type: attr,
205            })
206        })
207        .map_err(Into::into)
208    }
209}
210
211pub fn encode_controls(w: &mut BerWriter, controls: &[Control]) {
212    w.write_sequence(Tag::context_constructed(0), |outer| {
213        for ctrl in controls {
214            outer.write_sequence(Tag::sequence(), |inner| {
215                inner.write_bytes(ctrl.oid.as_bytes());
216                if ctrl.critical {
217                    inner.write_boolean(true);
218                }
219                if let Some(val) = &ctrl.value {
220                    inner.write_bytes(val);
221                }
222            });
223        }
224    });
225}
226
227pub fn decode_controls(r: &mut BerReader<'_>) -> Result<Vec<Control>, ldap_client_ber::BerError> {
228    let mut controls = Vec::new();
229    r.read_sequence_lax(Tag::context_constructed(0), |outer| {
230        while !outer.is_empty() {
231            outer.read_sequence(Tag::sequence(), |inner| {
232                let oid = String::from_utf8_lossy(inner.read_octet_string()?).into_owned();
233                let mut critical = false;
234                let mut value = None;
235
236                while !inner.is_empty() {
237                    let tag = inner.peek_tag()?;
238                    if tag == Tag::universal(ldap_client_ber::tag::BOOLEAN) {
239                        critical = inner.read_boolean()?;
240                    } else {
241                        value = Some(inner.read_octet_string()?.to_vec());
242                    }
243                }
244
245                controls.push(Control {
246                    oid,
247                    critical,
248                    value,
249                });
250                Ok(())
251            })?;
252        }
253        Ok(())
254    })?;
255    Ok(controls)
256}