feat(users): allow role assignment for Azure AD and LDAP users
- Backend: add admin-only guard + role validation to PUT /users/{id}
- Backend: prevent admins from changing their own role
- Frontend: role toggle button (person-check / person-dash) per user row
- Frontend: admin badge green, viewer badge secondary, ldap badge blue
- i18n: add makeAdmin / makeViewer translations (de + en)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1368,8 +1368,8 @@ async function loadUsers() {
|
||||
<td>${u.id}</td>
|
||||
<td><strong>${esc(u.username)}</strong></td>
|
||||
<td>${esc(u.email || '-')}</td>
|
||||
<td><span class="badge bg-info">${esc(u.role || 'admin')}</span></td>
|
||||
<td><span class="badge bg-${u.auth_provider === 'azure' ? 'primary' : 'secondary'}">${esc(u.auth_provider || 'local')}</span></td>
|
||||
<td><span class="badge bg-${u.role === 'admin' ? 'success' : 'secondary'}">${esc(u.role || 'admin')}</span></td>
|
||||
<td><span class="badge bg-${u.auth_provider === 'azure' ? 'primary' : u.auth_provider === 'ldap' ? 'info' : 'secondary'}">${esc(u.auth_provider || 'local')}</span></td>
|
||||
<td>${langDisplay}</td>
|
||||
<td>${mfaDisplay}</td>
|
||||
<td>${u.is_active ? `<span class="badge bg-success">${t('common.active')}</span>` : `<span class="badge bg-danger">${t('common.disabled')}</span>`}</td>
|
||||
@@ -1381,6 +1381,11 @@ async function loadUsers() {
|
||||
}
|
||||
${u.auth_provider === 'local' ? `<button class="btn btn-outline-info" title="${t('common.resetPassword')}" onclick="resetUserPassword(${u.id}, '${esc(u.username)}')"><i class="bi bi-key"></i></button>` : ''}
|
||||
${u.totp_enabled ? `<button class="btn btn-outline-secondary" title="${t('mfa.resetMfa')}" onclick="resetUserMfa(${u.id}, '${esc(u.username)}')"><i class="bi bi-shield-x"></i></button>` : ''}
|
||||
${currentUser && currentUser.role === 'admin' && u.id !== currentUser.id
|
||||
? (u.role === 'admin'
|
||||
? `<button class="btn btn-outline-secondary" title="${t('settings.makeViewer')}" onclick="toggleUserRole(${u.id}, 'admin')"><i class="bi bi-person-dash"></i></button>`
|
||||
: `<button class="btn btn-outline-success" title="${t('settings.makeAdmin')}" onclick="toggleUserRole(${u.id}, 'viewer')"><i class="bi bi-person-check"></i></button>`)
|
||||
: ''}
|
||||
<button class="btn btn-outline-danger" title="${t('common.delete')}" onclick="deleteUser(${u.id}, '${esc(u.username)}')"><i class="bi bi-trash"></i></button>
|
||||
</div>
|
||||
</td>
|
||||
@@ -1440,6 +1445,16 @@ async function toggleUserActive(id, active) {
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleUserRole(id, currentRole) {
|
||||
const newRole = currentRole === 'admin' ? 'viewer' : 'admin';
|
||||
try {
|
||||
await api('PUT', `/users/${id}`, { role: newRole });
|
||||
loadUsers();
|
||||
} catch (err) {
|
||||
showSettingsAlert('danger', t('errors.updateFailed', { error: err.message }));
|
||||
}
|
||||
}
|
||||
|
||||
async function resetUserPassword(id, username) {
|
||||
if (!confirm(t('messages.confirmResetPassword', { username }))) return;
|
||||
try {
|
||||
|
||||
@@ -170,6 +170,8 @@
|
||||
"saveBranding": "Branding speichern",
|
||||
"userManagement": "Benutzerverwaltung",
|
||||
"newUser": "Neuer Benutzer",
|
||||
"makeAdmin": "Zum Admin befördern",
|
||||
"makeViewer": "Zum Viewer degradieren",
|
||||
"thId": "ID",
|
||||
"thUsername": "Benutzername",
|
||||
"thEmail": "E-Mail",
|
||||
|
||||
@@ -191,6 +191,8 @@
|
||||
"saveBranding": "Save Branding",
|
||||
"userManagement": "User Management",
|
||||
"newUser": "New User",
|
||||
"makeAdmin": "Promote to admin",
|
||||
"makeViewer": "Demote to viewer",
|
||||
"thId": "ID",
|
||||
"thUsername": "Username",
|
||||
"thEmail": "Email",
|
||||
|
||||
Reference in New Issue
Block a user