ldap3_serde/exop_impl/
passmod.rs

1use super::{Exop, ExopParser};
2
3use bytes::BytesMut;
4
5use lber_serde::common::TagClass;
6use lber_serde::parse::parse_tag;
7use lber_serde::structures::{ASNTag, OctetString, Sequence, Tag};
8use lber_serde::write;
9
10pub const PASSMOD_OID: &str = "1.3.6.1.4.1.4203.1.11.1";
11
12/// Password Modify extended operation ([RFC 3062](https://tools.ietf.org/html/rfc3062)).
13///
14/// The structure contains elements of a Password Modify request. The precise semantics
15/// of having a particular field present or absent will depend on the server receiving
16/// the request; consult the server documentation. Some rules are prescribed by the RFC
17/// and should generally apply:
18///
19/// * The `user_id` field contains the identity of the user whose password is being changed.
20///   This may or may not be a DN. If the field is absent, the identity associated with the
21///   current connection will be used.
22///
23/// * If `old_pass` is present, it must match the existing password.
24///
25/// * If `new_pass` is not present, the server may autogenerate the new password.
26///
27/// Although the specification doesn't constrain the values of old and new passwords, this
28/// implementation limits them to UTF-8 strings.
29#[derive(Clone, Debug)]
30pub struct PasswordModify<'a> {
31    pub user_id: Option<&'a str>,
32    pub old_pass: Option<&'a str>,
33    pub new_pass: Option<&'a str>,
34}
35
36/// Password Modify response.
37///
38/// If the server has generated a new password, it must send its value in the response.
39#[derive(Clone, Debug)]
40pub struct PasswordModifyResp {
41    pub gen_pass: String,
42}
43
44impl<'a> From<PasswordModify<'a>> for Exop {
45    fn from(pm: PasswordModify<'a>) -> Exop {
46        let mut pm_vec = vec![];
47        if let Some(user_id) = pm.user_id {
48            pm_vec.push(Tag::OctetString(OctetString {
49                id: 0,
50                class: TagClass::Context,
51                inner: Vec::from(user_id.as_bytes()),
52            }));
53        }
54        if let Some(old_pass) = pm.old_pass {
55            pm_vec.push(Tag::OctetString(OctetString {
56                id: 1,
57                class: TagClass::Context,
58                inner: Vec::from(old_pass.as_bytes()),
59            }));
60        }
61        if let Some(new_pass) = pm.new_pass {
62            pm_vec.push(Tag::OctetString(OctetString {
63                id: 2,
64                class: TagClass::Context,
65                inner: Vec::from(new_pass.as_bytes()),
66            }));
67        }
68        let val = if pm_vec.is_empty() {
69            None
70        } else {
71            let pm_val = Tag::Sequence(Sequence {
72                inner: pm_vec,
73                ..Default::default()
74            })
75            .into_structure();
76            let mut buf = BytesMut::new();
77            write::encode_into(&mut buf, pm_val).expect("encoded");
78            Some(Vec::from(&buf[..]))
79        };
80        Exop {
81            name: Some(PASSMOD_OID.to_owned()),
82            val,
83        }
84    }
85}
86
87impl ExopParser for PasswordModifyResp {
88    fn parse(val: &[u8]) -> PasswordModifyResp {
89        let tags = match parse_tag(val) {
90            Ok((_, tag)) => tag,
91            _ => panic!("failed to parse password modify return value"),
92        };
93        let mut tags = tags
94            .expect_constructed()
95            .expect("password modify sequence")
96            .into_iter();
97        let gen_pass = tags
98            .next()
99            .expect("element")
100            .match_class(TagClass::Context)
101            .and_then(|t| t.match_id(0))
102            .and_then(|t| t.expect_primitive())
103            .expect("generated password")
104            .as_slice()
105            .to_owned();
106        let gen_pass = String::from_utf8(gen_pass).expect("generated password not UTF-8");
107        PasswordModifyResp { gen_pass }
108    }
109}