use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "type", content = "config", rename_all = "snake_case")]
pub enum NatsAuth {
#[default]
None,
Token {
token: String,
},
UserPassword {
username: String,
password: String,
},
NKey {
nkey: String,
},
CredsFile {
path: PathBuf,
},
}
impl std::fmt::Debug for NatsAuth {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
NatsAuth::None => f.write_str("None"),
NatsAuth::Token { .. } => f
.debug_struct("Token")
.field("token", &"<redacted>")
.finish(),
NatsAuth::UserPassword { username, .. } => f
.debug_struct("UserPassword")
.field("username", username)
.field("password", &"<redacted>")
.finish(),
NatsAuth::NKey { .. } => f.debug_struct("NKey").field("nkey", &"<redacted>").finish(),
NatsAuth::CredsFile { path } => {
f.debug_struct("CredsFile").field("path", path).finish()
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn default_is_none() {
assert!(matches!(NatsAuth::default(), NatsAuth::None));
}
#[test]
fn serde_round_trip_token() {
let auth = NatsAuth::Token {
token: "sekret".into(),
};
let v = serde_json::to_value(&auth).unwrap();
assert_eq!(v["type"], "token");
assert_eq!(v["config"]["token"], "sekret");
let parsed: NatsAuth = serde_json::from_value(v).unwrap();
assert!(matches!(parsed, NatsAuth::Token { token } if token == "sekret"));
}
#[test]
fn serde_round_trip_user_password() {
let v = json!({"type": "user_password", "config": {"username": "u", "password": "p"}});
let parsed: NatsAuth = serde_json::from_value(v).unwrap();
assert!(
matches!(parsed, NatsAuth::UserPassword { username, password } if username == "u" && password == "p")
);
}
#[test]
fn serde_round_trip_creds_file() {
let v = json!({"type": "creds_file", "config": {"path": "/tmp/x.creds"}});
let parsed: NatsAuth = serde_json::from_value(v).unwrap();
assert!(
matches!(parsed, NatsAuth::CredsFile { path } if path == std::path::Path::new("/tmp/x.creds"))
);
}
#[test]
fn debug_redacts_token() {
let auth = NatsAuth::Token {
token: "super-secret-token".into(),
};
let dbg = format!("{auth:?}");
assert!(
!dbg.contains("super-secret-token"),
"debug leaked token: {dbg}"
);
assert!(dbg.contains("redacted"));
}
#[test]
fn debug_redacts_password_but_shows_username() {
let auth = NatsAuth::UserPassword {
username: "alice".into(),
password: "hunter2".into(),
};
let dbg = format!("{auth:?}");
assert!(dbg.contains("alice"));
assert!(!dbg.contains("hunter2"), "debug leaked password: {dbg}");
}
#[test]
fn debug_redacts_nkey() {
let auth = NatsAuth::NKey {
nkey: "SUACSSL3UAHUDXKFSNVUZRF5UHPMWZ6BFDTJ7M6USDXIEDNPPQYYYCU3VY".into(),
};
let dbg = format!("{auth:?}");
assert!(!dbg.contains("SUACSSL3"), "debug leaked nkey: {dbg}");
}
#[test]
fn schema_compiles() {
let _ = schemars::schema_for!(NatsAuth);
}
}