Look for Stalwart's capability where Stalwart advertises it
Self-service credentials, the About page and Files all keyed off `urn:stalwart:jmap`, and all three looked for it in the session-level `capabilities`. Stalwart has never put it there. `Session::new` builds that list from a fixed set the capability is not part of, in any 0.16.x from 0.16.0 to 0.16.19; it is handed out per-account instead, so it arrives in `primaryAccounts` and in each account's `accountCapabilities`. So every real 0.16 server read as pre-0.16. Password changes, 2FA and app passwords fell back to `POST /api/account/auth`, which 0.16 removed, and reported that the server offers no self-service credential management. About named the wrong generation. Files ran the pre-0.16 path, omitting `nodeType` and listing the tree through get. Look in all three places, on both sides. Two nearby soft spots go with it: a transport error while probing the registry no longer downgrades a server to the legacy path -- which would have posted the current password to an endpoint that is not there -- and a locale request that is merely refused no longer discards a generation the capability had already settled. The mock advertised the capability in the session, which is why no test ever caught this; it now advertises it where the real server does, and validates `using` by the urn rather than by the session, as Stalwart does. Put the old lookup back and nine tests fail. Stalwart still publishes no version number to clients -- VERSION_PUBLIC is a fixed "1.0.0" -- so About continues to report the generation and edition, which are now the right ones.
This commit is contained in:
@@ -17,6 +17,20 @@ function session(caps: string[]): JmapSession {
|
||||
const NEW_SERVER = ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:filenode", "urn:stalwart:jmap"];
|
||||
const OLD_SERVER = ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:filenode"];
|
||||
|
||||
/**
|
||||
* The session a real Stalwart 0.16 sends: `urn:stalwart:jmap` is handed out
|
||||
* per-account and never appears in the session-level capabilities, so a client
|
||||
* that only checks there drops every 0.16 server onto the older code path.
|
||||
*/
|
||||
function realStalwartSession(): JmapSession {
|
||||
return {
|
||||
capabilities: Object.fromEntries(OLD_SERVER.map((c) => [c, {}])),
|
||||
accounts: { a1: { accountCapabilities: { "urn:ietf:params:jmap:filenode": {}, "urn:stalwart:jmap": {} } } },
|
||||
primaryAccounts: { "urn:stalwart:jmap": "a1" },
|
||||
state: "s",
|
||||
} as unknown as JmapSession;
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
client.session = null;
|
||||
});
|
||||
@@ -37,6 +51,16 @@ describe("on Stalwart 0.16 and newer", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("on a real 0.16 session, which advertises per-account only", () => {
|
||||
it("is recognised as 0.16 even though the session capabilities do not say so", () => {
|
||||
client.session = realStalwartSession();
|
||||
expect(client.hasCapability("urn:stalwart:jmap")).toBe(false);
|
||||
expect(supportsNodeType()).toBe(true);
|
||||
expect(queryOmitsDirectories()).toBe(false);
|
||||
expect(directoryCreate(null, "ihasmail")).toEqual({ parentId: null, name: "ihasmail", nodeType: "directory" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("on Stalwart before 0.16", () => {
|
||||
it("never mentions nodeType, in creates or in requested properties", () => {
|
||||
client.session = session(OLD_SERVER);
|
||||
|
||||
Reference in New Issue
Block a user