Add role based login and user management

This commit is contained in:
DonVoo 2026-07-13 00:28:01 +02:00
parent f67bbdce78
commit 353dafc30d
4 changed files with 681 additions and 34 deletions

View file

@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"os"
"strings"
_ "modernc.org/sqlite"
)
@ -37,6 +38,14 @@ type Account struct {
Active bool `json:"active"`
}
type AppUser struct {
ID int64
Username string
Role string
Active bool
PasswordHash string
}
// ConnectDB oeffnet die SQLite-DB (modernc, kein cgo) und legt das Schema an.
//
// Schema (TODO Codex):
@ -107,6 +116,21 @@ func ConnectDB() error {
errors INTEGER NOT NULL DEFAULT 0,
state TEXT NOT NULL DEFAULT 'running'
)`,
`CREATE TABLE IF NOT EXISTS app_users(
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'user',
active INTEGER NOT NULL DEFAULT 1,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
)`,
`CREATE TABLE IF NOT EXISTS app_sessions(
token TEXT PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES app_users(id) ON DELETE CASCADE,
expires_at TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
)`,
}
for _, stmt := range schema {
if _, err := db.Exec(stmt); err != nil {
@ -118,6 +142,84 @@ func ConnectDB() error {
return nil
}
func CountAppUsers() (int, error) {
var count int
err := DB.QueryRow(`SELECT count(*) FROM app_users`).Scan(&count)
return count, err
}
func ListAppUsers() ([]AppUser, error) {
rows, err := DB.Query(`SELECT id, username, password_hash, role, active FROM app_users ORDER BY username`)
if err != nil {
return nil, err
}
defer rows.Close()
var out []AppUser
for rows.Next() {
u, err := scanAppUser(rows)
if err != nil {
return nil, err
}
out = append(out, u)
}
return out, rows.Err()
}
func GetAppUser(username string) (AppUser, error) {
row := DB.QueryRow(`SELECT id, username, password_hash, role, active FROM app_users WHERE username=?`, username)
return scanAppUser(row)
}
func GetAppUserByID(id int64) (AppUser, error) {
row := DB.QueryRow(`SELECT id, username, password_hash, role, active FROM app_users WHERE id=?`, id)
return scanAppUser(row)
}
func SaveAppUser(username, passwordHash, role string, active bool) error {
role = normalizeRole(role)
if passwordHash == "" {
_, err := DB.Exec(`UPDATE app_users SET role=?, active=?, updated_at=CURRENT_TIMESTAMP WHERE username=?`,
role, boolInt(active), username)
return err
}
_, err := DB.Exec(`INSERT INTO app_users(username, password_hash, role, active)
VALUES(?,?,?,?)
ON CONFLICT(username) DO UPDATE SET
password_hash=excluded.password_hash,
role=excluded.role,
active=excluded.active,
updated_at=CURRENT_TIMESTAMP`,
username, passwordHash, role, boolInt(active))
return err
}
func DeleteAppUser(username string) error {
_, err := DB.Exec(`DELETE FROM app_users WHERE username=?`, username)
return err
}
func CreateSession(token string, userID int64, expiresAt string) error {
_, err := DB.Exec(`INSERT INTO app_sessions(token, user_id, expires_at) VALUES(?,?,?)`, token, userID, expiresAt)
return err
}
func DeleteSession(token string) error {
_, err := DB.Exec(`DELETE FROM app_sessions WHERE token=?`, token)
return err
}
func SessionUser(token string) (AppUser, error) {
row := DB.QueryRow(`SELECT u.id, u.username, u.password_hash, u.role, u.active
FROM app_sessions s JOIN app_users u ON u.id=s.user_id
WHERE s.token=? AND s.expires_at > CURRENT_TIMESTAMP AND u.active=1`, token)
return scanAppUser(row)
}
func CleanupSessions() error {
_, err := DB.Exec(`DELETE FROM app_sessions WHERE 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
@ -202,6 +304,10 @@ type accountScanner interface {
Scan(dest ...any) error
}
type appUserScanner interface {
Scan(dest ...any) error
}
func scanAccount(s accountScanner) (Account, error) {
var a Account
var srcInsecure, dstInsecure, active int
@ -242,6 +348,26 @@ func normalizeAccount(a *Account) {
}
}
func scanAppUser(s appUserScanner) (AppUser, error) {
var u AppUser
var active int
err := s.Scan(&u.ID, &u.Username, &u.PasswordHash, &u.Role, &active)
u.Role = normalizeRole(u.Role)
u.Active = active != 0
return u, err
}
func normalizeRole(role string) string {
switch strings.ToLower(strings.TrimSpace(role)) {
case "admin":
return "admin"
case "verwalter":
return "verwalter"
default:
return "user"
}
}
func boolInt(v bool) int {
if v {
return 1