Sign in on the mail server's own page (OAuth with PKCE), sessions hold tokens; tenants on every edition

Contract C-8 and C-10: with OAUTH_CLIENT_SECRET set, sign-in goes through the
server's page and the session keeps sealed tokens, renewed before they expire,
instead of a password. Push keeps a credential that renews itself. A password
change signs the session out, since the server revokes its tokens. The mock
answers OAuth for tests and development. Eleven new strings, in all nine
catalogues.
This commit is contained in:
2026-09-18 15:30:37 -07:00
parent c118184975
commit 8857bdac30
24 changed files with 1058 additions and 74 deletions
@@ -21,7 +21,7 @@ const PERMS = ["sysTenantGet", "sysTenantQuery", "sysTenantCreate"];
const signIn = (edition: string | null, enterpriseNotices = false) =>
useSession.setState({ session: { capabilities: {}, accounts: {}, primaryAccounts: {}, username: "[email protected]", ihasmail: { permissions: PERMS, server: { edition, enterpriseNotices } } } as unknown as JmapSession });
/** Tenants are managed on Enterprise only; anywhere else the page is the notice and nothing more. */
/** INBUXA: tenants are managed on every server, whatever edition it reports. */
describe("the Tenants page", () => {
let host: HTMLDivElement;
let root: Root;
@@ -43,25 +43,17 @@ describe("the Tenants page", () => {
host.remove();
});
for (const edition of ["community", "oss", null]) {
it(`shows only the notice on ${edition ?? "a server that reports no edition"}`, async () => {
for (const edition of ["community", "oss", null, "enterprise"]) {
it(`lists and offers tenants on ${edition ?? "a server that reports no edition"}, with no Enterprise notice`, async () => {
signIn(edition);
await render();
expect(host.querySelector(".admin-notice.warn")?.textContent).toContain("Tenants are a Stalwart Enterprise feature");
expect(host.textContent).not.toContain("New tenant");
expect(host.querySelector('input[type="search"]')).toBeNull();
expect(host.querySelector(".admin-table")).toBeNull();
expect(api.queryTenants).not.toHaveBeenCalled();
expect(host.querySelector(".admin-notice")).toBeNull();
expect(host.textContent).toContain("New tenant");
expect(host.querySelector(".admin-table")?.textContent).toContain("Acme Corp");
expect(api.queryTenants).toHaveBeenCalled();
});
}
it("lists and offers tenants on Enterprise, and does not say they are Enterprise", async () => {
signIn("enterprise");
await render();
expect(host.querySelector(".admin-notice")).toBeNull();
expect(host.textContent).toContain("New tenant");
expect(host.querySelector(".admin-table")?.textContent).toContain("Acme Corp");
});
});
describe("the Tenants page where the installation asks for Enterprise notices", () => {