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 minidom::Element;
117 use std::convert::TryFrom;
118 use std::str::FromStr;
119 use crate::ns;
120
121 #[test]
122 fn test_size() {
123 assert_size!(Append, 56);
124 }
125
126 #[test]
127 fn simple() {
128 let elem: Element = "<append xmlns='urn:xmpp:saslcert:1'><name>Mobile Client</name><x509cert>AAAA</x509cert></append>".parse().unwrap();
129 let append = Append::try_from(elem).unwrap();
130 assert_eq!(append.name.0, "Mobile Client");
131 assert_eq!(append.cert.data, b"\0\0\0");
132
133 let elem: Element = "<disable xmlns='urn:xmpp:saslcert:1'><name>Mobile Client</name></disable>".parse().unwrap();
134 let disable = Disable::try_from(elem).unwrap();
135 assert_eq!(disable.name.0, "Mobile Client");
136
137 let elem: Element = "<revoke xmlns='urn:xmpp:saslcert:1'><name>Mobile Client</name></revoke>".parse().unwrap();
138 let revoke = Revoke::try_from(elem).unwrap();
139 assert_eq!(revoke.name.0, "Mobile Client");
140 }
141
142 #[test]
143 fn list() {
144 let elem: Element = r#"
145 <items xmlns='urn:xmpp:saslcert:1'>
146 <item>
147 <name>Mobile Client</name>
148 <x509cert>AAAA</x509cert>
149 <users>
150 <resource>Phone</resource>
151 </users>
152 </item>
153 <item>
154 <name>Laptop</name>
155 <x509cert>BBBB</x509cert>
156 </item>
157 </items>"#.parse().unwrap();
158 let mut list = ListCertsResponse::try_from(elem).unwrap();
159 assert_eq!(list.items.len(), 2);
160
161 let item = list.items.pop().unwrap();
162 assert_eq!(item.name.0, "Laptop");
163 assert_eq!(item.cert.data, [4, 16, 65]);
164 assert!(item.users.is_none());
165
166 let item = list.items.pop().unwrap();
167 assert_eq!(item.name.0, "Mobile Client");
168 assert_eq!(item.cert.data, b"\0\0\0");
169 assert_eq!(item.users.unwrap().resources.len(), 1);
170 }
171
172 #[test]
173 fn test_serialise() {
174 let append = Append {
175 name: Name::from_str("Mobile Client").unwrap(),
176 cert: Cert { data: b"\0\0\0".to_vec() },
177 no_cert_management: false,
178 };
179 let elem: Element = append.into();
180 assert!(elem.is("append", ns::SASL_CERT));
181
182 let disable = Disable {
183 name: Name::from_str("Mobile Client").unwrap(),
184 };
185 let elem: Element = disable.into();
186 assert!(elem.is("disable", ns::SASL_CERT));
187 let elem = elem.children().cloned().collect::<Vec<_>>().pop().unwrap();
188 assert!(elem.is("name", ns::SASL_CERT));
189 assert_eq!(elem.text(), "Mobile Client");
190 }
191}