Add role based login and user management
This commit is contained in:
parent
f67bbdce78
commit
353dafc30d
4 changed files with 681 additions and 34 deletions
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue