diff --git a/README.md b/README.md index 2773137..645ac16 100644 --- a/README.md +++ b/README.md @@ -20,13 +20,13 @@ B{rew}log ships as one executable. You decide whether it acts as a server or a c ### First-time setup -On first start, you must set an admin password via the `BREWLOG_ADMIN_PASSWORD` environment variable: +On first start, you must set an admin username and password via the `BREWLOG_ADMIN_USERNAME` and `BREWLOG_ADMIN_PASSWORD` environment variables: ```bash -BREWLOG_ADMIN_PASSWORD="your-secure-password" brewlog serve +BREWLOG_ADMIN_USERNAME="admin" BREWLOG_ADMIN_PASSWORD="your-secure-password" brewlog serve ``` -This creates the admin user in the database. On subsequent starts, the environment variable is not required. +This creates the admin user in the database. On subsequent starts, the environment variables are not required. ### Authentication diff --git a/src/application/server.rs b/src/application/server.rs index 8777c71..0f71a53 100644 --- a/src/application/server.rs +++ b/src/application/server.rs @@ -26,6 +26,7 @@ pub struct ServerConfig { pub bind_address: SocketAddr, pub database_url: String, pub admin_password: Option, + pub admin_username: Option, } #[derive(Clone)] @@ -75,7 +76,7 @@ pub async fn serve(config: ServerConfig) -> anyhow::Result<()> { Arc::new(SqlSessionRepository::new(database.clone_pool())); // Bootstrap admin user if no users exist - bootstrap_admin_user(&user_repo, config.admin_password).await?; + bootstrap_admin_user(&user_repo, config.admin_username, config.admin_password).await?; let state = AppState::new( roaster_repo, @@ -106,6 +107,7 @@ pub async fn serve(config: ServerConfig) -> anyhow::Result<()> { async fn bootstrap_admin_user( user_repo: &Arc, + admin_username: Option, admin_password: Option, ) -> anyhow::Result<()> { // Check if any users exist @@ -120,6 +122,13 @@ async fn bootstrap_admin_user( } // No users exist - we need to create the admin user + let username = admin_username.ok_or_else(|| { + anyhow::anyhow!( + "No users exist in the database. Please provide BREWLOG_ADMIN_USERNAME \ + environment variable to create the admin user." + ) + })?; + let password = admin_password.ok_or_else(|| { anyhow::anyhow!( "No users exist in the database. Please provide BREWLOG_ADMIN_PASSWORD \ @@ -127,11 +136,11 @@ async fn bootstrap_admin_user( ) })?; - info!("No users found. Creating admin user..."); + info!("No users found. Creating admin user '{}'...", username); let password_hash = hash_password(&password).context("failed to hash admin password")?; - let admin_user = NewUser::new("admin".to_string(), password_hash); + let admin_user = NewUser::new(username, password_hash); user_repo .insert(admin_user) diff --git a/src/main.rs b/src/main.rs index f1a1b64..5a193b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,6 +50,7 @@ async fn run_server(command: ServeCommand) -> Result<()> { bind_address: command.bind_address, database_url: command.database_url, admin_password: command.admin_password, + admin_username: command.admin_username, }; serve(config).await diff --git a/src/presentation/cli/mod.rs b/src/presentation/cli/mod.rs index c8a93ef..82d89ba 100644 --- a/src/presentation/cli/mod.rs +++ b/src/presentation/cli/mod.rs @@ -74,6 +74,9 @@ pub struct ServeCommand { #[arg(long, env = "BREWLOG_ADMIN_PASSWORD")] pub admin_password: Option, + + #[arg(long, env = "BREWLOG_ADMIN_USERNAME")] + pub admin_username: Option, } pub(crate) fn print_json(value: &T) -> anyhow::Result<()> diff --git a/tests/cli/helpers.rs b/tests/cli/helpers.rs index 15e32cf..d229b37 100644 --- a/tests/cli/helpers.rs +++ b/tests/cli/helpers.rs @@ -58,6 +58,7 @@ fn ensure_server_started() -> Result<(String, String), String> { &db_url, ]) .env("BREWLOG_ADMIN_PASSWORD", admin_password) + .env("BREWLOG_ADMIN_USERNAME", "admin") .env("RUST_LOG", "error") .stdout(Stdio::null()) .stderr(Stdio::null())