Every credential provider says whether it still holds a consumer

holds() for postgres, mssql, mongodb, minio, lavinmq, mosquitto, mailu
and gitea, so the harness makes again a login the backend lost (hq issue
120). Each checks the mesh's password as the consumer presents it, or
compares it read-only, and returns false only when the backend says the
credential is absent or wrong; an unreachable backend throws.
This commit is contained in:
jochen
2026-09-26 01:09:52 +02:00
parent 3d271f72ea
commit 0cb0f814b4
24 changed files with 286 additions and 12 deletions
+26
View File
@@ -109,6 +109,32 @@ print(EJSON.stringify({ ok: 1 }));
await this.evalJs<{ ok: number }>(js);
}
/**
* Whether `user` authenticates against `database` with exactly `password` and holds `dbOwner`
* there: checked by connecting as the consumer, the way it connects. Read-only. `false` only on an
* authentication failure or a missing role; an unreachable server rejects (novox/hq issue 120).
*/
async canAuthenticateAs(database: string, user: string, password: string): Promise<boolean> {
const uri =
`mongodb://${encodeURIComponent(user)}:${encodeURIComponent(password)}@${this.conn.host}:${this.conn.port}` +
`/${encodeURIComponent(database)}?authSource=${encodeURIComponent(database)}&serverSelectionTimeoutMS=10000`;
let stdout: string;
try {
({ stdout } = await run(
"mongosh",
[uri, "--quiet", "--eval",
"print(EJSON.stringify(db.runCommand({ connectionStatus: 1 }).authInfo.authenticatedUserRoles))"],
{ timeout: 30_000 },
));
} catch (err) {
const text = `${(err as { stderr?: string }).stderr ?? ""}${(err as { stdout?: string }).stdout ?? ""}`;
if (/Authentication failed|AuthenticationFailed/i.test(text)) return false;
throw err;
}
const roles = JSON.parse(stdout.trim()) as { role: string; db: string }[];
return roles.some((r) => r.role === "dbOwner" && r.db === database);
}
/** Drop a database and its owning user, idempotently. Dropping the database evicts its data; the
* user is removed first so a re-grant of the same login starts clean. */
async dropDatabaseAndUser(database: string, user: string): Promise<void> {