Add a webhooker resetpw subcommand and a bootstrap banner (closes #208) (#239)
All checks were successful
check / check (push) Successful in 3m25s
All checks were successful
check / check (push) Successful in 3m25s
The admin bootstrap password was printed once, as one line among roughly 45 fx lines, and under docker run -d went to container logs subject to rotation. There was no reset path at all -- no subcommand, no forgot-password flow, no env override -- so recovery meant hand-deleting the users row from webhooker.db, which was documented nowhere. Adds webhooker resetpw [-generate] <username>. The password is read from stdin or generated with the existing crypto/rand helper, never taken from argv where /proc would publish it. It reuses the existing Argon2id hashing rather than reimplementing the parameters, and writes a single UPDATE only after the hash is complete, so no failure can leave an account with no usable password. An unknown username is a hard error and never creates an account. It refuses to run against a DATA_DIR held by a live instance, via the exclusive lock from #201. DATA_DIR and webhooker.db are checked to exist before the lock is acquired, so a mistyped path creates nothing -- neither a directory tree nor a stray lock file. The bootstrap password now appears exactly once, in a distinct banner written straight to a caller-named writer rather than as an fx log line.
This commit was merged in pull request #239.
This commit is contained in:
@@ -2,6 +2,7 @@ package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"time"
|
||||
@@ -66,6 +67,13 @@ func (r *RetentionReaper) ExportSetInterval(d time.Duration) {
|
||||
r.interval = d
|
||||
}
|
||||
|
||||
// ExportSetBannerOut redirects the first-boot credentials banner, so a
|
||||
// test can read what the operator would have seen. It must be called
|
||||
// before the fx start hook runs, which is where the account is seeded.
|
||||
func (d *Database) ExportSetBannerOut(w io.Writer) {
|
||||
d.bannerOut = w
|
||||
}
|
||||
|
||||
// DummyPasswordHashForTest exposes the encoded hash that unknown
|
||||
// usernames are verified against.
|
||||
func DummyPasswordHashForTest() string {
|
||||
|
||||
Reference in New Issue
Block a user