fix(auth): make diagnostics match runtime safety
This commit is contained in:
@@ -32,6 +32,8 @@ export interface LocalUserRecord {
|
||||
}
|
||||
|
||||
export interface LocalUserRegistry {
|
||||
/** Safe production diagnostic probe; never returns user records or hashes. */
|
||||
hasEnabledAdmin(): Promise<boolean>;
|
||||
findByUsername(username: string): Promise<LocalUserRecord | undefined>;
|
||||
findBySubject(id: string): Promise<LocalUserRecord | undefined>;
|
||||
verify(user: LocalUserRecord | undefined, password: string): Promise<boolean>;
|
||||
@@ -191,13 +193,11 @@ function parseRegistry(source: string): LocalUserRecord[] {
|
||||
const parsed = registrySchema.parse(document.toJSON());
|
||||
const ids = new Set<string>();
|
||||
const usernames = new Set<string>();
|
||||
let enabledAdmin = false;
|
||||
const records = parsed.users.map((user) => {
|
||||
const normalizedUsername = normalizeUsername(user.username);
|
||||
if (ids.has(user.id) || usernames.has(normalizedUsername)) throw invalid();
|
||||
ids.add(user.id);
|
||||
usernames.add(normalizedUsername);
|
||||
if (user.enabled && user.roles.includes("admin")) enabledAdmin = true;
|
||||
return Object.freeze({
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
@@ -209,7 +209,6 @@ function parseRegistry(source: string): LocalUserRecord[] {
|
||||
authRevision: user.authRevision,
|
||||
});
|
||||
});
|
||||
if (!enabledAdmin) throw invalid();
|
||||
return records;
|
||||
} catch {
|
||||
throw invalid();
|
||||
@@ -241,13 +240,22 @@ export function createLocalUserRegistry(usersPath: string): LocalUserRegistry {
|
||||
throw invalid();
|
||||
}
|
||||
|
||||
function operationalRecords(): LocalUserRecord[] {
|
||||
const records = current();
|
||||
if (!records.some((user) => user.enabled && user.roles.includes("admin"))) throw invalid();
|
||||
return records;
|
||||
}
|
||||
|
||||
return {
|
||||
async hasEnabledAdmin(): Promise<boolean> {
|
||||
return current().some((user) => user.enabled && user.roles.includes("admin"));
|
||||
},
|
||||
async findByUsername(username: string): Promise<LocalUserRecord | undefined> {
|
||||
const normalized = normalizeUsername(username);
|
||||
return current().find((user) => user.normalizedUsername === normalized);
|
||||
return operationalRecords().find((user) => user.normalizedUsername === normalized);
|
||||
},
|
||||
async findBySubject(id: string): Promise<LocalUserRecord | undefined> {
|
||||
return current().find((user) => user.id === id);
|
||||
return operationalRecords().find((user) => user.id === id);
|
||||
},
|
||||
async verify(user: LocalUserRecord | undefined, password: string): Promise<boolean> {
|
||||
if (!user || !user.enabled) {
|
||||
|
||||
Reference in New Issue
Block a user