From 3ca0f7ca218cc67862d5d9bc61c5f3bdc5f7e108 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Sat, 15 Feb 2025 14:06:48 +0100 Subject: [PATCH] feat: password struct --- src/api/config.rs | 52 ++++++++++++++++++++++++++----------------- src/client.rs | 2 +- src/ffi/python/mod.rs | 2 +- src/tests/fixtures.rs | 2 +- 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/api/config.rs b/src/api/config.rs index 8cfea00c..4ae2a237 100644 --- a/src/api/config.rs +++ b/src/api/config.rs @@ -1,6 +1,10 @@ //! # Config //! Data structure defining clients configuration +use std::fmt::{Debug, Display}; + +use serde::Serialize; + /// Configuration struct for the `codemp` client. /// /// `username` and `password` are required fields, everything else is optional. @@ -8,7 +12,7 @@ /// `host`, `port` and `tls` affect all connections to all gRPC services; the /// resulting endpoint is composed like this: /// http{tls?'s':''}://{host}:{port} -#[derive(Clone, Default)] +#[derive(Clone, Debug, Default)] #[cfg_attr(feature = "js", napi_derive::napi(object))] #[cfg_attr(feature = "py", pyo3::pyclass(get_all, set_all))] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] @@ -16,7 +20,7 @@ pub struct Config { /// User identifier used to register, possibly your email. pub username: String, /// User password chosen upon registration. - pub password: String, // must not leak this! + pub password: Password, // must not leak this! /// Address of server to connect to, default api.code.mp. pub host: Option, /// Port to connect to, default 50053. @@ -30,7 +34,7 @@ impl Config { pub fn new(username: impl ToString, password: impl ToString) -> Self { Self { username: username.to_string(), - password: password.to_string(), + password: password.to_string().into(), host: None, port: None, tls: None, @@ -62,24 +66,30 @@ impl Config { } } -// manual impl: we want to obfuscate the password field!! -// TODO: can we just tag password to be obfuscated in debug print? -// reimplementing the whole Debug thing is pretty lame -impl std::fmt::Debug for Config { +#[derive(Clone, Default)] +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize), serde(transparent))] +pub struct Password(String); + +impl From for Password { + fn from(value: String) -> Self { + Password(value) + } +} + +impl From for String { + fn from(value: Password) -> Self { + value.0 + } +} + +impl Display for Password { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if f.alternate() { - write!(f, -r#"""Config {{ - username: {}, - password: ********, - host: {:#?}, - port: {:#?}, - tls: {:#?} -}}"""#, - self.username, self.host, self.port, self.tls - ) - } else { - write!(f, "Config {{ username: {}, password: ********, host: {:?}, port: {:?}, tls: {:?} }}", self.username, self.host, self.port, self.tls) - } + write!(f, "********") + } +} + +impl Debug for Password { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "********") } } diff --git a/src/client.rs b/src/client.rs index 0df972c2..dad968f2 100644 --- a/src/client.rs +++ b/src/client.rs @@ -56,7 +56,7 @@ impl Client { let resp = auth .login(LoginRequest { username: config.username.clone(), - password: config.password.clone(), + password: config.password.to_string().clone(), }) .await? .into_inner(); diff --git a/src/ffi/python/mod.rs b/src/ffi/python/mod.rs index 4e6ecd1a..7573c909 100644 --- a/src/ffi/python/mod.rs +++ b/src/ffi/python/mod.rs @@ -202,7 +202,7 @@ impl Config { Ok(Self { username, - password, + password: password.into(), host, port, tls, diff --git a/src/tests/fixtures.rs b/src/tests/fixtures.rs index b52712cb..07ba5a7b 100644 --- a/src/tests/fixtures.rs +++ b/src/tests/fixtures.rs @@ -64,7 +64,7 @@ impl ScopedFixture for ClientFixture { }); let client = crate::Client::connect(crate::api::Config { username, - password, + password: password.into(), tls: Some(false), ..Default::default() })