faucet_common_nats/
auth.rs1use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5use std::path::PathBuf;
6
7#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
16#[serde(tag = "type", content = "config", rename_all = "snake_case")]
17pub enum NatsAuth {
18 #[default]
20 None,
21 Token {
23 token: String,
25 },
26 UserPassword {
28 username: String,
30 password: String,
32 },
33 NKey {
35 nkey: String,
37 },
38 CredsFile {
41 path: PathBuf,
43 },
44}
45
46impl std::fmt::Debug for NatsAuth {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 match self {
50 NatsAuth::None => f.write_str("None"),
51 NatsAuth::Token { .. } => f
52 .debug_struct("Token")
53 .field("token", &"<redacted>")
54 .finish(),
55 NatsAuth::UserPassword { username, .. } => f
56 .debug_struct("UserPassword")
57 .field("username", username)
58 .field("password", &"<redacted>")
59 .finish(),
60 NatsAuth::NKey { .. } => f.debug_struct("NKey").field("nkey", &"<redacted>").finish(),
61 NatsAuth::CredsFile { path } => {
62 f.debug_struct("CredsFile").field("path", path).finish()
63 }
64 }
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71 use serde_json::json;
72
73 #[test]
74 fn default_is_none() {
75 assert!(matches!(NatsAuth::default(), NatsAuth::None));
76 }
77
78 #[test]
79 fn serde_round_trip_token() {
80 let auth = NatsAuth::Token {
81 token: "sekret".into(),
82 };
83 let v = serde_json::to_value(&auth).unwrap();
84 assert_eq!(v["type"], "token");
85 assert_eq!(v["config"]["token"], "sekret");
86 let parsed: NatsAuth = serde_json::from_value(v).unwrap();
87 assert!(matches!(parsed, NatsAuth::Token { token } if token == "sekret"));
88 }
89
90 #[test]
91 fn serde_round_trip_user_password() {
92 let v = json!({"type": "user_password", "config": {"username": "u", "password": "p"}});
93 let parsed: NatsAuth = serde_json::from_value(v).unwrap();
94 assert!(
95 matches!(parsed, NatsAuth::UserPassword { username, password } if username == "u" && password == "p")
96 );
97 }
98
99 #[test]
100 fn serde_round_trip_creds_file() {
101 let v = json!({"type": "creds_file", "config": {"path": "/tmp/x.creds"}});
102 let parsed: NatsAuth = serde_json::from_value(v).unwrap();
103 assert!(
104 matches!(parsed, NatsAuth::CredsFile { path } if path == std::path::Path::new("/tmp/x.creds"))
105 );
106 }
107
108 #[test]
109 fn debug_redacts_token() {
110 let auth = NatsAuth::Token {
111 token: "super-secret-token".into(),
112 };
113 let dbg = format!("{auth:?}");
114 assert!(
115 !dbg.contains("super-secret-token"),
116 "debug leaked token: {dbg}"
117 );
118 assert!(dbg.contains("redacted"));
119 }
120
121 #[test]
122 fn debug_redacts_password_but_shows_username() {
123 let auth = NatsAuth::UserPassword {
124 username: "alice".into(),
125 password: "hunter2".into(),
126 };
127 let dbg = format!("{auth:?}");
128 assert!(dbg.contains("alice"));
129 assert!(!dbg.contains("hunter2"), "debug leaked password: {dbg}");
130 }
131
132 #[test]
133 fn debug_redacts_nkey() {
134 let auth = NatsAuth::NKey {
135 nkey: "SUACSSL3UAHUDXKFSNVUZRF5UHPMWZ6BFDTJ7M6USDXIEDNPPQYYYCU3VY".into(),
136 };
137 let dbg = format!("{auth:?}");
138 assert!(!dbg.contains("SUACSSL3"), "debug leaked nkey: {dbg}");
139 }
140
141 #[test]
142 fn schema_compiles() {
143 let _ = schemars::schema_for!(NatsAuth);
144 }
145}