Feat admin rbac migration (#1)
Co-authored-by: Jeffrey Smith <jasafpro@gmail.com> Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
This commit was merged in pull request #1.
This commit is contained in:
@@ -32,7 +32,6 @@ const bcryptCost = 12
|
||||
type Claims struct {
|
||||
UserID string `json:"user_id"`
|
||||
Email string `json:"email"`
|
||||
Role string `json:"role"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
@@ -295,7 +294,6 @@ func (h *AuthHandler) generateTokens(user *models.User) (gin.H, error) {
|
||||
accessClaims := Claims{
|
||||
UserID: user.ID,
|
||||
Email: user.Email,
|
||||
Role: user.Role,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(15 * time.Minute)),
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
@@ -327,7 +325,6 @@ func (h *AuthHandler) generateTokens(user *models.User) (gin.H, error) {
|
||||
"username": user.Username,
|
||||
"email": user.Email,
|
||||
"display_name": user.DisplayName,
|
||||
"role": user.Role,
|
||||
"handle": user.Handle,
|
||||
"auth_source": user.AuthSource,
|
||||
},
|
||||
@@ -489,10 +486,9 @@ func BootstrapAdmin(cfg *config.Config, s store.Stores, uekCache ...*crypto.UEKC
|
||||
|
||||
existing, _ := s.Users.GetByUsername(ctx, cfg.AdminUsername)
|
||||
if existing != nil {
|
||||
// Update password and ensure admin role
|
||||
// Update password
|
||||
s.Users.Update(ctx, existing.ID, map[string]interface{}{
|
||||
"password_hash": string(hash),
|
||||
"role": models.UserRoleAdmin,
|
||||
"is_active": true,
|
||||
})
|
||||
// If the actual password changed, the vault seal is stale. Probe it
|
||||
@@ -507,6 +503,8 @@ func BootstrapAdmin(cfg *config.Config, s store.Stores, uekCache ...*crypto.UEKC
|
||||
cache = uekCache[0]
|
||||
}
|
||||
ProbeAndRepairVault(ctx, s, existing.ID, cfg.AdminPassword, cache)
|
||||
auth.EnsureEveryoneGroup(ctx, s, existing.ID)
|
||||
auth.AddToAdminsGroup(ctx, s, existing.ID)
|
||||
log.Printf(" ✅ Admin user '%s' updated", cfg.AdminUsername)
|
||||
return
|
||||
}
|
||||
@@ -521,7 +519,6 @@ func BootstrapAdmin(cfg *config.Config, s store.Stores, uekCache ...*crypto.UEKC
|
||||
Username: strings.ToLower(cfg.AdminUsername),
|
||||
Email: strings.ToLower(email),
|
||||
PasswordHash: string(hash),
|
||||
Role: models.UserRoleAdmin,
|
||||
IsActive: true,
|
||||
AuthSource: "builtin",
|
||||
Handle: handle,
|
||||
@@ -531,12 +528,14 @@ func BootstrapAdmin(cfg *config.Config, s store.Stores, uekCache ...*crypto.UEKC
|
||||
log.Printf("⚠ Failed to create admin user: %v", err)
|
||||
return
|
||||
}
|
||||
auth.EnsureEveryoneGroup(ctx, s, user.ID)
|
||||
auth.AddToAdminsGroup(ctx, s, user.ID)
|
||||
log.Printf(" ✅ Admin user '%s' created", cfg.AdminUsername)
|
||||
}
|
||||
|
||||
// SeedUsers creates or updates users from the SEED_USERS env var.
|
||||
// Format: "user:pass:role,user2:pass2:role2" where role is "admin" or "user".
|
||||
// Upsert: existing users get their password and role refreshed on every restart.
|
||||
// Format: "user:pass[:admin],user2:pass2" — third field "admin" adds to Admins group.
|
||||
// Upsert: existing users get their password refreshed on every restart.
|
||||
// Gated to non-production environments.
|
||||
func SeedUsers(cfg *config.Config, s store.Stores, uekCache ...*crypto.UEKCache) {
|
||||
if cfg.SeedUsers == "" {
|
||||
@@ -561,16 +560,13 @@ func SeedUsers(cfg *config.Config, s store.Stores, uekCache ...*crypto.UEKCache)
|
||||
|
||||
parts := strings.SplitN(entry, ":", 3)
|
||||
if len(parts) < 2 {
|
||||
log.Printf("⚠ Seed user skipped (bad format, want user:pass[:role]): %q", entry)
|
||||
log.Printf("⚠ Seed user skipped (bad format, want user:pass[:admin]): %q", entry)
|
||||
continue
|
||||
}
|
||||
|
||||
username := strings.ToLower(strings.TrimSpace(parts[0]))
|
||||
password := strings.TrimSpace(parts[1])
|
||||
role := models.UserRoleUser
|
||||
if len(parts) >= 3 && strings.TrimSpace(parts[2]) == "admin" {
|
||||
role = models.UserRoleAdmin
|
||||
}
|
||||
isAdmin := len(parts) >= 3 && strings.TrimSpace(parts[2]) == "admin"
|
||||
|
||||
if username == "" || password == "" {
|
||||
log.Printf("⚠ Seed user skipped (empty username or password)")
|
||||
@@ -583,15 +579,13 @@ func SeedUsers(cfg *config.Config, s store.Stores, uekCache ...*crypto.UEKCache)
|
||||
continue
|
||||
}
|
||||
|
||||
// Upsert: update password+role if user exists, create if not
|
||||
// Upsert: update password if user exists, create if not
|
||||
existing, _ := s.Users.GetByUsername(ctx, username)
|
||||
if existing != nil {
|
||||
s.Users.Update(ctx, existing.ID, map[string]interface{}{
|
||||
"password_hash": string(hash),
|
||||
"role": role,
|
||||
"is_active": true,
|
||||
})
|
||||
// Probe vault with current password — only destroys if seal is stale
|
||||
if existing.Handle == "" {
|
||||
handle := auth.UniqueHandle(ctx, s.Users, models.HandleFromName(username))
|
||||
s.Users.Update(ctx, existing.ID, map[string]interface{}{"handle": handle})
|
||||
@@ -601,7 +595,11 @@ func SeedUsers(cfg *config.Config, s store.Stores, uekCache ...*crypto.UEKCache)
|
||||
cache = uekCache[0]
|
||||
}
|
||||
ProbeAndRepairVault(ctx, s, existing.ID, password, cache)
|
||||
log.Printf(" 🌱 Seed user '%s' updated (role=%s)", username, role)
|
||||
auth.EnsureEveryoneGroup(ctx, s, existing.ID)
|
||||
if isAdmin {
|
||||
auth.AddToAdminsGroup(ctx, s, existing.ID)
|
||||
}
|
||||
log.Printf(" 🌱 Seed user '%s' updated (admin=%v)", username, isAdmin)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -610,7 +608,6 @@ func SeedUsers(cfg *config.Config, s store.Stores, uekCache ...*crypto.UEKCache)
|
||||
Username: username,
|
||||
Email: username + "@switchboard.local",
|
||||
PasswordHash: string(hash),
|
||||
Role: role,
|
||||
IsActive: true,
|
||||
AuthSource: "builtin",
|
||||
Handle: handle,
|
||||
@@ -620,7 +617,11 @@ func SeedUsers(cfg *config.Config, s store.Stores, uekCache ...*crypto.UEKCache)
|
||||
log.Printf("⚠ Seed user '%s' failed: %v", username, err)
|
||||
continue
|
||||
}
|
||||
log.Printf(" 🌱 Seed user '%s' created (role=%s, active=true)", username, role)
|
||||
auth.EnsureEveryoneGroup(ctx, s, user.ID)
|
||||
if isAdmin {
|
||||
auth.AddToAdminsGroup(ctx, s, user.ID)
|
||||
}
|
||||
log.Printf(" 🌱 Seed user '%s' created (admin=%v, active=true)", username, isAdmin)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user