Give local users their own manager: custom passwords and role reassignment

Move user management out of Settings into its own /users page (nav-gated
to owners), let an owner type a specific password on reset instead of
always generating a random one, and add role reassignment via a new
PUT /auth/users/{id}/role endpoint. Role changes revoke the target's
existing sessions, same as a password reset, so a demoted user can't
keep acting under a stale, higher-privileged session.
This commit is contained in:
2026-08-21 14:23:52 -07:00
parent 4b5dae5879
commit 864e68253a
10 changed files with 887 additions and 225 deletions
+16 -3
View File
@@ -18,15 +18,16 @@
onCloseMobile
}: { onOpenPalette: () => void; mobileOpen?: boolean; onCloseMobile?: () => void } = $props();
const navItems = [
const baseNavItems = [
{ href: '/', label: 'Search', icon: '◇' },
{ href: '/dashboards', label: 'Dashboards', icon: '▤' },
{ href: '/alerts', label: 'Alerts', icon: '▲' },
{ href: '/data-sources', label: 'Data Sources', icon: '◈' },
{ href: '/agents', label: 'Agents', icon: '●' },
{ href: '/hosts', label: 'Hosts', icon: '▣' },
{ href: '/settings', label: 'Settings', icon: '⚙' }
{ href: '/hosts', label: 'Hosts', icon: '▣' }
];
const usersNavItem = { href: '/users', label: 'Users', icon: '◐' };
const settingsNavItem = { href: '/settings', label: 'Settings', icon: '⚙' };
function isActive(href: string): boolean {
if (href === '/') return page.url.pathname === '/';
@@ -44,6 +45,18 @@
getLocalSession().then((s) => (localSession = s === 'disabled' ? null : s));
});
// The Users nav item only ever makes sense for local-auth mode's
// owner-only user manager (see routes/users/+page.svelte) -- an
// enterprise-SSO deployment or a non-owner local session never sees
// it, same gating that page enforces itself if reached directly.
const isLocalOwner = $derived.by(() => {
const s = localSession;
return s !== null && s.role === 'owner';
});
const navItems = $derived(
isLocalOwner ? [...baseNavItems, usersNavItem, settingsNavItem] : [...baseNavItems, settingsNavItem]
);
let loggingOut = $state(false);
async function handleLogout() {
loggingOut = true;