Review fixes: holds and create agree, and no password leaves a check
create re-enables what holds refuses (mssql login, mosquitto client, mailu mailbox, gitea user) and clears an expired postgres password, so no disabled account loops. mssql and mongodb checks take the password from the environment, never argv; mosquitto_ctrl failures no longer repeat -P. mosquitto reads 'could not ask' as an error, not absence. mailu checks existence and enabled only: its imap passdb cannot verify a password. mssql checks the user's SID; gitea pages teams at 50.
This commit is contained in:
+12
-20
@@ -127,7 +127,9 @@ export class MailuClient {
|
||||
}
|
||||
|
||||
async changePassword(email: string, password: string): Promise<void> {
|
||||
await this.api("PATCH", `/user/${encodeURIComponent(email)}`, { raw_password: password });
|
||||
// enabled: a disabled mailbox is what the provisioner's check reports as lost, so applying the
|
||||
// mesh's password again also enables it; otherwise the two would disagree for ever.
|
||||
await this.api("PATCH", `/user/${encodeURIComponent(email)}`, { raw_password: password, enabled: true });
|
||||
}
|
||||
|
||||
async deleteUser(email: string): Promise<void> {
|
||||
@@ -135,34 +137,24 @@ export class MailuClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a mailbox exists, is enabled, and accepts exactly this password. Read-only. Existence
|
||||
* from the admin API; the password from `doveadm auth test` in the imap container, which is how
|
||||
* the mail server itself authenticates, and which exits 77 for a refused login. The password
|
||||
* reaches doveadm through the exec's environment, never the host's argv. An unreachable API or
|
||||
* container rejects (novox/hq issue 120).
|
||||
* Whether a mailbox exists and is enabled. Read-only, through the admin API.
|
||||
*
|
||||
* **The password is not checked.** Mailu authenticates in its admin service, behind the front;
|
||||
* the imap server's own password database accepts any password from Mailu's subnet, so asking it
|
||||
* (`doveadm auth test`) proves nothing, or refuses everyone. A lost or disabled mailbox is caught;
|
||||
* a password changed by hand is not (novox/hq issue 120).
|
||||
*/
|
||||
async holdsUser(email: string, password: string): Promise<boolean> {
|
||||
async holdsUser(email: string): Promise<boolean> {
|
||||
const res = await fetch(`${this.baseUrl}/user/${encodeURIComponent(email)}`, {
|
||||
headers: { Authorization: this.apiKey, Accept: "application/json" },
|
||||
});
|
||||
if (res.status === 404) return false;
|
||||
if (!res.ok) throw new Error(`Mailu API GET /user/${email}: ${res.status} ${await res.text()}`);
|
||||
const user = (await res.json()) as { enabled?: boolean };
|
||||
if (user.enabled === false) return false;
|
||||
try {
|
||||
await run(
|
||||
"docker",
|
||||
["exec", "-e", "MESH_USER", "-e", "MESH_PW", this.imapContainer,
|
||||
"sh", "-c", 'doveadm auth test "$MESH_USER" "$MESH_PW"'],
|
||||
{ env: { ...process.env, MESH_USER: email, MESH_PW: password }, timeout: 30_000 },
|
||||
);
|
||||
return true;
|
||||
} catch (err) {
|
||||
if ((err as { code?: number }).code === 77) return false;
|
||||
throw err;
|
||||
}
|
||||
return user.enabled !== false;
|
||||
}
|
||||
|
||||
|
||||
async listAliases(): Promise<MailuAlias[]> {
|
||||
const aliases = await this.api<any[]>("GET", "/alias");
|
||||
return (aliases ?? []).map((a) => ({
|
||||
|
||||
@@ -65,6 +65,6 @@ runProvisioner("smtp", {
|
||||
// Asked every minute by the harness: whether the backend still holds this consumer exactly as
|
||||
// the mesh gave it, so a login lost behind the provisioner's back is made again (novox/hq issue 120).
|
||||
async holds(p: Provision): Promise<boolean> {
|
||||
return mailu.holdsUser(addressOf(p), p.password);
|
||||
return mailu.holdsUser(addressOf(p));
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user