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
+8 -6
View File
@@ -108,7 +108,7 @@ function addCredentialModal() {
});
if (!beginRes.ok) {
throw beginRes.error || 'Registration failed';
throw new Error(beginRes.error || 'Registration failed');
}
const options = beginRes.data;
@@ -129,15 +129,16 @@ function addCredentialModal() {
});
if (!finishRes.ok) {
throw finishRes.error || 'Registration verification failed';
throw new Error(finishRes.error || 'Registration verification failed');
}
toast('Passkey registered', 'success');
closeModal();
loadCredentials();
} catch (e) {
if (!e.message.toLowerCase().includes('cancelled')) {
toast(e.message || 'Registration failed', 'error');
const msg = typeof e === 'string' ? e : (e.message || 'Registration failed');
if (!msg.toLowerCase().includes('cancelled')) {
toast(msg, 'error');
}
} finally {
setModalProcessing(false);
@@ -180,14 +181,15 @@ function confirmRemove(credentialId, credentialName) {
});
if (!res.ok) {
throw res.error || 'Removal failed';
throw new Error(res.error || 'Removal failed');
}
toast('Passkey removed', 'success');
closeModal();
loadCredentials();
} catch (e) {
toast(e.message || 'Removal failed', 'error');
const msg = typeof e === 'string' ? e : (e.message || 'Removal failed');
toast(msg, 'error');
} finally {
setModalProcessing(false);
refreshModals();