Feat admin rbac migration (#1)
Some checks failed
CI/CD / detect-changes (push) Successful in 4s
CI/CD / test-frontend (push) Successful in 5s
CI/CD / build-and-deploy (push) Has been cancelled
CI/CD / test-sqlite (push) Has been cancelled
CI/CD / test-go-pg (push) Has been cancelled

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:
2026-03-26 17:30:36 +00:00
committed by xcaliber
parent b9180d4184
commit 5836ddad21
37 changed files with 381 additions and 547 deletions

View File

@@ -304,6 +304,31 @@ func TruncateAll(t *testing.T) {
}
}
// Re-seed system groups (truncated above).
if IsSQLite() {
DB.Exec(`INSERT OR IGNORE INTO groups (id, name, description, scope, created_by, source, permissions)
VALUES ('00000000-0000-0000-0000-000000000001', 'Everyone',
'Implicit group — all authenticated users receive these permissions.',
'global', NULL, 'system', '["extension.use","workflow.submit"]')`)
DB.Exec(`INSERT OR IGNORE INTO groups (id, name, description, scope, created_by, source, permissions)
VALUES ('00000000-0000-0000-0000-000000000002', 'Admins',
'Full platform access — replaces legacy admin role.',
'global', NULL, 'system',
'["surface.admin.access","extension.use","extension.install","workflow.create","workflow.submit","admin.view","token.unlimited"]')`)
} else {
DB.Exec(`INSERT INTO groups (id, name, description, scope, created_by, source, permissions)
VALUES ('00000000-0000-0000-0000-000000000001', 'Everyone',
'Implicit group — all authenticated users receive these permissions.',
'global', NULL, 'system', '["extension.use","workflow.submit"]'::jsonb)
ON CONFLICT (id) DO NOTHING`)
DB.Exec(`INSERT INTO groups (id, name, description, scope, created_by, source, permissions)
VALUES ('00000000-0000-0000-0000-000000000002', 'Admins',
'Full platform access — replaces legacy admin role.',
'global', NULL, 'system',
'["surface.admin.access","extension.use","extension.install","workflow.create","workflow.submit","admin.view","token.unlimited"]'::jsonb)
ON CONFLICT (id) DO NOTHING`)
}
// Re-seed default config rows.
if IsSQLite() {
DB.Exec(`
@@ -350,33 +375,69 @@ func TruncateAll(t *testing.T) {
}
}
// SeedTestUser creates a test user and returns the user ID.
// SeedTestUser creates a test user, adds to Everyone group, and returns the user ID.
func SeedTestUser(t *testing.T, username, email string) string {
t.Helper()
handle := strings.ToLower(strings.ReplaceAll(username, " ", "-"))
if IsSQLite() {
id := uuid.New().String()
_, err := DB.Exec(`
INSERT INTO users (id, username, email, password_hash, role, auth_source, handle)
VALUES (?, ?, ?, '$2a$10$dummy.hash.for.testing.only.000000000000000000000', 'user', 'builtin', ?)
INSERT INTO users (id, username, email, password_hash, auth_source, handle)
VALUES (?, ?, ?, '$2a$10$dummy.hash.for.testing.only.000000000000000000000', 'builtin', ?)
`, id, username, email, handle)
if err != nil {
t.Fatalf("SeedTestUser: %v", err)
}
SeedEveryoneGroupMember(t, id)
return id
}
var id string
err := DB.QueryRow(`
INSERT INTO users (username, email, password_hash, role, auth_source, handle)
VALUES ($1, $2, '$2a$10$dummy.hash.for.testing.only.000000000000000000000', 'user', 'builtin', $3)
INSERT INTO users (username, email, password_hash, auth_source, handle)
VALUES ($1, $2, '$2a$10$dummy.hash.for.testing.only.000000000000000000000', 'builtin', $3)
RETURNING id
`, username, email, handle).Scan(&id)
if err != nil {
t.Fatalf("SeedTestUser: %v", err)
}
SeedEveryoneGroupMember(t, id)
return id
}
// SeedEveryoneGroupMember adds a user to the Everyone system group in test databases.
func SeedEveryoneGroupMember(t *testing.T, userID string) {
t.Helper()
const everyoneGroupID = "00000000-0000-0000-0000-000000000001"
if IsSQLite() {
DB.Exec(`INSERT OR IGNORE INTO group_members (id, group_id, user_id, added_by) VALUES (?, ?, ?, ?)`,
uuid.New().String(), everyoneGroupID, userID, userID)
return
}
DB.Exec(`INSERT INTO group_members (group_id, user_id, added_by) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING`,
everyoneGroupID, userID, userID)
}
// SeedAdminsGroupMember adds a user to the Admins system group in test databases.
// Call after creating a user with role='admin' so they receive surface.admin.access
// through the normal permission resolution path.
func SeedAdminsGroupMember(t *testing.T, userID string) {
t.Helper()
const adminsGroupID = "00000000-0000-0000-0000-000000000002"
if IsSQLite() {
_, err := DB.Exec(`INSERT OR IGNORE INTO group_members (id, group_id, user_id, added_by) VALUES (?, ?, ?, ?)`,
uuid.New().String(), adminsGroupID, userID, userID)
if err != nil {
t.Fatalf("SeedAdminsGroupMember: %v", err)
}
return
}
_, err := DB.Exec(`INSERT INTO group_members (group_id, user_id, added_by) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING`,
adminsGroupID, userID, userID)
if err != nil {
t.Fatalf("SeedAdminsGroupMember: %v", err)
}
}
// SeedTestChannel creates a test channel owned by userID and returns the channel ID.
func SeedTestChannel(t *testing.T, userID, title string) string {
t.Helper()