1doc_auto_cfg! {
4 pub mod auth;
5 #[cfg(feature = "test")]
6 pub mod recorder;
7 pub mod urlencode;
8}
9
10doc_auto_cfg! {
11 pub use auth::Authorizer;
12 #[cfg(feature = "test")]
13 pub use recorder::Recorder;
14 pub use urlencode::Urlencoder;
15}
16
17use core::fmt::Display;
18
19#[macro_export]
25macro_rules! skip_serialize_oauth_parameters {
26 () => {
27 fn serialize_oauth_callback(&mut self) {}
28 fn serialize_oauth_consumer_key(&mut self) {}
29 fn serialize_oauth_nonce(&mut self) {}
30 fn serialize_oauth_signature_method(&mut self) {}
31 fn serialize_oauth_timestamp(&mut self) {}
32 fn serialize_oauth_token(&mut self) {}
33 fn serialize_oauth_verifier(&mut self) {}
34 fn serialize_oauth_version(&mut self) {}
35 };
36}
37
38#[doc(inline)]
39pub use skip_serialize_oauth_parameters;
40
41#[cfg_attr(all(feature = "alloc", feature = "hmac-sha1"), doc = " ```")]
52#[cfg_attr(not(all(feature = "alloc", feature = "hmac-sha1")), doc = " ```ignore")]
53pub trait Serializer {
100 type Output;
102
103 fn serialize_parameter<V>(&mut self, key: &str, value: V)
112 where
113 V: Display;
114
115 fn serialize_parameter_encoded<V>(&mut self, key: &str, value: V)
124 where
125 V: Display;
126
127 fn serialize_oauth_callback(&mut self);
131
132 fn serialize_oauth_consumer_key(&mut self);
136
137 fn serialize_oauth_nonce(&mut self);
141
142 fn serialize_oauth_signature_method(&mut self);
146
147 fn serialize_oauth_timestamp(&mut self);
151
152 fn serialize_oauth_token(&mut self);
156
157 fn serialize_oauth_verifier(&mut self);
161
162 fn serialize_oauth_version(&mut self);
166
167 fn end(self) -> Self::Output;
169}
170
171pub trait SerializerExt: Serializer {
173 fn serialize_oauth_parameters(&mut self);
175}
176
177impl<S: Serializer> SerializerExt for S {
178 fn serialize_oauth_parameters(&mut self) {
179 self.serialize_oauth_callback();
180 self.serialize_oauth_consumer_key();
181 self.serialize_oauth_nonce();
182 self.serialize_oauth_signature_method();
183 self.serialize_oauth_timestamp();
184 self.serialize_oauth_token();
185 self.serialize_oauth_verifier();
186 self.serialize_oauth_version();
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 #[cfg(not(feature = "std"))]
193 extern crate std;
194
195 use std::println;
196 use std::string::{String, ToString};
197
198 use super::*;
199
200 #[cfg(feature = "hmac-sha1")]
201 use crate::signature_method::HmacSha1;
202 use crate::signature_method::{Plaintext, Sign, SignatureMethod};
203 #[cfg(any(feature = "alloc", feature = "hmac-sha1"))]
204 use crate::Credentials;
205
206 cfg_if::cfg_if! {
209 if #[cfg(any(feature = "alloc", feature = "hmac-sha1"))] {
210 const CK: &str = "xvz1evFS4wEEPTGEFPHBog";
211 const CS: &str = "kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw";
212 const AK: &str = "370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb";
213 const AS: &str = "LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE";
214 }
215 }
216 #[cfg(feature = "hmac-sha1")]
217 const NONCE: &str = "kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg";
218 #[cfg(feature = "hmac-sha1")]
219 const TIMESTAMP: u64 = 1318622958;
220
221 struct Inspect<SM>(SM);
222 struct InspectSign<S>(S);
223
224 impl<SM: SignatureMethod> SignatureMethod for Inspect<SM> {
225 type Sign = InspectSign<SM::Sign>;
226
227 fn sign_with(self, client_secret: &str, token_secret: Option<&str>) -> Self::Sign {
228 println!("client_secret: {:?}", client_secret);
229 println!("token_secret: {:?}", token_secret);
230 InspectSign(self.0.sign_with(client_secret, token_secret))
231 }
232 }
233
234 #[allow(dead_code)]
235 #[derive(Clone, Debug)]
236 struct AssertImpl<'a>(
237 #[cfg(feature = "hmac-sha1")] Authorizer<'a, HmacSha1, String>,
238 Authorizer<'a, Plaintext<String>, String>,
239 );
240
241 impl<S: Sign> Sign for InspectSign<S> {
242 type Signature = S::Signature;
243
244 fn get_signature_method_name(&self) -> &'static str {
245 self.0.get_signature_method_name()
246 }
247 fn request_method(&mut self, method: &str) {
248 println!("method: {:?}", method);
249 self.0.request_method(method);
250 }
251 fn uri<T: Display>(&mut self, uri: T) {
252 println!("uri: {:?}", uri.to_string());
253 self.0.uri(uri);
254 }
255 fn delimiter(&mut self) {
256 println!("delimiter");
257 self.0.delimiter();
258 }
259 fn parameter<V: Display>(&mut self, k: &str, v: V) {
260 println!("parameter: {:?}={:?}", k, v.to_string());
261 self.0.parameter(k, v);
262 }
263 fn end(self) -> S::Signature {
264 println!("end");
265 self.0.end()
266 }
267 }
268
269 #[cfg(feature = "hmac-sha1")]
270 #[test]
271 fn serialize() {
272 use core::num::NonZeroU64;
273 use std::format;
274
275 use crate::serializer::auth;
276
277 macro_rules! test {
278 ($((
279 $method:expr, $ep:expr,
280 $ck:expr, $cs:expr, $t:expr, $ts:expr,
281 $nonce:expr, $timestamp:expr,
282 { $($param1:tt)* }, { $($param2:tt)* } $(,)*
283 ) -> ($expected_sign:expr, $expected_data:expr $(,)*);)*) => {
284 let client = Credentials::new(CK, CS);
285 let token = Credentials::new(AK, AS);
286 let mut options = auth::Options::new();
287 $(
288 options.nonce($nonce)
289 .timestamp($timestamp)
290 .version(true);
291 let mut auth = Authorizer::authorization_with_buf(
292 String::new(),
293 $method,
294 $ep,
295 client,
296 Some(token),
297 &options,
298 Inspect(crate::HMAC_SHA1),
299 );
300
301 test_inner! { auth; $($param1)* }
302 auth.serialize_oauth_parameters();
303 test_inner! { auth; $($param2)* }
304
305 let authorization = auth.end();
306 let expected = format!(
307 "OAuth \
308 oauth_consumer_key=\"{}\",\
309 oauth_nonce=\"{}\",\
310 oauth_signature_method=\"HMAC-SHA1\",\
311 oauth_timestamp=\"{}\",\
312 oauth_token=\"{}\",\
313 oauth_version=\"1.0\",\
314 oauth_signature=\"{}\"",
315 $ck,
316 $nonce,
317 $timestamp,
318 token.identifier,
319 $expected_sign,
320 );
321 assert_eq!(authorization, expected);
322
323 let mut urlencoded = if $method == "POST" {
324 Urlencoder::form_with_buf(String::new())
325 } else {
326 Urlencoder::query($ep.to_string())
327 };
328
329 test_inner! { urlencoded; $($param1)* }
330 urlencoded.serialize_oauth_parameters();
331 test_inner! { urlencoded; $($param2)* }
332
333 let data = urlencoded.end();
334 assert_eq!(data, $expected_data);
335 )*
336 };
337 }
338
339 macro_rules! test_inner {
340 ($ser:ident; encoded $key:ident: $v:expr, $($rest:tt)*) => {
341 $ser.serialize_parameter_encoded(stringify!($key), $v);
342 test_inner! { $ser; $($rest)* }
343 };
344 ($ser:ident; $key:ident: $v:expr, $($rest:tt)*) => {
345 $ser.serialize_parameter(stringify!($key), $v);
346 test_inner! { signerb; $($rest)* }
347 };
348 ($_signer:ident;) => ();
349 }
350
351 let timestamp = NonZeroU64::new(TIMESTAMP).unwrap();
352
353 test! {
354 (
355 "GET", "https://stream.twitter.com/1.1/statuses/sample.json",
356 CK, CS, AK, AS, NONCE, timestamp,
357 {}, { encoded stall_warnings: "true", },
358 ) -> (
359 "OGQqcy4l5xWBFX7t0DrkP5%2FD0rM%3D",
360 "https://stream.twitter.com/1.1/statuses/sample.json?stall_warnings=true",
361 );
362 (
363 "POST", "https://api.twitter.com/1.1/statuses/update.json",
364 CK, CS, AK, AS, NONCE, timestamp,
365 { encoded include_entities: "true", },
366 { status: "Hello Ladies + Gentlemen, a signed OAuth request!", },
367 ) -> (
368 "hCtSmYh%2BiHYCEqBWrE7C7hYmtUk%3D",
369 "include_entities=true&\
370 status=Hello%20Ladies%20%2B%20Gentlemen%2C%20a%20signed%20OAuth%20request%21",
371 );
372 ("POST", "https://example.com/post.json", CK, CS, AK, AS, NONCE, timestamp, {}, {})
373 -> ("pN52L1gJ6sOyYOyv23cwfWFsIZc%3D", "");
374 (
375 "GET", "https://example.com/get.json",
376 CK, CS, AK, AS, NONCE, timestamp,
377 { encoded bar: "%E9%85%92%E5%A0%B4", foo: "ふー", }, {},
378 ) -> (
379 "Xp35hf3T21yhpEuxez7p6bV62Bw%3D",
380 "https://example.com/get.json?bar=%E9%85%92%E5%A0%B4&foo=%E3%81%B5%E3%83%BC",
381 );
382 }
383 }
384
385 #[cfg(all(feature = "alloc", debug_assertions))]
386 #[test]
387 #[should_panic(
388 expected = "appended key is less than previously appended one in dictionary order\
389 \n previous: `\"foo\"`,\
390 \n current: `\"bar\"`"
391 )]
392 fn panic_on_misordering() {
393 let client = Credentials::new(CK, CS);
394 let token = Credentials::new(AK, AS);
395 let options = auth::Options::default();
396 let mut ser = Authorizer::authorization_with_buf(
397 String::new(),
398 "",
399 "",
400 client,
401 Some(token),
402 &options,
403 Plaintext::<String>::with_buf(),
404 );
405 ser.serialize_parameter_encoded("foo", true);
406 ser.serialize_parameter("bar", "ばー!");
407 }
408}