faucet_auth/
static_provider.rs1use async_trait::async_trait;
4use faucet_core::{AuthProvider, Credential, FaucetError};
5use serde_json::Value;
6
7#[derive(Debug, Clone)]
11pub struct StaticProvider {
12 credential: Credential,
13}
14
15impl StaticProvider {
16 pub fn bearer(token: impl Into<String>) -> Self {
18 Self {
19 credential: Credential::Bearer(token.into()),
20 }
21 }
22
23 pub fn new(credential: Credential) -> Self {
25 Self { credential }
26 }
27
28 pub fn from_config(config: &Value) -> Result<Self, FaucetError> {
31 if let Some(token) = config.get("token").and_then(Value::as_str) {
32 return Ok(Self::bearer(token));
33 }
34 if let (Some(name), Some(value)) = (
35 config.get("header").and_then(Value::as_str),
36 config.get("value").and_then(Value::as_str),
37 ) {
38 return Ok(Self::new(Credential::Header {
39 name: name.to_string(),
40 value: value.to_string(),
41 }));
42 }
43 if let (Some(username), Some(password)) = (
44 config.get("username").and_then(Value::as_str),
45 config.get("password").and_then(Value::as_str),
46 ) {
47 return Ok(Self::new(Credential::Basic {
48 username: username.to_string(),
49 password: password.to_string(),
50 }));
51 }
52 Err(FaucetError::Config(
53 "static auth provider: config must contain `token`, `header`+`value`, or `username`+`password`".into(),
54 ))
55 }
56}
57
58#[async_trait]
59impl AuthProvider for StaticProvider {
60 async fn credential(&self) -> Result<Credential, FaucetError> {
61 Ok(self.credential.clone())
62 }
63
64 fn provider_name(&self) -> &'static str {
65 "static"
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[tokio::test]
74 async fn bearer_round_trips() {
75 let p = StaticProvider::bearer("t");
76 assert_eq!(
77 p.credential().await.unwrap(),
78 Credential::Bearer("t".into())
79 );
80 }
81
82 #[tokio::test]
83 async fn from_config_bearer() {
84 let p = StaticProvider::from_config(&serde_json::json!({"token": "abc"})).unwrap();
85 assert_eq!(
86 p.credential().await.unwrap(),
87 Credential::Bearer("abc".into())
88 );
89 }
90
91 #[tokio::test]
92 async fn from_config_header() {
93 let p =
94 StaticProvider::from_config(&serde_json::json!({"header": "X-Api-Key", "value": "k"}))
95 .unwrap();
96 assert_eq!(
97 p.credential().await.unwrap(),
98 Credential::Header {
99 name: "X-Api-Key".into(),
100 value: "k".into()
101 }
102 );
103 }
104
105 #[test]
106 fn from_config_empty_errors() {
107 assert!(StaticProvider::from_config(&serde_json::json!({})).is_err());
108 }
109
110 #[test]
111 fn debug_does_not_leak_token() {
112 let p = StaticProvider::bearer("supersecretstatic");
115 let s = format!("{p:?}");
116 assert!(!s.contains("supersecretstatic"), "static token leaked: {s}");
117 }
118}