Add login rename and password reset flow

This commit is contained in:
DonVoo 2026-07-13 01:19:47 +02:00
parent b625c95c29
commit e7611b2d49
4 changed files with 362 additions and 28 deletions

View file

@ -4,6 +4,7 @@ import (
"database/sql"
"encoding/json"
"errors"
"fmt"
"os"
"strings"
@ -133,6 +134,13 @@ func ConnectDB() error {
expires_at TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
)`,
`CREATE TABLE IF NOT EXISTS app_password_resets(
token_hash TEXT PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES app_users(id) ON DELETE CASCADE,
expires_at TEXT NOT NULL,
used_at TEXT,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
)`,
}
for _, stmt := range schema {
if _, err := db.Exec(stmt); err != nil {
@ -232,6 +240,44 @@ func SaveAppUser(username, displayName, passwordHash, role string, active bool)
return err
}
func UpdateAppUser(oldUsername, username, displayName, passwordHash, role string, active bool) error {
oldUsername = strings.TrimSpace(oldUsername)
username = strings.TrimSpace(username)
displayName = strings.TrimSpace(displayName)
role = normalizeRole(role)
if oldUsername == "" || oldUsername == username {
return SaveAppUser(username, displayName, passwordHash, role, active)
}
tx, err := DB.Begin()
if err != nil {
return err
}
defer tx.Rollback()
var existing int
err = tx.QueryRow(`SELECT 1 FROM app_users WHERE username=?`, username).Scan(&existing)
if err == nil {
return fmt.Errorf("Login/E-Mail existiert bereits.")
}
if !errors.Is(err, sql.ErrNoRows) {
return err
}
if passwordHash == "" {
_, err = tx.Exec(`UPDATE app_users SET username=?, display_name=?, role=?, active=?, updated_at=CURRENT_TIMESTAMP WHERE username=?`,
username, displayName, role, boolInt(active), oldUsername)
} else {
_, err = tx.Exec(`UPDATE app_users SET username=?, display_name=?, password_hash=?, role=?, active=?, updated_at=CURRENT_TIMESTAMP WHERE username=?`,
username, displayName, passwordHash, role, boolInt(active), oldUsername)
}
if err != nil {
return err
}
_, err = tx.Exec(`DELETE FROM app_sessions WHERE user_id IN (SELECT id FROM app_users WHERE username=?)`, username)
if err != nil {
return err
}
return tx.Commit()
}
func DeleteAppUser(username string) error {
_, err := DB.Exec(`DELETE FROM app_users WHERE username=?`, username)
return err
@ -259,6 +305,46 @@ func CleanupSessions() error {
return err
}
func CreatePasswordReset(tokenHash string, userID int64, expiresAt string) error {
_, err := DB.Exec(`INSERT INTO app_password_resets(token_hash, user_id, expires_at) VALUES(?,?,?)`, tokenHash, userID, expiresAt)
return err
}
func PasswordResetUser(tokenHash string) (AppUser, error) {
row := DB.QueryRow(`SELECT u.id, u.username, u.display_name, u.password_hash, u.role, u.active
FROM app_password_resets r JOIN app_users u ON u.id=r.user_id
WHERE r.token_hash=? AND r.used_at IS NULL AND r.expires_at > CURRENT_TIMESTAMP AND u.active=1`, tokenHash)
return scanAppUser(row)
}
func UsePasswordReset(tokenHash, passwordHash string) error {
tx, err := DB.Begin()
if err != nil {
return err
}
defer tx.Rollback()
var userID int64
err = tx.QueryRow(`SELECT user_id FROM app_password_resets WHERE token_hash=? AND used_at IS NULL AND expires_at > CURRENT_TIMESTAMP`, tokenHash).Scan(&userID)
if err != nil {
return err
}
if _, err := tx.Exec(`UPDATE app_users SET password_hash=?, updated_at=CURRENT_TIMESTAMP WHERE id=?`, passwordHash, userID); err != nil {
return err
}
if _, err := tx.Exec(`UPDATE app_password_resets SET used_at=CURRENT_TIMESTAMP WHERE token_hash=?`, tokenHash); err != nil {
return err
}
if _, err := tx.Exec(`DELETE FROM app_sessions WHERE user_id=?`, userID); err != nil {
return err
}
return tx.Commit()
}
func CleanupPasswordResets() error {
_, err := DB.Exec(`DELETE FROM app_password_resets WHERE used_at IS NOT NULL OR expires_at <= CURRENT_TIMESTAMP`)
return err
}
func ListAccounts() ([]Account, error) {
rows, err := DB.Query(`SELECT id,name,src_host,src_port,src_security,src_insecure,src_user,src_pass,src_proto,
dst_host,dst_port,dst_security,dst_insecure,dst_user,dst_pass,mbox_dir,active