1use std::fmt;
16
17use cheetah_string::CheetahString;
18use serde::Deserialize;
19use serde::Serialize;
20
21use crate::authentication::acl_signer::SignatureAlgorithm;
22use crate::maintenance::MaintenancePolicyReference;
23use crate::AuthFailureKind;
24use crate::AuthOperation;
25use crate::AuthServiceError;
26use crate::AuthServiceResult;
27
28#[derive(Clone, Serialize, Deserialize)]
29#[serde(default, rename_all = "camelCase")]
30pub struct AuthConfig {
31 pub config_name: CheetahString,
32 pub cluster_name: CheetahString,
33 pub auth_config_path: CheetahString,
34 pub acl_file: CheetahString,
35 pub acl_file_watch_enabled: bool,
36 pub acl_file_watch_interval_millis: u64,
37
38 pub authentication_enabled: bool,
39 pub authentication_provider: CheetahString,
40 pub authentication_metadata_provider: CheetahString,
41 pub authentication_strategy: CheetahString,
42 pub authentication_whitelist: CheetahString,
43 pub init_authentication_user: CheetahString,
44 pub inner_client_authentication_credentials: CheetahString,
45 pub signature_algorithm: SignatureAlgorithm,
46 pub request_timestamp_expired_millis: u64,
50
51 pub authorization_enabled: bool,
52 pub authorization_provider: CheetahString,
53 pub authorization_metadata_provider: CheetahString,
54 pub authorization_strategy: CheetahString,
55 pub authorization_whitelist: CheetahString,
56
57 pub maintenance_enabled: bool,
59 pub maintenance_policy_path: CheetahString,
61 pub maintenance_policy_version: u64,
63 pub maintenance_policy_sha256: CheetahString,
65
66 pub migrate_auth_from_v1_enabled: bool,
67
68 pub user_cache_max_num: u32,
69 pub user_cache_expired_second: u32,
70 pub user_cache_refresh_second: u32,
71
72 pub acl_cache_max_num: u32,
73 pub acl_cache_expired_second: u32,
74 pub acl_cache_refresh_second: u32,
77
78 pub stateful_authentication_cache_max_num: u32,
79 pub stateful_authentication_cache_expired_second: u32,
80 pub stateful_authorization_cache_max_num: u32,
81 pub stateful_authorization_cache_expired_second: u32,
82 pub stateful_authorization_cache_negative_enable: bool,
83}
84
85impl fmt::Debug for AuthConfig {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 f.debug_struct("AuthConfig")
88 .field("config_name", &self.config_name)
89 .field("cluster_name", &self.cluster_name)
90 .field("auth_config_path", &self.auth_config_path)
91 .field("acl_file", &self.acl_file)
92 .field("acl_file_watch_enabled", &self.acl_file_watch_enabled)
93 .field("acl_file_watch_interval_millis", &self.acl_file_watch_interval_millis)
94 .field("authentication_enabled", &self.authentication_enabled)
95 .field("authentication_provider", &self.authentication_provider)
96 .field(
97 "authentication_metadata_provider",
98 &self.authentication_metadata_provider,
99 )
100 .field("authentication_strategy", &self.authentication_strategy)
101 .field("authentication_whitelist", &self.authentication_whitelist)
102 .field(
103 "init_authentication_user",
104 &redacted_if_present(&self.init_authentication_user),
105 )
106 .field(
107 "inner_client_authentication_credentials",
108 &redacted_if_present(&self.inner_client_authentication_credentials),
109 )
110 .field("signature_algorithm", &self.signature_algorithm)
111 .field(
112 "request_timestamp_expired_millis",
113 &self.request_timestamp_expired_millis,
114 )
115 .field("authorization_enabled", &self.authorization_enabled)
116 .field("authorization_provider", &self.authorization_provider)
117 .field("authorization_metadata_provider", &self.authorization_metadata_provider)
118 .field("authorization_strategy", &self.authorization_strategy)
119 .field("authorization_whitelist", &self.authorization_whitelist)
120 .field("maintenance_enabled", &self.maintenance_enabled)
121 .field("maintenance_policy_path", &self.maintenance_policy_path)
122 .field("maintenance_policy_version", &self.maintenance_policy_version)
123 .field("maintenance_policy_sha256", &self.maintenance_policy_sha256)
124 .field("migrate_auth_from_v1_enabled", &self.migrate_auth_from_v1_enabled)
125 .field("user_cache_max_num", &self.user_cache_max_num)
126 .field("user_cache_expired_second", &self.user_cache_expired_second)
127 .field("user_cache_refresh_second", &self.user_cache_refresh_second)
128 .field("acl_cache_max_num", &self.acl_cache_max_num)
129 .field("acl_cache_expired_second", &self.acl_cache_expired_second)
130 .field("acl_cache_refresh_second", &self.acl_cache_refresh_second)
131 .field(
132 "stateful_authentication_cache_max_num",
133 &self.stateful_authentication_cache_max_num,
134 )
135 .field(
136 "stateful_authentication_cache_expired_second",
137 &self.stateful_authentication_cache_expired_second,
138 )
139 .field(
140 "stateful_authorization_cache_max_num",
141 &self.stateful_authorization_cache_max_num,
142 )
143 .field(
144 "stateful_authorization_cache_expired_second",
145 &self.stateful_authorization_cache_expired_second,
146 )
147 .field(
148 "stateful_authorization_cache_negative_enable",
149 &self.stateful_authorization_cache_negative_enable,
150 )
151 .finish()
152 }
153}
154
155fn redacted_if_present(value: &CheetahString) -> Option<&'static str> {
156 if value.is_empty() {
157 None
158 } else {
159 Some("<redacted>")
160 }
161}
162
163impl Default for AuthConfig {
164 fn default() -> Self {
165 Self {
166 config_name: CheetahString::new(),
167 cluster_name: CheetahString::new(),
168 auth_config_path: CheetahString::new(),
169 acl_file: CheetahString::new(),
170 acl_file_watch_enabled: false,
171 acl_file_watch_interval_millis: 5_000,
172
173 authentication_enabled: false,
174 authentication_provider: CheetahString::new(),
175 authentication_metadata_provider: CheetahString::new(),
176 authentication_strategy: CheetahString::new(),
177 authentication_whitelist: CheetahString::new(),
178 init_authentication_user: CheetahString::new(),
179 inner_client_authentication_credentials: CheetahString::new(),
180 signature_algorithm: SignatureAlgorithm::default(),
181 request_timestamp_expired_millis: 0,
182
183 authorization_enabled: false,
184 authorization_provider: CheetahString::new(),
185 authorization_metadata_provider: CheetahString::new(),
186 authorization_strategy: CheetahString::new(),
187 authorization_whitelist: CheetahString::new(),
188
189 maintenance_enabled: false,
190 maintenance_policy_path: CheetahString::new(),
191 maintenance_policy_version: 0,
192 maintenance_policy_sha256: CheetahString::new(),
193
194 migrate_auth_from_v1_enabled: false,
195
196 user_cache_max_num: 1000,
197 user_cache_expired_second: 600,
198 user_cache_refresh_second: 60,
199
200 acl_cache_max_num: 1000,
201 acl_cache_expired_second: 600,
202 acl_cache_refresh_second: 60,
203
204 stateful_authentication_cache_max_num: 10000,
205 stateful_authentication_cache_expired_second: 60,
206 stateful_authorization_cache_max_num: 10000,
207 stateful_authorization_cache_expired_second: 60,
208 stateful_authorization_cache_negative_enable: false,
209 }
210 }
211}
212
213impl AuthConfig {
214 pub fn maintenance_policy_reference(&self) -> AuthServiceResult<Option<MaintenancePolicyReference>> {
222 if !self.maintenance_enabled {
223 return Ok(None);
224 }
225 if !self.authentication_enabled || !self.authorization_enabled {
226 return Err(AuthServiceError::new(
227 AuthOperation::MaintainService,
228 AuthFailureKind::InvalidConfiguration,
229 ));
230 }
231 if self.maintenance_policy_path.trim().is_empty()
232 || self.maintenance_policy_version == 0
233 || self.maintenance_policy_sha256.trim().is_empty()
234 {
235 return Err(AuthServiceError::new(
236 AuthOperation::MaintainService,
237 AuthFailureKind::InvalidConfiguration,
238 ));
239 }
240 Ok(Some(MaintenancePolicyReference {
241 path: self.maintenance_policy_path.as_str().into(),
242 version: self.maintenance_policy_version,
243 sha256: self.maintenance_policy_sha256.to_string(),
244 }))
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251
252 #[test]
253 fn test_auth_config_default_values() {
254 let config = AuthConfig::default();
255
256 assert!(config.config_name.is_empty());
258 assert!(config.cluster_name.is_empty());
259 assert!(config.auth_config_path.is_empty());
260 assert!(config.acl_file.is_empty());
261
262 assert!(config.authentication_provider.is_empty());
263 assert!(config.authentication_metadata_provider.is_empty());
264 assert!(config.authentication_strategy.is_empty());
265 assert!(config.authentication_whitelist.is_empty());
266 assert!(config.init_authentication_user.is_empty());
267 assert!(config.inner_client_authentication_credentials.is_empty());
268 assert_eq!(config.signature_algorithm, SignatureAlgorithm::HmacSha1);
269 assert_eq!(config.request_timestamp_expired_millis, 0);
270
271 assert!(config.authorization_provider.is_empty());
272 assert!(config.authorization_metadata_provider.is_empty());
273 assert!(config.authorization_strategy.is_empty());
274 assert!(config.authorization_whitelist.is_empty());
275
276 assert!(!config.authentication_enabled);
278 assert!(!config.authorization_enabled);
279 assert!(!config.maintenance_enabled);
280 assert!(config.maintenance_policy_path.is_empty());
281 assert_eq!(config.maintenance_policy_version, 0);
282 assert!(config.maintenance_policy_sha256.is_empty());
283 assert!(!config.acl_file_watch_enabled);
284 assert!(!config.migrate_auth_from_v1_enabled);
285 assert_eq!(config.acl_file_watch_interval_millis, 5_000);
286
287 assert_eq!(config.user_cache_max_num, 1000);
289 assert_eq!(config.user_cache_expired_second, 600);
290 assert_eq!(config.user_cache_refresh_second, 60);
291
292 assert_eq!(config.acl_cache_max_num, 1000);
293 assert_eq!(config.acl_cache_expired_second, 600);
294 assert_eq!(config.acl_cache_refresh_second, 60);
295
296 assert_eq!(config.stateful_authentication_cache_max_num, 10000);
297 assert_eq!(config.stateful_authentication_cache_expired_second, 60);
298 assert_eq!(config.stateful_authorization_cache_max_num, 10000);
299 assert_eq!(config.stateful_authorization_cache_expired_second, 60);
300 assert!(!config.stateful_authorization_cache_negative_enable);
301 }
302
303 #[test]
304 fn auth_config_deserializes_acl_file_fields_from_camel_case() {
305 let config: AuthConfig = serde_yaml::from_str(
306 r#"
307aclFile: conf/plain_acl.yml
308aclFileWatchEnabled: true
309aclFileWatchIntervalMillis: 250
310signatureAlgorithm: HmacSHA256
311requestTimestampExpiredMillis: 300000
312statefulAuthorizationCacheNegativeEnable: true
313"#,
314 )
315 .unwrap();
316
317 assert_eq!(config.acl_file.as_str(), "conf/plain_acl.yml");
318 assert!(config.acl_file_watch_enabled);
319 assert_eq!(config.acl_file_watch_interval_millis, 250);
320 assert_eq!(config.signature_algorithm, SignatureAlgorithm::HmacSha256);
321 assert_eq!(config.request_timestamp_expired_millis, 300_000);
322 assert!(config.stateful_authorization_cache_negative_enable);
323 }
324
325 #[test]
326 fn auth_config_debug_redacts_embedded_credentials() {
327 let config = AuthConfig {
328 init_authentication_user: CheetahString::from("admin:init-secret"),
329 inner_client_authentication_credentials: CheetahString::from(
330 r#"{"accessKey":"inner","secretKey":"inner-secret"}"#,
331 ),
332 ..AuthConfig::default()
333 };
334
335 let debug = format!("{config:?}");
336
337 assert!(debug.contains("<redacted>"));
338 assert!(!debug.contains("init-secret"));
339 assert!(!debug.contains("inner-secret"));
340 }
341
342 #[test]
343 fn maintenance_reference_requires_auth_and_complete_pin() {
344 let disabled = AuthConfig::default();
345 assert!(disabled
346 .maintenance_policy_reference()
347 .expect("disabled maintenance has no reference")
348 .is_none());
349
350 let incomplete = AuthConfig {
351 maintenance_enabled: true,
352 ..AuthConfig::default()
353 };
354 assert!(incomplete.maintenance_policy_reference().is_err());
355
356 let complete = AuthConfig {
357 authentication_enabled: true,
358 authorization_enabled: true,
359 maintenance_enabled: true,
360 maintenance_policy_path: "maintenance-policy.json".into(),
361 maintenance_policy_version: 7,
362 maintenance_policy_sha256: "a".repeat(64).into(),
363 ..AuthConfig::default()
364 };
365 assert_eq!(
366 complete
367 .maintenance_policy_reference()
368 .expect("complete reference")
369 .expect("enabled maintenance reference")
370 .version,
371 7
372 );
373 }
374}