Multiple users, all admins

The single admin account becomes a list of users; config.json moves to
version 2 and the old admin is migrated on first start. Every user is an
admin. Sessions are tied to a user and their password, so deleting a user
or resetting a password signs them out at once. API tokens belong to the
user who made them and go away with that user.

Admins add users with a temporary password and choose whether it must be
changed at first sign-in; until then the API refuses everything but the
password change. Settings gets My account and Users cards, and the token
table shows each token's owner. 'GHOSTWIRE passwd [username]' resets any
user's password. A failed update now also restores config.json, since the
new version may have upgraded it.
This commit is contained in:
Daniel Redetzke
2026-10-04 15:51:14 +03:00
parent f703618b9d
commit 4ecfddf06a
11 changed files with 825 additions and 109 deletions
+5 -5
View File
@@ -106,7 +106,7 @@ func (p installPlan) apply(c *Config) {
c.Server.ListenPort = p.port
}
if p.passwordHash != "" {
c.Admin.PasswordHash = p.passwordHash
c.Users[0].PasswordHash = p.passwordHash
}
}
@@ -287,11 +287,11 @@ func askInstall(in io.Reader, cur *Config, existing bool, given map[string]bool,
}
}
// Admin account, only when no password is set yet.
if cur.Admin.PasswordHash == "" {
// First user, only when nobody has a password yet.
if !cur.passwordSet() {
fmt.Println("\nAdmin account")
for {
pw, err := readSecret(fmt.Sprintf(" Password for %q (at least 12 characters): ", cur.Admin.Username))
pw, err := readSecret(fmt.Sprintf(" Password for %q (at least 12 characters): ", cur.Users[0].Username))
if err != nil {
return p, errCancelled
}
@@ -393,6 +393,6 @@ func printInstallSummary(cur *Config, existing bool, p installPlan) {
fmt.Printf(" Tunnel network %s\n", tunnel)
fmt.Printf(" Firewall %s must be reachable\n", strings.Join(ports, ", "))
if p.passwordHash != "" {
fmt.Printf(" Admin %s (password set)\n", next.Admin.Username)
fmt.Printf(" Admin %s (password set)\n", next.Users[0].Username)
}
}