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}