fix: harden auth and fix frontend issues

- Add builtin admin user with full access, immutable permissions (lib/db.py, lib/auth_users.py, webui/static/pages/users.js)
- Fix passkeys TypeError on string throws (webui/static/pages/passkeys.js)
- Add zero-permission warning in create user modal (webui/static/pages/users.js)
- Restore readonly on proxy paths textarea (webui/static/pages/proxy.js)
- Mask credential ownership errors to prevent enumeration (lib/webauthn.py, tests/test_auth.py)
This commit is contained in:
2026-07-28 18:52:03 +00:00
parent a82578f342
commit 8ae60ab8cf
7 changed files with 78 additions and 22 deletions
+12 -4
View File
@@ -8,6 +8,8 @@
import { h, definePage, reactive } from '/static/hoover/index.js';
import { html, PageHeader, Table, Badge, ConfirmDelete, Empty, Card, openModal, closeModal, formModal, apiFetch, toast, esc } from '/static/hoover/index.js';
const BUILTIN_ADMIN = 'admin';
const SUBSYSTEMS = [
{ key: 'firewall', label: 'Firewall' },
{ key: 'network', label: 'Network' },
@@ -157,11 +159,16 @@ function openEditPermissionsModal(user) {
for (const sub of SUBSYSTEMS) {
const level = document.getElementById('edit-perm-' + sub.key).value;
if (level && level !== '—') {
perms[sub.key] = level;
perms[sub.key] = level;
}
}
}
const res = await apiFetch('/api/auth/users/' + encodeURIComponent(user.username), {
if (user.username === BUILTIN_ADMIN) {
toast('Cannot modify permissions for builtin admin', 'error');
return;
}
const res = await apiFetch('/api/auth/users/' + encodeURIComponent(user.username), {
method: 'POST',
body: { permissions: perms },
});
@@ -215,7 +222,8 @@ function UsersPage() {
<td class="text-sm">${u.credCount || 0}</td>
<td class="text-sm text-muted">${permBadges.length ? permBadges.join(' ') : '—'}</td>
<td>
<button class="btn btn-sm btn-outline" onClick=${() => openEditPermissionsModal(u)}>Edit</button>
${u.username === BUILTIN_ADMIN ? html`<span class="text-muted text-sm">(builtin)</span>` :
html`<button class="btn btn-sm btn-outline" onClick=${() => openEditPermissionsModal(u)}>Edit</button>`}
${isMe ? html`<span class="text-muted text-sm">(you)</span>` :
html`<${ConfirmDelete}
url=${'/api/auth/users/' + encodeURIComponent(u.username)}