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;
};
export function listUsers(): AppUser[] {
return db.all<AppUser>(
`SELECT id, username, full_name, email, auth_source, created_at, updated_at
FROM users
ORDER BY username COLLATE NOCASE`
);
export function listUsers(): (AppUser & { has_local_credentials: boolean })[] {
return db
.all<AppUser & { has_local_credentials: 0 | 1 }>(
`SELECT u.id, u.username, u.full_name, u.email, u.auth_source, u.created_at, u.updated_at,
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: {

View File

@@ -8,6 +8,7 @@
full_name: string;
email: string | null;
auth_source: string;
has_local_credentials: boolean;
}
let users = $state<User[]>([]);
@@ -340,9 +341,16 @@
{user.email || '—'}
</td>
<td class="px-4 py-3">
<div class="flex flex-wrap gap-1">
<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 class="px-4 py-3 text-right">
<div class="inline-flex items-center gap-1">