1use std::{borrow::Cow, collections::HashMap};
2
3use crate::{SecretsProvider, SignResult, SignerError};
4use crate::{
5 OAUTH_CALLBACK_KEY, OAUTH_CONSUMER_KEY, OAUTH_KEY_PREFIX, OAUTH_NONCE_KEY,
6 OAUTH_SIGNATURE_METHOD_KEY, OAUTH_TIMESTAMP_KEY, OAUTH_TOKEN_KEY, OAUTH_VERIFIER_KEY,
7 OAUTH_VERSION_KEY, REALM_KEY,
8};
9use http::Method;
10use oauth1_request::signature_method::SignatureMethod;
11use oauth1_request::signer::Signer as OAuthSigner;
12use oauth1_request::{HmacSha1, Options};
13use url::Url;
14
15#[derive(Debug, Clone)]
27pub struct Signer<'a, TSecrets, TSM>
28where
29 TSecrets: SecretsProvider + Clone,
30 TSM: SignatureMethod + Clone,
31{
32 secrets: TSecrets,
33 parameters: Result<OAuthParameters<'a, TSM>, SignerError>,
34}
35
36impl<'a, TSecretsProvider, TSM> Signer<'a, TSecretsProvider, TSM>
37where
38 TSecretsProvider: SecretsProvider + Clone,
39 TSM: SignatureMethod + Clone,
40{
41 pub fn new(secrets: TSecretsProvider, parameters: OAuthParameters<'a, TSM>) -> Self {
42 Signer {
43 secrets,
44 parameters: Ok(parameters),
45 }
46 }
47
48 pub fn override_oauth_parameter(mut self, parameters: HashMap<String, String>) -> Self {
49 for (key, value) in parameters {
50 self.parameters = match self.parameters {
51 Ok(p) => match key.as_str() {
52 OAUTH_CALLBACK_KEY => Ok(p.callback(value)),
54 OAUTH_NONCE_KEY => Ok(p.nonce(value)),
55 OAUTH_VERIFIER_KEY => Ok(p.verifier(value)),
56 REALM_KEY => Ok(p.realm(value)),
57 OAUTH_TIMESTAMP_KEY => match value.parse::<u64>() {
59 Ok(v) => Ok(p.timestamp(v)),
60 Err(_) => Err(SignerError::InvalidTimestamp(value)),
61 },
62 OAUTH_VERSION_KEY => match value.as_str() {
63 "1.0" => Ok(p.version(true)),
64 "" => Ok(p.version(false)),
65 _ => Err(SignerError::InvalidVersion(value)),
66 },
67 OAUTH_SIGNATURE_METHOD_KEY | OAUTH_CONSUMER_KEY | OAUTH_TOKEN_KEY => {
69 Err(SignerError::UnconfigurableParameter(key))
70 }
71 _ => Err(SignerError::UnknownParameter(key)),
72 },
73 Err(e) => Err(e),
74 };
75 }
76
77 self
78 }
79
80 pub(crate) fn generate_signature(
82 self,
83 method: Method,
84 url: Url,
85 payload: &str,
86 is_url_query: bool,
87 ) -> SignResult<String> {
88 let (consumer_key, consumer_secret) = self.secrets.get_consumer_key_pair();
89 let (token, token_secret) = self.secrets.get_token_option_pair();
90 let params = self.parameters?;
92 let options = params.build_options(token);
93
94 let parsed_payload: Vec<(Cow<str>, Cow<str>)> =
96 url::form_urlencoded::parse(payload.as_bytes())
97 .into_iter()
98 .collect();
99 let oauth_identifier = vec![(Cow::from(OAUTH_KEY_PREFIX), Cow::from(""))];
101 let mut sorted_query = [parsed_payload, oauth_identifier].concat();
102
103 sorted_query.sort();
105
106 let mut divided = sorted_query
108 .splitn(2, |(k, _)| k == &OAUTH_KEY_PREFIX)
109 .into_iter();
110 let query_before_oauth = divided.next().unwrap();
111 let query_after_oauth = divided.next().unwrap_or_default();
112
113 let sig_method = params.signature_method.clone();
116 let mut signer = generate_signer(
118 sig_method,
119 method.as_str(),
120 url,
121 consumer_secret,
122 token_secret,
123 is_url_query,
124 );
125
126 for (key, value) in query_before_oauth {
128 if !key.starts_with(OAUTH_KEY_PREFIX) {
129 signer.parameter(key, value);
131 }
132 }
133 let mut signer = signer.oauth_parameters(consumer_key, &options);
135 for (key, value) in query_after_oauth {
137 if !key.starts_with(OAUTH_KEY_PREFIX) {
138 signer.parameter(key, value);
140 }
141 }
142
143 let sign = signer.finish().authorization;
145
146 if let Some(realm) = params.realm {
147 Ok(format!("{},{}=\"{}\"", sign, REALM_KEY, realm.as_ref()))
149 } else {
150 Ok(sign)
152 }
153 }
154}
155
156fn generate_signer<TSM>(
157 signature_method: TSM,
158 method: &str,
159 url: Url,
160 consumer_secret: &str,
161 token_secret: Option<&str>,
162 is_url_query: bool,
163) -> OAuthSigner<TSM>
164where
165 TSM: SignatureMethod,
166{
167 if is_url_query {
168 OAuthSigner::with_signature_method(
169 signature_method,
170 method,
171 url,
172 consumer_secret,
173 token_secret,
174 )
175 } else {
176 OAuthSigner::form_with_signature_method(
177 signature_method,
178 method,
179 url,
180 consumer_secret,
181 token_secret,
182 )
183 }
184}
185
186#[derive(Debug, Clone)]
250pub struct OAuthParameters<'a, TSM>
251where
252 TSM: SignatureMethod + Clone,
253{
254 callback: Option<Cow<'a, str>>,
255 nonce: Option<Cow<'a, str>>,
256 realm: Option<Cow<'a, str>>,
257 signature_method: TSM,
258 timestamp: Option<u64>,
259 verifier: Option<Cow<'a, str>>,
260 version: bool,
261}
262
263impl Default for OAuthParameters<'static, HmacSha1> {
264 fn default() -> Self {
265 OAuthParameters {
266 callback: None,
267 nonce: None,
268 realm: None,
269 signature_method: HmacSha1,
270 timestamp: None,
271 verifier: None,
272 version: false,
273 }
274 }
275}
276
277impl OAuthParameters<'_, HmacSha1> {
278 pub fn new() -> Self {
279 Default::default()
280 }
281}
282
283impl<'a, TSM> OAuthParameters<'a, TSM>
284where
285 TSM: SignatureMethod + Clone,
286{
287 pub fn callback<T>(self, callback: T) -> Self
289 where
290 T: Into<Cow<'a, str>>,
291 {
292 OAuthParameters {
293 callback: Some(callback.into()),
294 ..self
295 }
296 }
297
298 pub fn nonce<T>(self, nonce: T) -> Self
300 where
301 T: Into<Cow<'a, str>>,
302 {
303 OAuthParameters {
304 nonce: Some(nonce.into()),
305 ..self
306 }
307 }
308
309 pub fn realm<T>(self, realm: T) -> Self
315 where
316 T: Into<Cow<'a, str>>,
317 {
318 OAuthParameters {
319 realm: Some(realm.into()),
320 ..self
321 }
322 }
323
324 pub fn timestamp<T>(self, timestamp: T) -> Self
326 where
327 T: Into<u64>,
328 {
329 OAuthParameters {
330 timestamp: Some(timestamp.into()),
331 ..self
332 }
333 }
334
335 pub fn verifier<T>(self, verifier: T) -> Self
337 where
338 T: Into<Cow<'a, str>>,
339 {
340 OAuthParameters {
341 verifier: Some(verifier.into()),
342 ..self
343 }
344 }
345
346 pub fn version<T>(self, version: T) -> Self
353 where
354 T: Into<bool>,
355 {
356 OAuthParameters {
357 version: version.into(),
358 ..self
359 }
360 }
361 pub fn signature_method<T>(self, signature_method: T) -> OAuthParameters<'a, T>
362 where
363 T: SignatureMethod + Clone,
364 {
365 OAuthParameters {
366 signature_method,
367 callback: None,
368 nonce: None,
369 realm: None,
370 timestamp: None,
371 verifier: None,
372 version: false,
373 }
374 }
375}
376
377impl<T> OAuthParameters<'_, T>
378where
379 T: SignatureMethod + Clone,
380{
381 fn build_options<'a>(&'a self, token: Option<&'a str>) -> Options<'a> {
382 let mut opt = Options::new();
383
384 if let Some(ref callback) = self.callback {
387 opt.callback(callback.as_ref());
388 }
389 if let Some(ref nonce) = self.nonce {
390 opt.nonce(nonce.as_ref());
391 }
392 if let Some(timestamp) = self.timestamp {
393 opt.timestamp(timestamp);
394 }
395 if let Some(token) = token {
396 opt.token(token);
397 }
398 if let Some(ref verifier) = self.verifier {
399 opt.verifier(verifier.as_ref());
400 }
401 opt.version(self.version);
402 if self.version {}
403
404 opt
405 }
406}