feat: add BREWLOG_ADMIN_USERNAME to select admin username on first start
This commit is contained in:
parent
0e06d4c5dc
commit
4358fa63dc
5 changed files with 20 additions and 6 deletions
|
|
@ -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
|
### 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
|
```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
|
### Authentication
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ pub struct ServerConfig {
|
||||||
pub bind_address: SocketAddr,
|
pub bind_address: SocketAddr,
|
||||||
pub database_url: String,
|
pub database_url: String,
|
||||||
pub admin_password: Option<String>,
|
pub admin_password: Option<String>,
|
||||||
|
pub admin_username: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
@ -75,7 +76,7 @@ pub async fn serve(config: ServerConfig) -> anyhow::Result<()> {
|
||||||
Arc::new(SqlSessionRepository::new(database.clone_pool()));
|
Arc::new(SqlSessionRepository::new(database.clone_pool()));
|
||||||
|
|
||||||
// Bootstrap admin user if no users exist
|
// 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(
|
let state = AppState::new(
|
||||||
roaster_repo,
|
roaster_repo,
|
||||||
|
|
@ -106,6 +107,7 @@ pub async fn serve(config: ServerConfig) -> anyhow::Result<()> {
|
||||||
|
|
||||||
async fn bootstrap_admin_user(
|
async fn bootstrap_admin_user(
|
||||||
user_repo: &Arc<dyn UserRepository>,
|
user_repo: &Arc<dyn UserRepository>,
|
||||||
|
admin_username: Option<String>,
|
||||||
admin_password: Option<String>,
|
admin_password: Option<String>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
// Check if any users exist
|
// Check if any users exist
|
||||||
|
|
@ -120,6 +122,13 @@ async fn bootstrap_admin_user(
|
||||||
}
|
}
|
||||||
|
|
||||||
// No users exist - we need to create the 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(|| {
|
let password = admin_password.ok_or_else(|| {
|
||||||
anyhow::anyhow!(
|
anyhow::anyhow!(
|
||||||
"No users exist in the database. Please provide BREWLOG_ADMIN_PASSWORD \
|
"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 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
|
user_repo
|
||||||
.insert(admin_user)
|
.insert(admin_user)
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ async fn run_server(command: ServeCommand) -> Result<()> {
|
||||||
bind_address: command.bind_address,
|
bind_address: command.bind_address,
|
||||||
database_url: command.database_url,
|
database_url: command.database_url,
|
||||||
admin_password: command.admin_password,
|
admin_password: command.admin_password,
|
||||||
|
admin_username: command.admin_username,
|
||||||
};
|
};
|
||||||
|
|
||||||
serve(config).await
|
serve(config).await
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,9 @@ pub struct ServeCommand {
|
||||||
|
|
||||||
#[arg(long, env = "BREWLOG_ADMIN_PASSWORD")]
|
#[arg(long, env = "BREWLOG_ADMIN_PASSWORD")]
|
||||||
pub admin_password: Option<String>,
|
pub admin_password: Option<String>,
|
||||||
|
|
||||||
|
#[arg(long, env = "BREWLOG_ADMIN_USERNAME")]
|
||||||
|
pub admin_username: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn print_json<T>(value: &T) -> anyhow::Result<()>
|
pub(crate) fn print_json<T>(value: &T) -> anyhow::Result<()>
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ fn ensure_server_started() -> Result<(String, String), String> {
|
||||||
&db_url,
|
&db_url,
|
||||||
])
|
])
|
||||||
.env("BREWLOG_ADMIN_PASSWORD", admin_password)
|
.env("BREWLOG_ADMIN_PASSWORD", admin_password)
|
||||||
|
.env("BREWLOG_ADMIN_USERNAME", "admin")
|
||||||
.env("RUST_LOG", "error")
|
.env("RUST_LOG", "error")
|
||||||
.stdout(Stdio::null())
|
.stdout(Stdio::null())
|
||||||
.stderr(Stdio::null())
|
.stderr(Stdio::null())
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue