chewdata/connector/authenticator/basic.rs
1//! Authenticate the request with basic auth.
2//!
3//! ### Configuration
4//!
5//!| key | alias | Description | Default Value | Possible Values |
6//!| -------- | ---------- | -------------------------------------------- | ------------- | --------------- |
7//!| type | - | Required in order to use this authentication | `basic` | `basic` |
8//!| username | user / usr | Username to use for the authentification | `null` | String |
9//!| password | pass / pwd | Password to use for the authentification | `null` | String |
10//!
11//! ### Examples
12//!
13//!```json
14//![
15//! {
16//! "type": "read",
17//! "connector":{
18//! "type": "curl",
19//! "endpoint": "{{ CURL_ENDPOINT }}",
20//! "path": "/get",
21//! "method": "get",
22//! "authenticator": {
23//! "type": "basic",
24//! "username": "{{ BASIC_USERNAME }}",
25//! "password": "{{ BASIC_PASSWORD }}",
26//! }
27//! },
28//! }
29//!]
30//!```
31use crate::helper::string::{DisplayOnlyForDebugging, Obfuscate};
32
33use super::Authenticator;
34use async_trait::async_trait;
35use base64::Engine;
36use http::header;
37use serde::{Deserialize, Serialize};
38use std::{
39 fmt,
40 io::{Error, ErrorKind, Result},
41};
42
43#[derive(Deserialize, Serialize, Clone)]
44#[serde(default, deny_unknown_fields)]
45pub struct Basic {
46 #[serde(alias = "usr")]
47 #[serde(alias = "user")]
48 pub username: String,
49 #[serde(alias = "pwd")]
50 #[serde(alias = "pass")]
51 pub password: String,
52}
53
54impl Default for Basic {
55 fn default() -> Self {
56 Basic {
57 username: "".to_owned(),
58 password: "".to_owned(),
59 }
60 }
61}
62
63impl fmt::Debug for Basic {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 f.debug_struct("Basic")
66 .field("username", &self.username)
67 .field(
68 "password",
69 &self.password.to_obfuscate().display_only_for_debugging(),
70 )
71 .finish()
72 }
73}
74
75impl Basic {
76 /// Get new authentification
77 ///
78 /// # Examples
79 ///
80 /// ```
81 /// use chewdata::connector::authenticator::basic::Basic;
82 ///
83 /// let auth = Basic::new("my_username", "my_password");
84 /// ```
85 pub fn new(username: &str, password: &str) -> Self {
86 Basic {
87 username: username.to_string(),
88 password: password.to_string(),
89 }
90 }
91}
92
93#[async_trait]
94impl Authenticator for Basic {
95 /// See [`Authenticator::authenticate`] for more details.
96 ///
97 /// # Examples
98 ///
99 /// ```
100 /// use chewdata::connector::authenticator::{AuthenticatorType, basic::Basic, Authenticator};
101 /// use smol::prelude::*;
102 /// use std::io;
103 /// use base64::Engine;
104 ///
105 /// use macro_rules_attribute::apply;
106 /// use smol_macros::main;
107 ///
108 /// #[apply(main!)]
109 /// async fn main() -> io::Result<()> {
110 /// let username = "my_username";
111 /// let password = "my_password";
112 /// let token_expected = "Basic bXlfdXNlcm5hbWU6bXlfcGFzc3dvcmQ=";
113 ///
114 /// let (auth_name, auth_value) = Basic::new(username, password).authenticate().await?;
115 /// assert_eq!(auth_name, "authorization".to_string().into_bytes());
116 /// assert_eq!(token_expected.as_bytes(), auth_value);
117 ///
118 /// Ok(())
119 /// }
120 /// ```
121 async fn authenticate(&self) -> Result<(Vec<u8>, Vec<u8>)> {
122 if let ("", "") = (self.username.as_ref(), self.password.as_ref()) {
123 return Err(Error::new(
124 ErrorKind::InvalidData,
125 "Basic authentification require a username and a password",
126 ));
127 }
128
129 let basic = base64::engine::general_purpose::STANDARD
130 .encode(format!("{}:{}", self.username, self.password));
131
132 Ok((
133 header::AUTHORIZATION.as_str().as_bytes().to_vec(),
134 format!("Basic {}", basic).as_bytes().to_vec(),
135 ))
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142 use macro_rules_attribute::apply;
143 use smol_macros::test;
144
145 #[apply(test!)]
146 async fn authenticate() {
147 let username = "my_username";
148 let password = "my_password";
149 let token_expected = "Basic bXlfdXNlcm5hbWU6bXlfcGFzc3dvcmQ=";
150
151 let (auth_name, auth_value) = Basic::new(username, password).authenticate().await.unwrap();
152 assert_eq!(auth_name, "authorization".to_string().into_bytes());
153 assert_eq!(token_expected.as_bytes(), auth_value);
154 }
155}