feat: HMAC-signed cookies for UI sessions, bcrypt hash for admin bootstrap, simplified NixOS module

This commit is contained in:
didier committed 2026-06-16 13:12:51 -04:00
1 parent 56da3bae61
commit 5be2005f70
3 files changed
+51 -57

No files matched your search

+32 -12
View File
@@ -106,15 +106,10 @@ func (s *sqliteStore) countUsers(ctx context.Context) (int, error) {
return n, err
}
func (s *sqliteStore) createUser(ctx context.Context, username, password string) (*userRow, error) {
h, err := hashPassword(password)
if err != nil {
return nil, fmt.Errorf("hash password: %w", err)
}
func (s *sqliteStore) createUserWithHash(ctx context.Context, username, passwordHash string) (*userRow, error) {
result, err := s.db.ExecContext(ctx,
`INSERT INTO users (username, password_hash) VALUES (?, ?)`,
username, h,
username, passwordHash,
)
if err != nil {
return nil, fmt.Errorf("insert user: %w", err)
@@ -404,14 +399,32 @@ func (s *sqliteStore) bootstrapAdmin(ctx context.Context, logger *zap.Logger) {
}
username := getenvDefault("ADMIN_USERNAME", "admin")
password := os.Getenv("ADMIN_PASSWORD")
if password == "" {
var password string
var passwordHash string
if h := os.Getenv("ADMIN_PASSWORD_HASH"); h != "" {
passwordHash = h
} else if pw := os.Getenv("ADMIN_PASSWORD"); pw != "" {
var err error
passwordHash, err = hashPassword(pw)
if err != nil {
logger.Fatal("failed to hash admin password", zap.Error(err))
}
password = pw
}
if passwordHash == "" {
password = generateRandomPassword()
logger.Warn("ADMIN_PASSWORD not set, generated random password",
var err error
passwordHash, err = hashPassword(password)
if err != nil {
logger.Fatal("failed to hash admin password", zap.Error(err))
}
logger.Warn("no admin password configured, generated random password",
zap.String("password", password))
}
admin, err := s.createUser(ctx, username, password)
admin, err := s.createUserWithHash(ctx, username, passwordHash)
if err != nil {
logger.Fatal("failed to create admin user", zap.Error(err))
}
@@ -643,7 +656,14 @@ func createUserHandler(store *sqliteStore, logger *zap.Logger) http.HandlerFunc
return
}
user, err := store.createUser(r.Context(), body.Username, body.Password)
h, err := hashPassword(body.Password)
if err != nil {
logger.Error("hash password", zap.Error(err))
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "internal error"})
return
}
user, err := store.createUserWithHash(r.Context(), body.Username, h)
if err != nil {
if strings.Contains(err.Error(), "UNIQUE constraint") {
writeJSON(w, http.StatusConflict, map[string]string{"error": "username already exists"})