drop users.role column: full RBAC through group membership
All checks were successful
CI/CD / detect-changes (pull_request) Successful in 4s
CI/CD / test-frontend (pull_request) Successful in 5s
CI/CD / test-go-pg (pull_request) Successful in 2m22s
CI/CD / test-sqlite (pull_request) Successful in 2m35s
CI/CD / build-and-deploy (pull_request) Successful in 1m18s

The role column was a pre-RBAC artifact. All authorization now flows
through explicit group membership and permission grants:

- Everyone group: all users added on creation (no implicit membership)
- Admins group: grants surface.admin.access + all platform permissions
- JWT claims, login response, profile: role field removed
- OIDC: isIdPAdmin() maps IdP claims → Admins group (no role writes)
- Admin UI: role dropdown removed, admin managed through groups
- Middleware cache simplified to isActive only

28 files changed, -79 lines net. Zero magic roles.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-26 17:23:12 +00:00
parent e8e45184f7
commit b10f5bee05
28 changed files with 206 additions and 285 deletions

View File

@@ -6,6 +6,7 @@ import (
"fmt"
"log"
"switchboard-core/auth"
"switchboard-core/events"
"switchboard-core/models"
"switchboard-core/store"
@@ -44,26 +45,23 @@ func RoleFallbackHandler(svc *Service, stores store.Stores) events.Handler {
body = fmt.Sprintf("Primary model error: %s", p.Error)
}
// Notify all admin users
// Notify all Admins group members
ctx := context.Background()
admins, _, err := stores.Users.List(ctx, store.ListOptions{Limit: 500})
admins, err := stores.Groups.ListMembers(ctx, auth.AdminsGroupID)
if err != nil {
log.Printf("[notifications] failed to list users for role.fallback: %v", err)
log.Printf("[notifications] failed to list admins for role.fallback: %v", err)
return
}
for _, u := range admins {
if u.Role != "admin" {
continue
}
n := models.Notification{
UserID: u.ID,
UserID: u.UserID,
Type: models.NotifTypeRoleFallback,
Title: title,
Body: body,
}
if err := svc.Notify(ctx, &n); err != nil {
log.Printf("[notifications] failed to create role.fallback notification for %s: %v", u.ID, err)
log.Printf("[notifications] failed to create role.fallback notification for %s: %v", u.UserID, err)
}
}
}