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
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:
@@ -33,7 +33,7 @@ export default function UsersSection() {
|
||||
username: form.username.value.trim(),
|
||||
email: form.email.value.trim(),
|
||||
password: form.password.value,
|
||||
role: form.role.value,
|
||||
is_admin: form.is_admin.checked,
|
||||
};
|
||||
if (!data.username || !data.password) return sw.toast('Username and password required', 'error');
|
||||
try {
|
||||
@@ -52,10 +52,10 @@ export default function UsersSection() {
|
||||
} catch (e) { sw.toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
async function changeRole(id, role) {
|
||||
async function toggleAdmin(id, isAdmin) {
|
||||
try {
|
||||
await sw.api.admin.users.updateRole(id, role);
|
||||
sw.toast(`Role updated to ${role}`, 'success');
|
||||
await sw.api.admin.users.updateRole(id, isAdmin);
|
||||
sw.toast(isAdmin ? 'Admin granted' : 'Admin revoked', 'success');
|
||||
load();
|
||||
} catch (e) { sw.toast(e.message, 'error'); }
|
||||
}
|
||||
@@ -112,8 +112,8 @@ export default function UsersSection() {
|
||||
<div class="form-group"><label>Username</label><input name="username" placeholder="username" autocomplete="off" /></div>
|
||||
<div class="form-group"><label>Email</label><input name="email" placeholder="email@example.com" autocomplete="off" /></div>
|
||||
<div class="form-group"><label>Password</label><input name="password" type="password" placeholder="min 8 chars" autocomplete="new-password" /></div>
|
||||
<div class="form-group"><label>Role</label>
|
||||
<select name="role"><option value="user">User</option><option value="admin">Admin</option></select>
|
||||
<div class="form-group" style="align-self:flex-end;">
|
||||
<label class="toggle-label"><input type="checkbox" name="is_admin" /><span class="toggle-track"></span><span>Admin</span></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row" style="margin-top:8px;gap:8px;">
|
||||
@@ -131,17 +131,11 @@ export default function UsersSection() {
|
||||
<strong>${u.username}</strong>
|
||||
<span class="text-muted" style="margin-left:8px;font-size:12px;">${u.email || ''}</span>
|
||||
${' '}${statusBadge(u)}
|
||||
${' '}<span class="badge ${u.role === 'admin' ? 'badge-admin' : ''}">${u.role}</span>
|
||||
</div>
|
||||
<div class="admin-actions-cell">
|
||||
${u.status === 'pending' && html`
|
||||
<button class="btn-small btn-primary" onClick=${() => approveUser(u.id)}>Approve</button>
|
||||
`}
|
||||
<select value=${u.role} onChange=${e => changeRole(u.id, e.target.value)}
|
||||
style="font-size:11px;padding:2px 4px;">
|
||||
<option value="user">user</option>
|
||||
<option value="admin">admin</option>
|
||||
</select>
|
||||
${u.is_active
|
||||
? html`<button class="btn-small" onClick=${() => toggleActive(u.id, false)}>Disable</button>`
|
||||
: html`<button class="btn-small" onClick=${() => toggleActive(u.id, true)}>Enable</button>`
|
||||
|
||||
Reference in New Issue
Block a user