diff --git a/CHANGELOG.md b/CHANGELOG.md index c47339da..c2f13943 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,17 @@ The published image tags: `latest` follows the most recent release below, ## Unreleased + +**Single sign-on can be the only way in.** A new setting under Admin → SSO, **Single sign-on only** +(or `OIDC_DISABLE_PASSWORD_LOGIN=true` in the environment), hides the password form and makes the +server refuse a password sign-in, so everybody comes in through the provider. It is in force only +while a provider is set up, so a provider that was removed cannot leave an install with no way in, +and the box can be ticked only by an administrator who has signed in through the provider +themselves. If the provider is down, `php bin/console app:sso:allow-password-login` brings the form +back. App passwords for mail clients and the phone apps, and sessions that are already open, are not +affected. Off by default. Schema change: one boolean column on `oidc_config`, false for every +existing install. [#169](https://github.com/karatektus/pl_mail/issues/169). + **A reply threads for the person who gets it.** A reply sent from plMail, in the web composer or over JMAP, reached the recipient without `In-Reply-To` or `References`, so Outlook, Gmail and Apple Mail showed it as a new conversation instead of part of the one it answered. The draft always held @@ -124,19 +135,14 @@ a sandbox that sends no session cookie, so each image was answered with a redire page. They now load through a signed link (`/mail/inline/{id}`) that only answers for an image of the message being read, and nothing that is not a picture. The link stops working after a day or two, so one that leaks through a forward or a log does not open the image for good; the message -signs a fresh one each time it is shown. Images labelled `application/octet-stream` or -`image/octet-stream` load the same way when their bytes are a picture. Messages already in the +signs a fresh one each time it is shown. Many mailers label an inline image +`application/octet-stream`, or `image/octet-stream`, which is not a registered type at all and is +what Vaultwarden's notification mails put on their logo, and such a part was served as a download. +For those labels only, the file's own bytes now decide: a PNG, JPEG, GIF or WebP is shown, while +SVG, HTML and anything unrecognised are still downloads. A remote image with that label is +identified the same way by the image proxy and served under its real type. Messages already in the mailbox are fixed too, no backfill needed. No schema change. - -**Single sign-on can be the only way in.** A new setting under Admin → SSO, **Single sign-on only** -(or `OIDC_DISABLE_PASSWORD_LOGIN=true` in the environment), hides the password form and makes the -server refuse a password sign-in, so everybody comes in through the provider. It is in force only -while a provider is set up, so a provider that was removed cannot leave an install with no way in, -and the box can be ticked only by an administrator who has signed in through the provider -themselves. If the provider is down, `php bin/console app:sso:allow-password-login` brings the form -back. App passwords for mail clients and the phone apps, and sessions that are already open, are not -affected. Off by default. Schema change: one boolean column on `oidc_config`, false for every -existing install. [#169](https://github.com/karatektus/pl_mail/issues/169). +[#213](https://github.com/karatektus/pl_mail/issues/213). **A deleted label no longer comes back after the next sync.** On a plain IMAP account, and for a folder on Outlook, deleting a label left its folder on the mail server, and the next sync made the @@ -152,17 +158,6 @@ reached Outlook at all, and now does; labels nested under a Gmail label are dele it. No schema change. [#40](https://github.com/karatektus/pl_mail/issues/40). -**Inline images labelled `application/octet-stream` show up.** Many mailers do not say what an -inline image is — they label it `application/octet-stream`, or `image/octet-stream`, which is not -a registered type at all and is what Vaultwarden's notification mails put on their logo — and the -attachment route served every such part as a download, so the image never appeared in the message. For those labels only, the file's own -bytes now decide, held to the same allow-list as everything else: a PNG, JPEG, GIF or WebP is -served inline, while SVG, HTML and anything unrecognised are still downloads. The image proxy -already did this for unlabelled remote images and now shares the same check, so a remote image -labelled `image/octet-stream` is now also identified from its bytes and served under its real -type, rather than passed through under the label it came with. No schema change. -Part of [#213](https://github.com/karatektus/pl_mail/issues/213). - **A mail can go to Bcc recipients only.** Sending with nobody in To was refused with "Add at least one recipient before sending", even with addresses in Cc or Bcc: the window counted all three, but the server only counted To. It now counts all three as well. A mail with no recipient at all is diff --git a/public/whats-new/sso-only.png b/public/whats-new/sso-only.png new file mode 100644 index 00000000..fca4ddad Binary files /dev/null and b/public/whats-new/sso-only.png differ diff --git a/tests/e2e/whats-new-screenshots.spec.ts b/tests/e2e/whats-new-screenshots.spec.ts index ad805ce9..f798fb9b 100644 --- a/tests/e2e/whats-new-screenshots.spec.ts +++ b/tests/e2e/whats-new-screenshots.spec.ts @@ -188,4 +188,49 @@ test.describe("What's new screenshots, as an administrator", () => { await capture(page, "sso", panel, 560); }); + + /** + * Single sign-on only (#169): the login page with the provider's button and + * no password form, which is the feature as everybody else meets it. + * + * The provider is saved through Admin → SSO, since the client secret is + * stored encrypted and SQL cannot write it. The box itself is set in the + * database: the form only lets an administrator tick it who has signed in + * through that provider, and this stack has no provider to sign in at. + * Undone in `finally`, so the stack is left with password sign-in and no + * provider, as the other pictures and the regression suite expect. + */ + test("sso-only", async ({ page }) => { + await login(page, TEST_DEMO_ADMIN.email, TEST_DEMO_ADMIN.password); + await page.goto("/admin?section=sso"); + + const panel = page.locator("turbo-frame#admin-sso"); + await panel.getByLabel("Issuer").fill("https://auth.example.com/application/o/plmail/"); + await panel.getByLabel("Client ID").fill("plmail"); + await panel.getByLabel("Client secret").fill("not-a-real-secret"); + await panel.getByLabel("Name on the button").fill("Authentik"); + await panel.getByRole("button", { name: "Save", exact: true }).click(); + await expect(panel.getByLabel("Issuer")).toHaveValue("https://auth.example.com/application/o/plmail/"); + + try { + consoleCommand(`dbal:run-sql "UPDATE oidc_config SET password_login_disabled = true"`); + await page.context().clearCookies(); + await page.goto("/login"); + + await expect(page.getByLabel("Password")).toHaveCount(0); + await expect(page.getByText("Authentik")).toBeVisible(); + + // With a margin of the page around it: cropped to the box, the + // logo touches the top edge and the card's bottom border is lost. + const box = await page.locator("div.max-w-sm").first().boundingBox(); + expect(box, "sso-only: no login card").not.toBeNull(); + await page.screenshot({ + path: `${OUT}/sso-only.png`, + clip: { x: box!.x - 32, y: box!.y - 32, width: box!.width + 64, height: box!.height + 64 }, + }); + } finally { + consoleCommand("app:sso:allow-password-login"); + consoleCommand(`dbal:run-sql "DELETE FROM oidc_config"`); + } + }); });