databend_client/
auth.rs

1// Copyright 2021 Datafuse Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use reqwest::RequestBuilder;
16
17use crate::error::{Error, Result};
18
19pub trait Auth: Sync + Send {
20    fn wrap(&self, builder: RequestBuilder) -> Result<RequestBuilder>;
21    fn can_reload(&self) -> bool {
22        false
23    }
24    fn username(&self) -> String;
25}
26
27#[derive(Clone)]
28pub struct BasicAuth {
29    username: String,
30    password: SensitiveString,
31}
32
33impl BasicAuth {
34    pub fn new(username: impl ToString, password: impl ToString) -> Self {
35        Self {
36            username: username.to_string(),
37            password: SensitiveString(password.to_string()),
38        }
39    }
40}
41
42impl Auth for BasicAuth {
43    fn wrap(&self, builder: RequestBuilder) -> Result<RequestBuilder> {
44        Ok(builder.basic_auth(&self.username, Some(self.password.inner())))
45    }
46
47    fn username(&self) -> String {
48        self.username.clone()
49    }
50}
51
52#[derive(Clone)]
53pub struct AccessTokenAuth {
54    token: SensitiveString,
55}
56
57impl AccessTokenAuth {
58    pub fn new(token: impl ToString) -> Self {
59        Self {
60            token: SensitiveString::from(token.to_string()),
61        }
62    }
63}
64
65impl Auth for AccessTokenAuth {
66    fn wrap(&self, builder: RequestBuilder) -> Result<RequestBuilder> {
67        Ok(builder.bearer_auth(self.token.inner()))
68    }
69
70    fn username(&self) -> String {
71        "token".to_string()
72    }
73}
74
75#[derive(Clone)]
76pub struct AccessTokenFileAuth {
77    token_file: String,
78}
79
80impl AccessTokenFileAuth {
81    pub fn new(token_file: impl ToString) -> Self {
82        let token_file = token_file.to_string();
83        Self { token_file }
84    }
85}
86
87impl Auth for AccessTokenFileAuth {
88    fn wrap(&self, builder: RequestBuilder) -> Result<RequestBuilder> {
89        let token = std::fs::read_to_string(&self.token_file).map_err(|e| {
90            Error::IO(format!(
91                "cannot read access token from file {}: {}",
92                self.token_file, e
93            ))
94        })?;
95        Ok(builder.bearer_auth(token.trim()))
96    }
97
98    fn can_reload(&self) -> bool {
99        true
100    }
101
102    fn username(&self) -> String {
103        "token".to_string()
104    }
105}
106
107#[derive(::serde::Deserialize, ::serde::Serialize)]
108#[serde(from = "String", into = "String")]
109#[derive(Clone, Default, PartialEq, Eq)]
110pub struct SensitiveString(String);
111
112impl From<String> for SensitiveString {
113    fn from(value: String) -> Self {
114        Self(value)
115    }
116}
117
118impl From<&str> for SensitiveString {
119    fn from(value: &str) -> Self {
120        Self(value.to_string())
121    }
122}
123
124impl From<SensitiveString> for String {
125    fn from(value: SensitiveString) -> Self {
126        value.0
127    }
128}
129
130impl std::fmt::Display for SensitiveString {
131    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132        write!(f, "**REDACTED**")
133    }
134}
135
136impl std::fmt::Debug for SensitiveString {
137    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138        // we keep the double quotes here to keep the String behavior
139        write!(f, "\"**REDACTED**\"")
140    }
141}
142
143impl SensitiveString {
144    #[must_use]
145    pub fn inner(&self) -> &str {
146        self.0.as_str()
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153
154    #[test]
155    fn serialization() {
156        let json_value = "\"foo\"";
157        let value: SensitiveString = serde_json::from_str(json_value).unwrap();
158        let result: String = serde_json::to_string(&value).unwrap();
159        assert_eq!(result, json_value);
160    }
161
162    #[test]
163    fn hide_content() {
164        let value = SensitiveString("hello world".to_string());
165        let display = format!("{value}");
166        assert_eq!(display, "**REDACTED**");
167        let debug = format!("{value:?}");
168        assert_eq!(debug, "\"**REDACTED**\"");
169    }
170}