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}