Every build writes the exact source it was built from, uncommitted work and new files included, as source.tar.gz next to the app, named after that tree. Docker builds, which have no git, pack the build context and name it by a hash of its files. The sign-in page and Settings > About link to it instead of a repository that can drift. What users, operators and packagers see no longer names the upstream server: - interface text, in all nine catalogues, with a token-session line for Security; - server messages; - the settings, now MAIL_SERVER_URL, MAIL_SERVERS_FILE, ADMIN_URL and MAIL_SERVER_FOLLOW_ADVERTISED_URLS, and mail-servers.example.json; - the Tenants notice, which is gone; - the README, CONTRIBUTING and SECURITY. ihasmail's own FEATURES, KNOWN-ISSUES and ROADMAP stay with public ihasmail, and INBUXA.md is folded into the README.
73 lines
3.2 KiB
TypeScript
73 lines
3.2 KiB
TypeScript
import { test, before, after } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
/**
|
|
* ihasmail requires Stalwart 0.16 or newer. Sign-in is where that is enforced,
|
|
* and it matters that it is enforced *there*: the alternative is signing
|
|
* someone in and letting Files, the account locale and self-service
|
|
* credentials each fail in their own way, with nothing to connect the three or
|
|
* to say what the real problem is.
|
|
*
|
|
* The refusal also has to keep two things apart that look the same from the
|
|
* outside. Bad credentials are a 401 the user can fix by typing again; an
|
|
* unsupported server is not, and telling someone their password is wrong when
|
|
* it is not would send them round in circles.
|
|
*/
|
|
|
|
const PORT = 18799;
|
|
process.env.MOCK_PORT = String(PORT);
|
|
process.env.MOCK_USER = "[email protected]";
|
|
process.env.MOCK_PASS = "demo-password";
|
|
process.env.MOCK_NO_REGISTRY = "1"; // a server without urn:stalwart:jmap
|
|
process.env.MAIL_SERVER_URL = `http://127.0.0.1:${PORT}`;
|
|
process.env.APP_SECRET = "test-secret-for-login-guard";
|
|
|
|
const mock = await import("./mock/index.js");
|
|
const { createApp } = await import("./app.js");
|
|
|
|
const app = createApp();
|
|
const HEADERS = { "content-type": "application/json", "x-requested-with": "ihasmail" };
|
|
|
|
async function login(body: unknown): Promise<{ status: number; body: any; setCookie: string | null }> {
|
|
const res = await app.request("/api/auth/login", { method: "POST", headers: HEADERS, body: JSON.stringify(body) });
|
|
const text = await res.text();
|
|
return { status: res.status, body: text ? JSON.parse(text) : null, setCookie: res.headers.get("set-cookie") };
|
|
}
|
|
|
|
before(() => {
|
|
assert.equal(process.env.MOCK_NO_REGISTRY, "1");
|
|
});
|
|
|
|
after(() => {
|
|
(mock as { server?: { close(): void } }).server?.close();
|
|
});
|
|
|
|
test("a server without the registry is refused, with good credentials", async () => {
|
|
const res = await login({ username: "[email protected]", password: "demo-password" });
|
|
assert.equal(res.status, 501);
|
|
assert.equal(res.body.error, "unsupported_server");
|
|
});
|
|
|
|
test("the message says the credentials were fine, and that the server isn't supported", async () => {
|
|
const { body } = await login({ username: "[email protected]", password: "demo-password" });
|
|
// Someone hitting this has typed a correct password. Saying so is the
|
|
// difference between "wrong server" and "try your password again".
|
|
assert.match(body.message, /credentials are fine/i);
|
|
assert.match(body.message, /isn't one this webmail supports/);
|
|
});
|
|
|
|
test("no session is minted for a server we cannot talk to", async () => {
|
|
// A cookie here would leave a signed-in session against a server every
|
|
// other request is going to fail on.
|
|
const res = await login({ username: "[email protected]", password: "demo-password" });
|
|
assert.equal(res.setCookie, null);
|
|
});
|
|
|
|
test("bad credentials on such a server are still a 401, not the server error", async () => {
|
|
// The upstream session request fails first, and that answer is the honest
|
|
// one: we never got far enough to learn what the server supports.
|
|
const res = await login({ username: "[email protected]", password: "wrong-password" });
|
|
assert.equal(res.status, 401);
|
|
assert.notEqual(res.body.error, "unsupported_server");
|
|
});
|