diff --git a/Cargo.lock b/Cargo.lock index c1a11b8..b7ffd32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1480,6 +1480,16 @@ dependencies = [ "untrusted", ] +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "serde", + "zeroize", +] + [[package]] name = "security-framework" version = "2.4.2" @@ -2396,6 +2406,7 @@ dependencies = [ "config", "once_cell", "reqwest", + "secrecy", "serde", "sqlx", "tokio", @@ -2407,6 +2418,12 @@ dependencies = [ "uuid", ] +[[package]] +name = "zeroize" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d68d9dcec5f9b43a30d38c49f91dfedfaac384cb8f085faca366c26207dd1619" + [[package]] name = "zstd" version = "0.9.1+zstd.1.5.1" diff --git a/Cargo.toml b/Cargo.toml index ebc4c2c..f3e133e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ tracing-subscriber = { version = "0.3", features = ["registry", "env-filter"] } tracing-bunyan-formatter = "0.3.1" tracing-log = "0.1.1" tracing-actix-web = "0.5.0-beta.6" +secrecy = { version = "0.8", features = ["serde"] } [dev-dependencies] reqwest = { version = "0.11", features = ["json"] } diff --git a/src/configuration.rs b/src/configuration.rs index 3ad6da7..915da12 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -1,3 +1,5 @@ +use secrecy::{ExposeSecret, Secret}; + #[derive(serde::Deserialize)] pub struct Settings { pub database: DatabaseSettings, @@ -7,25 +9,32 @@ pub struct Settings { #[derive(serde::Deserialize)] pub struct DatabaseSettings { pub username: String, - pub password: String, + pub password: Secret, pub port: u16, pub host: String, pub database_name: String, } impl DatabaseSettings { - pub fn connection_string(&self) -> String { - format!( + pub fn connection_string(&self) -> Secret { + Secret::new(format!( "postgres://{}:{}@{}:{}/{}", - self.username, self.password, self.host, self.port, self.database_name - ) + self.username, + self.password.expose_secret(), + self.host, + self.port, + self.database_name + )) } - pub fn connection_string_without_db(&self) -> String { - format!( + pub fn connection_string_without_db(&self) -> Secret { + Secret::new(format!( "postgres://{}:{}@{}:{}", - self.username, self.password, self.host, self.port - ) + self.username, + self.password.expose_secret(), + self.host, + self.port + )) } } diff --git a/src/main.rs b/src/main.rs index fa39aa2..a811adc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,18 +1,20 @@ +use secrecy::ExposeSecret; use sqlx::postgres::PgPool; use std::net::TcpListener; use zero2prod::configuration::get_configuration; use zero2prod::startup::run; use zero2prod::telemetry::{get_subscriber, init_subscriber}; -#[actix_web::main] +#[tokio::main] async fn main() -> std::io::Result<()> { let subscriber = get_subscriber("zero2prod".into(), "info".into(), std::io::stdout); init_subscriber(subscriber); let configuration = get_configuration().expect("Failed to read configuration."); - let connection_pool = PgPool::connect(&configuration.database.connection_string()) - .await - .expect("Failed to connect to Postgres."); + let connection_pool = + PgPool::connect(&configuration.database.connection_string().expose_secret()) + .await + .expect("Failed to connect to Postgres."); // Here we choose to bind explicitly to localhost, 127.0.0.1, for security // reasons. This binding may cause issues in some environments. For example, diff --git a/tests/health_check.rs b/tests/health_check.rs index 3506c79..ed3e5d7 100644 --- a/tests/health_check.rs +++ b/tests/health_check.rs @@ -1,4 +1,5 @@ use once_cell::sync::Lazy; +use secrecy::ExposeSecret; use sqlx::{Connection, Executor, PgConnection, PgPool}; use std::net::TcpListener; use uuid::Uuid; @@ -48,16 +49,17 @@ async fn spawn_app() -> TestApp { pub async fn configure_database(config: &DatabaseSettings) -> PgPool { // Create database - let mut connection = PgConnection::connect(&config.connection_string_without_db()) - .await - .expect("Failed to connect to Postgres"); + let mut connection = + PgConnection::connect(&config.connection_string_without_db().expose_secret()) + .await + .expect("Failed to connect to Postgres"); connection .execute(&*format!(r#"CREATE DATABASE "{}";"#, config.database_name)) .await .expect("Failed to create database."); // Migrate database - let connection_pool = PgPool::connect(&config.connection_string()) + let connection_pool = PgPool::connect(&config.connection_string().expose_secret()) .await .expect("Failed to connect to Postgres."); sqlx::migrate!("./migrations") @@ -68,7 +70,7 @@ pub async fn configure_database(config: &DatabaseSettings) -> PgPool { connection_pool } -#[actix_rt::test] +#[tokio::test] async fn health_check_works() { // Arrange let app = spawn_app().await; @@ -87,7 +89,7 @@ async fn health_check_works() { assert_eq!(Some(0), response.content_length()); } -#[actix_rt::test] +#[tokio::test] async fn subscribe_returns_a_200_for_valid_form_data() { // Arrange let app = spawn_app().await; @@ -115,7 +117,7 @@ async fn subscribe_returns_a_200_for_valid_form_data() { assert_eq!(saved.name, "le guin"); } -#[actix_rt::test] +#[tokio::test] async fn subscribe_returns_a_400_when_data_is_missing() { // Arrange let app = spawn_app().await;