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::{IqSetPayload, IqGetPayload, IqResultPayload};
  8use crate::util::helpers::Base64;
  9
 10generate_elem_id!(
 11    /// The name of a certificate.
 12    Name, "name", SASL_CERT
 13);
 14
 15generate_element!(
 16    /// An X.509 certificate.
 17    Cert, "x509cert", SASL_CERT,
 18    text: (
 19        /// The BER X.509 data.
 20        data: Base64<Vec<u8>>
 21    )
 22);
 23
 24generate_element!(
 25    /// For the client to upload an X.509 certificate.
 26    Append, "append", SASL_CERT,
 27    children: [
 28        /// The name of this certificate.
 29        name: Required<Name> = ("name", SASL_CERT) => Name,
 30
 31        /// The X.509 certificate to set.
 32        cert: Required<Cert> = ("x509cert", SASL_CERT) => Cert,
 33
 34        /// This client is forbidden from managing certificates.
 35        no_cert_management: Present<_> = ("no-cert-management", SASL_CERT) => bool
 36    ]
 37);
 38
 39impl IqSetPayload for Append {}
 40
 41generate_empty_element!(
 42    /// Client requests the current list of X.509 certificates.
 43    ListCertsQuery, "items", SASL_CERT
 44);
 45
 46impl IqGetPayload for ListCertsQuery {}
 47
 48generate_elem_id!(
 49    /// One resource currently using a certificate.
 50    Resource, "resource", SASL_CERT
 51);
 52
 53generate_element!(
 54    /// A list of resources currently using this certificate.
 55    Users, "users", SASL_CERT,
 56    children: [
 57        /// Resources currently using this certificate.
 58        resources: Vec<Resource> = ("resource", SASL_CERT) => Resource
 59    ]
 60);
 61
 62generate_element!(
 63    /// An X.509 certificate being set for this user.
 64    Item, "item", SASL_CERT,
 65    children: [
 66        /// The name of this certificate.
 67        name: Required<Name> = ("name", SASL_CERT) => Name,
 68
 69        /// The X.509 certificate to set.
 70        cert: Required<Cert> = ("x509cert", SASL_CERT) => Cert,
 71
 72        /// This client is forbidden from managing certificates.
 73        no_cert_management: Present<_> = ("no-cert-management", SASL_CERT) => bool,
 74
 75        /// List of resources currently using this certificate.
 76        users: Option<Users> = ("users", SASL_CERT) => Users
 77    ]
 78);
 79
 80generate_element!(
 81    /// Server answers with the current list of X.509 certificates.
 82    ListCertsResponse, "items", SASL_CERT,
 83    children: [
 84        /// List of certificates.
 85        items: Vec<Item> = ("item", SASL_CERT) => Item
 86    ]
 87);
 88
 89impl IqResultPayload for ListCertsResponse {}
 90
 91generate_element!(
 92    /// Client disables an X.509 certificate.
 93    Disable, "disable", SASL_CERT,
 94    children: [
 95        /// Name of the certificate to disable.
 96        name: Required<Name> = ("name", SASL_CERT) => Name
 97    ]
 98);
 99
100impl IqSetPayload for Disable {}
101
102generate_element!(
103    /// Client revokes an X.509 certificate.
104    Revoke, "revoke", SASL_CERT,
105    children: [
106        /// Name of the certificate to revoke.
107        name: Required<Name> = ("name", SASL_CERT) => Name
108    ]
109);
110
111impl IqSetPayload for Revoke {}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116    use crate::Element;
117    use std::convert::TryFrom;
118    use std::str::FromStr;
119    use crate::ns;
120
121    #[cfg(target_pointer_width = "32")]
122    #[test]
123    fn test_size() {
124        assert_size!(Append, 28);
125        assert_size!(Disable, 12);
126        assert_size!(Revoke, 12);
127        assert_size!(ListCertsQuery, 0);
128        assert_size!(ListCertsResponse, 12);
129        assert_size!(Item, 40);
130        assert_size!(Resource, 12);
131        assert_size!(Users, 12);
132        assert_size!(Cert, 12);
133    }
134
135    #[cfg(target_pointer_width = "64")]
136    #[test]
137    fn test_size() {
138        assert_size!(Append, 56);
139        assert_size!(Disable, 24);
140        assert_size!(Revoke, 24);
141        assert_size!(ListCertsQuery, 0);
142        assert_size!(ListCertsResponse, 24);
143        assert_size!(Item, 80);
144        assert_size!(Resource, 24);
145        assert_size!(Users, 24);
146        assert_size!(Cert, 24);
147    }
148
149    #[test]
150    fn simple() {
151        let elem: Element = "<append xmlns='urn:xmpp:saslcert:1'><name>Mobile Client</name><x509cert>AAAA</x509cert></append>".parse().unwrap();
152        let append = Append::try_from(elem).unwrap();
153        assert_eq!(append.name.0, "Mobile Client");
154        assert_eq!(append.cert.data, b"\0\0\0");
155
156        let elem: Element = "<disable xmlns='urn:xmpp:saslcert:1'><name>Mobile Client</name></disable>".parse().unwrap();
157        let disable = Disable::try_from(elem).unwrap();
158        assert_eq!(disable.name.0, "Mobile Client");
159
160        let elem: Element = "<revoke xmlns='urn:xmpp:saslcert:1'><name>Mobile Client</name></revoke>".parse().unwrap();
161        let revoke = Revoke::try_from(elem).unwrap();
162        assert_eq!(revoke.name.0, "Mobile Client");
163    }
164
165    #[test]
166    fn list() {
167        let elem: Element = r#"
168        <items xmlns='urn:xmpp:saslcert:1'>
169          <item>
170            <name>Mobile Client</name>
171            <x509cert>AAAA</x509cert>
172            <users>
173              <resource>Phone</resource>
174            </users>
175          </item>
176          <item>
177            <name>Laptop</name>
178            <x509cert>BBBB</x509cert>
179          </item>
180        </items>"#.parse().unwrap();
181        let mut list = ListCertsResponse::try_from(elem).unwrap();
182        assert_eq!(list.items.len(), 2);
183
184        let item = list.items.pop().unwrap();
185        assert_eq!(item.name.0, "Laptop");
186        assert_eq!(item.cert.data, [4, 16, 65]);
187        assert!(item.users.is_none());
188
189        let item = list.items.pop().unwrap();
190        assert_eq!(item.name.0, "Mobile Client");
191        assert_eq!(item.cert.data, b"\0\0\0");
192        assert_eq!(item.users.unwrap().resources.len(), 1);
193    }
194
195    #[test]
196    fn test_serialise() {
197        let append = Append {
198            name: Name::from_str("Mobile Client").unwrap(),
199            cert: Cert { data: b"\0\0\0".to_vec() },
200            no_cert_management: false,
201        };
202        let elem: Element = append.into();
203        assert!(elem.is("append", ns::SASL_CERT));
204
205        let disable = Disable {
206            name: Name::from_str("Mobile Client").unwrap(),
207        };
208        let elem: Element = disable.into();
209        assert!(elem.is("disable", ns::SASL_CERT));
210        let elem = elem.children().cloned().collect::<Vec<_>>().pop().unwrap();
211        assert!(elem.is("name", ns::SASL_CERT));
212        assert_eq!(elem.text(), "Mobile Client");
213    }
214}