Skip to main content

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}