email._compute('domain') returned variables['DOMAIN_PRIMARY'] verbatim. When
DOMAIN_PRIMARY held an unrendered template (e.g. the group_vars default
"{{ lookup('env','DOMAIN') | default('infinito.localhost', true) }}"), the raw
expression leaked into rendered templates. In discourse's containers/discourse.yml
the literal "{{ ..." opened a YAML flow mapping and made Ruby Psych fail with
"did not find expected ',' or '}' while parsing a flow mapping", aborting
`./launcher rebuild discourse` in CI.
Route every _compute() result through the same _render() helper that already
covered SYSTEM_EMAIL_* overrides, and add a regression test that exercises a
templated DOMAIN_PRIMARY default.
Rewrites both admin and biber OIDC scenarios to end in a clearly logged-out
state: bbbLogout hits the universal /logout endpoint (intercepted by the
app's nginx vhost and proxied to web-svc-logout to terminate Greenlight and
Keycloak in one hop), then assertLoggedOut polls /rooms for the
unauthenticated sign-in affordance. Scenarios renamed from "...lands on
greenlight" to "...OIDC login and logout" to reflect the final-state
coverage required by the role-spec MUSTs.
Expose compose.services.css.enabled as CSS_ENABLED in the dashboard
playwright env and have the "dashboard loads core css, javascript,
simpleicons, and logo assets" test call `test.skip(\!cssEnabled, …)`
when the shared CSS service is disabled for the deployment (e.g.
FULL_CYCLE runs with SERVICES_DISABLED="css …"), so the scenario no
longer flags a failure on an intentionally absent asset pipeline.
Expose compose.services.{oidc,ldap}.enabled as NEXTCLOUD_OIDC_ENABLED and
NEXTCLOUD_LDAP_ENABLED in the Playwright env, and derive a single
`nextcloudLoginFlavor` in the spec:
- OIDC + LDAP -> "oidc_login" (pulsejet/nextcloud-oidc-login,
auto_redirect hands straight to Keycloak)
- OIDC only -> "sociallogin" (nextcloud/sociallogin shows a
"Log in with Keycloak" entry first)
- no OIDC -> "native" (no Keycloak handoff; NC credential form
filled with the direct admin password)
loginToStandaloneNextcloud now builds its initial-state candidate list
per flavor instead of accepting every possible login UI shape, and the
native flavor uses NEXTCLOUD_DIRECT_LOGIN_PASSWORD rather than the
Keycloak user password.
Verified via reuse-kept redeploy (make deploy-reuse-kept-apps
APPS=web-app-nextcloud SERVICES_DISABLED="css matomo email"): both
Playwright scenarios pass (2 passed, 26.3s).
lookup('oidc_flavor') was reading applications[APP].compose.services
straight off variables["applications"], which in Ansible is the
pre-merge override slice — role defaults from config/main.yml (like
compose.services.ldap.enabled: true) are not yet visible there. As a
result ldap.enabled resolved to the lookup's default False on every
invocation and the Nextcloud flavor silently fell through to
"sociallogin" even when LDAP was enabled.
Switch the lookup to get_merged_applications(), matching the path
lookup('config') already uses, so role-level defaults and inventory
overrides are both visible. Tests now patch that merge function rather
than inject raw variables.
Verified live against the reuse-kept stack: lookup('oidc_flavor')
resolves to 'oidc_login', plugins.oidc_login.enabled=True and
plugins.sociallogin.enabled=False.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Fixes first-login flow for OIDC-provisioned users in Odoo 19. Upstream
auth_oauth._auth_oauth_signin delegates auto-provisioning through
auth_signup.signup() -> _create_user_from_template() which calls
template_user.copy() inside a savepoint. When that savepoint flushes,
ir_attachment._check_contents() walks env.user._get_group_ids(), and on
the unauthenticated OAuth callback env.user is an empty recordset, so
ensure_one() raises and the controller surfaces AccessDenied with no
traceback.
Overrides _auth_oauth_signin to bypass the template-copy path entirely
and create the user directly via sudo().create() with base.group_user,
then flushes pending recomputes under the sudo env before the
controller's cr.commit() runs.
Also makes the OIDC UID claim configurable via ODOO_OIDC_UID_FIELD
(default: preferred_username) so Odoo aligns with Mastodon, Pixelfed,
EspoCRM, and Taiga in the infinito.nexus stack instead of keying on
Keycloak's opaque sub UUID.
Playwright spec updated to tolerate Odoo 19's new .o_login_auth
container for the SSO provider list (older .o_auth_oauth_providers
selector still accepted).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Reorder `Bash(docker exec *)` into ASCII position and add a single space
before trailing `*` on `sudo`/`docker run`/`docker * run` entries so the
pattern matches invocations with explicit args (bare-verb calls remain
covered via other allow entries).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace `git ls-files` with an inline `.gitignore` parser + `rglob` walk so
the lint runs inside the `make exec` container where `.git/` isn't mounted.
The matcher covers the pattern shapes used in this repo's `.gitignore`
(bare basenames, `prefix/*`, `*.ext`, path-qualified exact matches).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Playwright drives Decidim's first-time OIDC registration (TOS + newsletter
modal) so the biber user is created on first login and reused idempotently
on subsequent runs. Ruby snippets previously inlined in tasks/01_setup.yml
and patch.py are now separate .rb files under files/ruby/, copied into the
image alongside patch.py. patch.py now also fixes Decidim 0.31.3's broken
omniauth_registrations_path view helper (both new.html.erb and
new_tos_fields.html.erb).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Relocates the role-local test file under `tests/unit/roles/web-app-decidim/`
so the new `test_*.py`-location lint has no offenders. Required for the
pairing commit b67b39221; got unstaged by pre-commit hook churn the
first time around.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a repository lint that walks `git ls-files -co --exclude-standard`
and fails if any `test_*.py` lives outside the top-level `tests/` dir,
so .gitignored artefacts stay excluded. Moves the existing decidim role
test into `tests/unit/roles/web-app-decidim/` and rewrites it as
`unittest.TestCase` so `unittest discover` actually executes it.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Shorten the module name now that it backs more than just lookup plugins
(validator CLI, tests, dashboard role). Updates all 26 import sites and
adds a unit test covering the pure helpers.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replaces 15 per-shape Bash entries that existed as sandbox auto-allow
workarounds with a single Bash(*) wildcard. Gating responsibility moves
entirely to ask (mutating gh/docker/git verbs) and deny (structurally
destructive shapes). Documentation updated across settings.md,
security.md, and sandbox.md to reflect the new model; em-dash clause
separators removed throughout per documentation.md style rules.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add tests/lint/ansible/roles/test_web_app_email_integration.py which:
- Emits a GHA warning annotation (via utils.annotations.message) for
each web-app-* role that does not call lookup('email', application_id)
and does not carry an explicit opt-out block.
- Hard-fails when a role DOES call the email lookup but its
compose.services.email is missing enabled: true or shared: true.
- Silencer format: `# noqa: email` on the line directly above an
`email:` block under compose.services with enabled: false + shared: false.
Opt out 13 roles that have no user-facing mail surface:
bridgy-fed, chess, dashboard, littlejs, mig, mini-qr, navigator,
oauth2-proxy, phpldapadmin, phpmyadmin, roulette-wheel, sphinx, yourls.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Apps with native SMTP env-var support now consume `lookup('email', application_id)`
behind `email.enabled` guards:
- web-app-openwebui: SMTP_HOST/PORT/USERNAME/PASSWORD/FROM_ADDRESS
- web-app-flowise: SMTP_HOST/PORT/USER/PASSWORD, SENDER_EMAIL
- web-app-pretix: PRETIX_MAIL_FROM/HOST/PORT/USER/PASSWORD/TLS/SSL
- web-app-gitlab: new GITLAB_OMNIBUS_SMTP list appended to GITLAB_OMNIBUS_ALL
openwebui and flowise also declare `compose.services.email` (gitlab and pretix
already did). gitlab and pretix map start_tls/tls from the lookup onto their
respective vendor TLS/SSL switches.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Moves the SYSTEM_EMAIL_* resolution logic out of group_vars/all/03_email.yml
into a dedicated Ansible lookup plugin, so every role template reads outgoing
mail settings via lookup('email') (global) or lookup('email', application_id)
(per-app). Per-app compose.services.email is merged on top of the resolved
defaults; SYSTEM_EMAIL_* variables remain usable as an inventory escape hatch.
Also splits the email docs: administrator-facing configuration stays under
docs/administration/configuration/, plugin contract and extension rules move
to docs/contributing/design/services/email.md. services.md is relocated to
services/base.md so the services/ subtree can host per-service design pages.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>