Compare commits
2 Commits
alpha-1.21
...
alpha-1.23
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
796824c400 | ||
|
|
8103fffcb8 |
@@ -70,12 +70,31 @@ async def update_user(
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Update an existing user (email, is_active, role)."""
|
||||
"""Update an existing user (email, is_active, role). Admin only."""
|
||||
if current_user.role != "admin":
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Only admins can update users.",
|
||||
)
|
||||
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
if not user:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found.")
|
||||
|
||||
update_data = payload.model_dump(exclude_none=True)
|
||||
|
||||
if "role" in update_data:
|
||||
if update_data["role"] not in ("admin", "viewer"):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Role must be 'admin' or 'viewer'.",
|
||||
)
|
||||
if user_id == current_user.id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="You cannot change your own role.",
|
||||
)
|
||||
|
||||
for field, value in update_data.items():
|
||||
if hasattr(user, field):
|
||||
setattr(user, field, value)
|
||||
|
||||
@@ -57,6 +57,7 @@ services:
|
||||
- "${WEB_UI_PORT:-8000}:8000"
|
||||
volumes:
|
||||
- ./data:/app/data:z
|
||||
- ./data/uploads:/app/static/uploads:z
|
||||
- ./logs:/app/logs:z
|
||||
- ./backups:/app/backups:z
|
||||
- /var/run/docker.sock:/var/run/docker.sock:z
|
||||
|
||||
@@ -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