Files
computer-playbook/roles/web-app-listmonk/TODO.md
kevinveenbirkenbachandClaude Opus 5 d0ec15cc05 fix(listmonk): recreate the container so its settings are ever loaded
Every entry in LISTMONK_SETTINGS was one boot late. Listmonk reads settings
into koanf once at startup (cmd/main.go:186 initSettings) and builds the media
store from them (:205); the only reload trigger is SIGHUP (:315). The role
writes them with a direct UPDATE from 02_manager_ops.yml:34, after the single
`notify: compose up` in the whole role has already been flushed at :8. A DB
write reaches neither koanf nor the settings API, and `changed_when: false`
meant the task could not notify a handler even if one existed.

It never self-healed either: notify fires only on a changed config.toml, which
on the second run is identical, so no recreate. In the CI shape -- fresh stack,
one deploy, then Playwright -- the settings were guaranteed inactive.

What was therefore never in effect: upload.provider=s3 and the whole
upload.s3.* block (uploads went to the container filesystem, not the bucket),
security.oidc (so /admin/login rendered no SSO control at all), smtp, and
app.root_url -- which is why the deployed instance's public pages linked to
http://localhost:9000.

Proven against listmonk/listmonk:v6.2.0 with a throwaway Postgres: with the
settings written and no reload, /admin/login has no OIDC form; after
`docker kill --signal HUP` the same URL renders
`<form method="post" action="/auth/oidc">`. `container recreate` is the
existing shared handler, already used for exactly this in
web-app-peertube/tasks/oidc/enable.yml.

The OIDC control is still not reachable by the shared persona helper after the
reload: clickOidcLoginLink prefers the strict locator, and Listmonk's native
submit button's accessible name is exactly "Login" while the OIDC button is
"Login with <name>" -- so the strict matcher takes the wrong control. There is
no setting that hides the native form; cmd/auth.go renderLoginPage hardcodes
PasswordEnabled. appearance.public.custom_js is a real v6.2.0 setting, injected
into /admin/login and served same-origin, so a few lines of JS submit the OIDC
form and the second hop disappears. The shared helper stays untouched, which
keeps the 61 other roles that route through it out of this.

Measured with the helper's own source driving a real browser: no reload ->
no form; reload alone -> reached: false; reload plus custom_js -> reached: true.

The administrator persona is unblocked accordingly. biber stays blocked, and
for the third distinct reason this role has been given: Listmonk matches OIDC
users by e-mail and provisions no biber account, cmd/auth.go OIDCFinish 404s
unless security.oidc.auto_create_users is set, and createOIDCUser needs a real
default_user_role_id -- the seeded database holds only Super Admin.

Three nocheck rationales in playwright.env.j2 asserted things that are false:
that mail delivery is "covered by the sys-svc-mail integration tests" (no such
tests exist; the coverage is in web-app-mailu's own spec), and twice that the
CSP helper asserts the injector origin (personas/utils/csp.js:96 bails with
`if (!host) continue`, and no consumer role renders CSS_BASE_URL). Rewritten to
what is actually true.

Not fixed here, recorded in TODO.md: security.enable_captcha,
security.captcha_key and security.captcha_secret do not exist in v6.2.0 -- the
live table carries one security.captcha object -- so all three UPDATEs match
zero rows and hCaptcha has never been active. Repairing that would switch a
captcha on for the public subscription form, which is a behaviour change nobody
asked for.

Unverified: the logout leg. Whether inAppLogout finds Listmonk's Vue user menu,
and whether assertCspInjections passes against the admin SPA, has not been run.
The first deploy settles both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 20:23:50 +02:00

24 lines
1.5 KiB
Markdown

# To-dos
- Three settings keys in `vars/main.yml` are silent no-ops on v6.2.0:
`security.enable_captcha`, `security.captcha_key` and `security.captcha_secret`.
The live `settings` table carries a single `security.captcha` object
(`{"altcha": {…}, "hcaptcha": {"key", "secret", "enabled"}}`), so all three
`UPDATE`s match zero rows and neither `ON_ERROR_STOP` nor the reaper notices.
hCaptcha has therefore never been active, which also makes the
`HCAPTCHA_SERVICE_ENABLED` rationale in `templates/playwright.env.j2` describe
protection that is not there. Rewrite the three keys as one object, then
decide whether a captcha on the public subscription form is wanted at all.
- Unblock the `biber` persona. Listmonk matches OIDC users by e-mail and the
role provisions no biber account; `cmd/auth.go` `OIDCFinish` 404s unless
`security.oidc.auto_create_users` is true, and `createOIDCUser` passes
`UserRoleID` as a plain int, so `default_user_role_id` must name a real role.
The seeded database holds exactly one (`id=1 Super Admin`), so unblocking
biber means first creating a limited Listmonk user role. It buys no coverage
the administrator persona does not already buy.
- Add `files/playwright/test-seaweedfs.js` now that the administrator persona
runs. The only bucket-write surface is the admin Media view; the model is
`roles/web-app-pixelfed/files/playwright/test-seaweedfs.js`. The
`# nocheck: seaweedfs-playwright` marker in `meta/services.yml` comes off with
it.