cert_management.rs

  1// Copyright (c) 2019 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
  2//
  3// This Source Code Form is subject to the terms of the Mozilla Public
  4// License, v. 2.0. If a copy of the MPL was not distributed with this
  5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6
  7use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
  8use crate::util::helpers::Base64;
  9
 10generate_elem_id!(
 11    /// The name of a certificate.
 12    Name,
 13    "name",
 14    SASL_CERT
 15);
 16
 17generate_element!(
 18    /// An X.509 certificate.
 19    Cert, "x509cert", SASL_CERT,
 20    text: (
 21        /// The BER X.509 data.
 22        data: Base64<Vec<u8>>
 23    )
 24);
 25
 26generate_element!(
 27    /// For the client to upload an X.509 certificate.
 28    Append, "append", SASL_CERT,
 29    children: [
 30        /// The name of this certificate.
 31        name: Required<Name> = ("name", SASL_CERT) => Name,
 32
 33        /// The X.509 certificate to set.
 34        cert: Required<Cert> = ("x509cert", SASL_CERT) => Cert,
 35
 36        /// This client is forbidden from managing certificates.
 37        no_cert_management: Present<_> = ("no-cert-management", SASL_CERT) => bool
 38    ]
 39);
 40
 41impl IqSetPayload for Append {}
 42
 43generate_empty_element!(
 44    /// Client requests the current list of X.509 certificates.
 45    ListCertsQuery,
 46    "items",
 47    SASL_CERT
 48);
 49
 50impl IqGetPayload for ListCertsQuery {}
 51
 52generate_elem_id!(
 53    /// One resource currently using a certificate.
 54    Resource,
 55    "resource",
 56    SASL_CERT
 57);
 58
 59generate_element!(
 60    /// A list of resources currently using this certificate.
 61    Users, "users", SASL_CERT,
 62    children: [
 63        /// Resources currently using this certificate.
 64        resources: Vec<Resource> = ("resource", SASL_CERT) => Resource
 65    ]
 66);
 67
 68generate_element!(
 69    /// An X.509 certificate being set for this user.
 70    Item, "item", SASL_CERT,
 71    children: [
 72        /// The name of this certificate.
 73        name: Required<Name> = ("name", SASL_CERT) => Name,
 74
 75        /// The X.509 certificate to set.
 76        cert: Required<Cert> = ("x509cert", SASL_CERT) => Cert,
 77
 78        /// This client is forbidden from managing certificates.
 79        no_cert_management: Present<_> = ("no-cert-management", SASL_CERT) => bool,
 80
 81        /// List of resources currently using this certificate.
 82        users: Option<Users> = ("users", SASL_CERT) => Users
 83    ]
 84);
 85
 86generate_element!(
 87    /// Server answers with the current list of X.509 certificates.
 88    ListCertsResponse, "items", SASL_CERT,
 89    children: [
 90        /// List of certificates.
 91        items: Vec<Item> = ("item", SASL_CERT) => Item
 92    ]
 93);
 94
 95impl IqResultPayload for ListCertsResponse {}
 96
 97generate_element!(
 98    /// Client disables an X.509 certificate.
 99    Disable, "disable", SASL_CERT,
100    children: [
101        /// Name of the certificate to disable.
102        name: Required<Name> = ("name", SASL_CERT) => Name
103    ]
104);
105
106impl IqSetPayload for Disable {}
107
108generate_element!(
109    /// Client revokes an X.509 certificate.
110    Revoke, "revoke", SASL_CERT,
111    children: [
112        /// Name of the certificate to revoke.
113        name: Required<Name> = ("name", SASL_CERT) => Name
114    ]
115);
116
117impl IqSetPayload for Revoke {}
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122    use crate::ns;
123    use crate::Element;
124    use std::convert::TryFrom;
125    use std::str::FromStr;
126
127    #[cfg(target_pointer_width = "32")]
128    #[test]
129    fn test_size() {
130        assert_size!(Append, 28);
131        assert_size!(Disable, 12);
132        assert_size!(Revoke, 12);
133        assert_size!(ListCertsQuery, 0);
134        assert_size!(ListCertsResponse, 12);
135        assert_size!(Item, 40);
136        assert_size!(Resource, 12);
137        assert_size!(Users, 12);
138        assert_size!(Cert, 12);
139    }
140
141    #[cfg(target_pointer_width = "64")]
142    #[test]
143    fn test_size() {
144        assert_size!(Append, 56);
145        assert_size!(Disable, 24);
146        assert_size!(Revoke, 24);
147        assert_size!(ListCertsQuery, 0);
148        assert_size!(ListCertsResponse, 24);
149        assert_size!(Item, 80);
150        assert_size!(Resource, 24);
151        assert_size!(Users, 24);
152        assert_size!(Cert, 24);
153    }
154
155    #[test]
156    fn simple() {
157        let elem: Element = "<append xmlns='urn:xmpp:saslcert:1'><name>Mobile Client</name><x509cert>AAAA</x509cert></append>".parse().unwrap();
158        let append = Append::try_from(elem).unwrap();
159        assert_eq!(append.name.0, "Mobile Client");
160        assert_eq!(append.cert.data, b"\0\0\0");
161
162        let elem: Element =
163            "<disable xmlns='urn:xmpp:saslcert:1'><name>Mobile Client</name></disable>"
164                .parse()
165                .unwrap();
166        let disable = Disable::try_from(elem).unwrap();
167        assert_eq!(disable.name.0, "Mobile Client");
168
169        let elem: Element =
170            "<revoke xmlns='urn:xmpp:saslcert:1'><name>Mobile Client</name></revoke>"
171                .parse()
172                .unwrap();
173        let revoke = Revoke::try_from(elem).unwrap();
174        assert_eq!(revoke.name.0, "Mobile Client");
175    }
176
177    #[test]
178    fn list() {
179        let elem: Element = r#"<items xmlns='urn:xmpp:saslcert:1'>
180          <item>
181            <name>Mobile Client</name>
182            <x509cert>AAAA</x509cert>
183            <users>
184              <resource>Phone</resource>
185            </users>
186          </item>
187          <item>
188            <name>Laptop</name>
189            <x509cert>BBBB</x509cert>
190          </item>
191        </items>"#
192            .parse()
193            .unwrap();
194        let mut list = ListCertsResponse::try_from(elem).unwrap();
195        assert_eq!(list.items.len(), 2);
196
197        let item = list.items.pop().unwrap();
198        assert_eq!(item.name.0, "Laptop");
199        assert_eq!(item.cert.data, [4, 16, 65]);
200        assert!(item.users.is_none());
201
202        let item = list.items.pop().unwrap();
203        assert_eq!(item.name.0, "Mobile Client");
204        assert_eq!(item.cert.data, b"\0\0\0");
205        assert_eq!(item.users.unwrap().resources.len(), 1);
206    }
207
208    #[test]
209    fn test_serialise() {
210        let append = Append {
211            name: Name::from_str("Mobile Client").unwrap(),
212            cert: Cert {
213                data: b"\0\0\0".to_vec(),
214            },
215            no_cert_management: false,
216        };
217        let elem: Element = append.into();
218        assert!(elem.is("append", ns::SASL_CERT));
219
220        let disable = Disable {
221            name: Name::from_str("Mobile Client").unwrap(),
222        };
223        let elem: Element = disable.into();
224        assert!(elem.is("disable", ns::SASL_CERT));
225        let elem = elem.children().cloned().collect::<Vec<_>>().pop().unwrap();
226        assert!(elem.is("name", ns::SASL_CERT));
227        assert_eq!(elem.text(), "Mobile Client");
228    }
229
230    #[test]
231    fn test_serialize_item() {
232        let reference: Element = "<item xmlns='urn:xmpp:saslcert:1'><name>Mobile Client</name><x509cert>AAAA</x509cert></item>"
233        .parse()
234        .unwrap();
235
236        let item = Item {
237            name: Name::from_str("Mobile Client").unwrap(),
238            cert: Cert {
239                data: b"\0\0\0".to_vec(),
240            },
241            no_cert_management: false,
242            users: None,
243        };
244
245        let serialized: Element = item.into();
246        assert_eq!(serialized, reference);
247    }
248
249    #[test]
250    fn test_serialize_append() {
251        let reference: Element = "<append xmlns='urn:xmpp:saslcert:1'><name>Mobile Client</name><x509cert>AAAA</x509cert></append>"
252        .parse()
253        .unwrap();
254
255        let append = Append {
256            name: Name::from_str("Mobile Client").unwrap(),
257            cert: Cert {
258                data: b"\0\0\0".to_vec(),
259            },
260            no_cert_management: false,
261        };
262
263        let serialized: Element = append.into();
264        assert_eq!(serialized, reference);
265    }
266
267    #[test]
268    fn test_serialize_disable() {
269        let reference: Element =
270            "<disable xmlns='urn:xmpp:saslcert:1'><name>Mobile Client</name></disable>"
271                .parse()
272                .unwrap();
273
274        let disable = Disable {
275            name: Name::from_str("Mobile Client").unwrap(),
276        };
277
278        let serialized: Element = disable.into();
279        assert_eq!(serialized, reference);
280    }
281
282    #[test]
283    fn test_serialize_revoke() {
284        let reference: Element =
285            "<revoke xmlns='urn:xmpp:saslcert:1'><name>Mobile Client</name></revoke>"
286                .parse()
287                .unwrap();
288
289        let revoke = Revoke {
290            name: Name::from_str("Mobile Client").unwrap(),
291        };
292
293        let serialized: Element = revoke.into();
294        assert_eq!(serialized, reference);
295    }
296}