feat(admin/users): show all auth sources when a user has multiple credentials
Some checks failed
PR Checks / lint-test-and-docker-build (pull_request) Failing after 1m0s

listUsers() now LEFT JOINs local_credentials and returns has_local_credentials.
The auth source cell renders both the primary auth_source badge and a secondary
Local badge when an OIDC user also has local credentials set.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-22 15:02:40 -05:00
parent 67648a5ba4
commit f4c5ee1f8f
2 changed files with 21 additions and 9 deletions

View File

@@ -27,12 +27,16 @@ const normalizeEmail = (value?: string | null): string | null | undefined => {
return trimmed ? trimmed : null; return trimmed ? trimmed : null;
}; };
export function listUsers(): AppUser[] { export function listUsers(): (AppUser & { has_local_credentials: boolean })[] {
return db.all<AppUser>( return db
`SELECT id, username, full_name, email, auth_source, created_at, updated_at .all<AppUser & { has_local_credentials: 0 | 1 }>(
FROM users `SELECT u.id, u.username, u.full_name, u.email, u.auth_source, u.created_at, u.updated_at,
ORDER BY username COLLATE NOCASE` CASE WHEN lc.user_id IS NOT NULL THEN 1 ELSE 0 END AS has_local_credentials
); FROM users u
LEFT JOIN local_credentials lc ON lc.user_id = u.id
ORDER BY u.username COLLATE NOCASE`
)
.map((row) => ({ ...row, has_local_credentials: row.has_local_credentials === 1 }));
} }
export function upsertUserFromAuth(input: { export function upsertUserFromAuth(input: {

View File

@@ -8,6 +8,7 @@
full_name: string; full_name: string;
email: string | null; email: string | null;
auth_source: string; auth_source: string;
has_local_credentials: boolean;
} }
let users = $state<User[]>([]); let users = $state<User[]>([]);
@@ -340,9 +341,16 @@
{user.email || '—'} {user.email || '—'}
</td> </td>
<td class="px-4 py-3"> <td class="px-4 py-3">
<span class="rounded bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-700" <div class="flex flex-wrap gap-1">
>{user.auth_source}</span <span class="rounded bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-700"
> >{user.auth_source}</span
>
{#if user.has_local_credentials && user.auth_source !== 'Local'}
<span class="rounded bg-blue-100 px-2 py-0.5 text-xs font-medium text-blue-700"
>Local</span
>
{/if}
</div>
</td> </td>
<td class="px-4 py-3 text-right"> <td class="px-4 py-3 text-right">
<div class="inline-flex items-center gap-1"> <div class="inline-flex items-center gap-1">