auth/user_provider/
static_user_provider.rs1use async_trait::async_trait;
16use snafu::OptionExt;
17
18use crate::common::REDACTED_USER_PROVIDER_OPTION;
19use crate::error::{InvalidConfigSnafu, Result};
20use crate::user_provider::{
21 PgAuthInfo, UserInfoMap, authenticate_with_credential, load_credential_from_file,
22 parse_credential_line, postgres_auth_info_with_credential, warn_if_pg_scram_disabled,
23};
24use crate::{Identity, Password, UserInfoRef, UserProvider};
25
26pub(crate) const STATIC_USER_PROVIDER: &str = "static_user_provider";
27
28pub struct StaticUserProvider {
29 users: UserInfoMap,
30}
31
32impl StaticUserProvider {
33 pub(crate) fn new(value: &str) -> Result<Self> {
34 let (mode, content) = value.split_once(':').context(InvalidConfigSnafu {
35 value: REDACTED_USER_PROVIDER_OPTION,
36 msg: "StaticUserProviderOption must be in format `<option>:<value>`",
37 })?;
38 match mode {
39 "file" => {
40 let users = load_credential_from_file(content)?;
41 Ok(StaticUserProvider { users })
42 }
43 "cmd" => content
44 .split(',')
45 .map(|kv| {
46 parse_credential_line(kv).context(InvalidConfigSnafu {
47 value: REDACTED_USER_PROVIDER_OPTION,
48 msg: "StaticUserProviderOption cmd values must be in format `user=pwd[,user=pwd]`",
49 })
50 })
51 .collect::<Result<UserInfoMap>>()
52 .map(|users| {
53 warn_if_pg_scram_disabled(&users);
54 StaticUserProvider { users }
55 }),
56 _ => InvalidConfigSnafu {
57 value: REDACTED_USER_PROVIDER_OPTION,
58 msg: "StaticUserProviderOption must be in format `file:<path>` or `cmd:<values>`",
59 }
60 .fail(),
61 }
62 }
63}
64
65#[async_trait]
66impl UserProvider for StaticUserProvider {
67 fn name(&self) -> &str {
68 STATIC_USER_PROVIDER
69 }
70
71 async fn authenticate(&self, id: Identity<'_>, pwd: Password<'_>) -> Result<UserInfoRef> {
72 authenticate_with_credential(&self.users, id, pwd)
73 }
74
75 async fn postgres_auth_info(&self, id: Identity<'_>, _catalog: &str) -> Result<PgAuthInfo> {
76 postgres_auth_info_with_credential(&self.users, id)
77 }
78
79 async fn authorize(
80 &self,
81 _catalog: &str,
82 _schema: &str,
83 _user_info: &UserInfoRef,
84 ) -> Result<()> {
85 Ok(())
87 }
88}
89
90#[cfg(test)]
91pub mod test {
92 use std::fs::File;
93 use std::io::{LineWriter, Write};
94
95 use common_test_util::temp_dir::create_temp_dir;
96 use pbkdf2::pbkdf2_hmac;
97 use sha2::Sha256;
98
99 use crate::UserProvider;
100 use crate::user_info::DefaultUserInfo;
101 use crate::user_provider::static_user_provider::StaticUserProvider;
102 use crate::user_provider::{Identity, Password};
103
104 async fn test_authenticate(provider: &dyn UserProvider, username: &str, password: &str) {
105 let re = provider
106 .authenticate(
107 Identity::UserId(username, None),
108 Password::PlainText(password.to_string().into()),
109 )
110 .await;
111 let _ = re.unwrap();
112 }
113
114 async fn test_authenticate_fails(provider: &dyn UserProvider, username: &str, password: &str) {
115 let re = provider
116 .authenticate(
117 Identity::UserId(username, None),
118 Password::PlainText(password.to_string().into()),
119 )
120 .await;
121 assert!(re.is_err());
122 }
123
124 fn pbkdf2_sha256_verifier(password: &str) -> String {
125 let iterations = 4096;
126 let salt = b"salt";
127 let mut hash = [0u8; 32];
128 pbkdf2_hmac::<Sha256>(password.as_bytes(), salt, iterations, &mut hash);
129 format!(
130 "pbkdf2_sha256:{iterations}:{}:{}",
131 hex::encode(salt),
132 hex::encode(hash)
133 )
134 }
135
136 #[tokio::test]
137 async fn test_authorize() {
138 let user_info = DefaultUserInfo::with_name("root");
139 let provider = StaticUserProvider::new("cmd:root=123456,admin=654321").unwrap();
140 provider
141 .authorize("catalog", "schema", &user_info)
142 .await
143 .unwrap();
144 }
145
146 #[tokio::test]
147 async fn test_inline_provider() {
148 let provider = StaticUserProvider::new("cmd:root=123456,admin=654321").unwrap();
149 test_authenticate(&provider, "root", "123456").await;
150 test_authenticate(&provider, "admin", "654321").await;
151
152 assert!(StaticUserProvider::new("cmd:user:readonyl=password").is_err());
153 }
154
155 #[tokio::test]
156 async fn test_inline_provider_with_pbkdf2_sha256_verifier() {
157 let provider =
158 StaticUserProvider::new(&format!("cmd:root={}", pbkdf2_sha256_verifier("123456")))
159 .unwrap();
160
161 test_authenticate(&provider, "root", "123456").await;
162 test_authenticate_fails(&provider, "root", "654321").await;
163 }
164
165 #[tokio::test]
166 async fn test_file_provider() {
167 let dir = create_temp_dir("test_file_provider");
168 let file_path = format!("{}/test_file_provider", dir.path().to_str().unwrap());
169 {
170 let file = File::create(&file_path);
172 let file = file.unwrap();
173 let mut lw = LineWriter::new(file);
174 assert!(
175 lw.write_all(
176 b"root=123456
177invalid:readonyl=password
178admin=654321",
179 )
180 .is_ok()
181 );
182 lw.flush().unwrap();
183 }
184
185 let param = format!("file:{file_path}");
186 let provider = StaticUserProvider::new(param.as_str()).unwrap();
187 test_authenticate(&provider, "root", "123456").await;
188 test_authenticate(&provider, "admin", "654321").await;
189 test_authenticate_fails(&provider, "invalid", "password").await;
190 }
191}