panel — open-source game server manager (public release)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
// reset-admin resets a panel user's password.
|
||||
//
|
||||
// Two modes:
|
||||
//
|
||||
// reset-admin --email admin@panel.local --password NEWPW [--db-url postgres://...]
|
||||
// Connects to Postgres (flag, or $PANEL_DB_URL, or the controller's
|
||||
// default dev DSN) and writes a fresh argon2id hash for that user.
|
||||
//
|
||||
// reset-admin <password>
|
||||
// Legacy: prints the argon2id hash to stdout for a manual
|
||||
// `UPDATE users SET password_hash = '...'`.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"golang.org/x/crypto/argon2"
|
||||
)
|
||||
|
||||
// Must match controller/cmd/controller/auth.go hashPassword parameters.
|
||||
const (
|
||||
argonTime = 3
|
||||
argonMemory = 64 * 1024
|
||||
argonThreads = 2
|
||||
argonKeyLen = 32
|
||||
argonSaltLen = 16
|
||||
|
||||
defaultDBURL = "postgres://panel:panel_dev@127.0.0.1:5432/panel?sslmode=disable"
|
||||
)
|
||||
|
||||
func hash(password string) (string, error) {
|
||||
salt := make([]byte, argonSaltLen)
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
return "", err
|
||||
}
|
||||
h := argon2.IDKey([]byte(password), salt, argonTime, argonMemory, argonThreads, argonKeyLen)
|
||||
return fmt.Sprintf("$argon2id$v=19$m=%d,t=%d,p=%d$%s$%s",
|
||||
argonMemory, argonTime, argonThreads,
|
||||
base64.RawStdEncoding.EncodeToString(salt),
|
||||
base64.RawStdEncoding.EncodeToString(h),
|
||||
), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
email := flag.String("email", "", "user email to reset (writes the DB; e.g. admin@panel.local)")
|
||||
password := flag.String("password", "", "new password (used with --email)")
|
||||
dbURL := flag.String("db-url", "", "Postgres connection string (default: $PANEL_DB_URL, else the controller's dev default)")
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintln(os.Stderr, `usage:
|
||||
reset-admin --email EMAIL --password NEWPW [--db-url postgres://...]
|
||||
reset-admin <password> (hash-only: prints argon2id hash for manual UPDATE)`)
|
||||
}
|
||||
flag.Parse()
|
||||
|
||||
// Legacy hash-only mode: one positional arg, no --email.
|
||||
if *email == "" {
|
||||
if flag.NArg() != 1 {
|
||||
flag.Usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
h, err := hash(flag.Arg(0))
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(h)
|
||||
return
|
||||
}
|
||||
|
||||
if *password == "" {
|
||||
fmt.Fprintln(os.Stderr, "reset-admin: --password is required with --email")
|
||||
os.Exit(2)
|
||||
}
|
||||
dsn := *dbURL
|
||||
if dsn == "" {
|
||||
dsn = os.Getenv("PANEL_DB_URL")
|
||||
}
|
||||
if dsn == "" {
|
||||
dsn = defaultDBURL
|
||||
}
|
||||
|
||||
h, err := hash(*password)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
conn, err := pgx.Connect(ctx, dsn)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "reset-admin: connect: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer conn.Close(ctx)
|
||||
|
||||
tag, err := conn.Exec(ctx,
|
||||
`UPDATE users SET password_hash = $1, updated_at = NOW() WHERE email = $2`,
|
||||
h, *email)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "reset-admin: update: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if tag.RowsAffected() == 0 {
|
||||
fmt.Fprintf(os.Stderr, "reset-admin: no user with email %q\n", *email)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("reset-admin: password updated for %s\n", *email)
|
||||
}
|
||||
Reference in New Issue
Block a user