Commit Graph
6 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 5 ca382df0a3 fix(roles): realign four admin passwords that never survived a rotation
These four read a credential that rotates with --schema, so unlike the roles
3c72c2002a moved onto platform_administrator they were already locked out
before that migration. Each wrote the password only at install time.

mediawiki passes --force to createAndPromote, which upstream documents as
'If account exists already, just grant it rights or change password'. The
password also leaves the host argv, which it previously entered directly. The
realign is gated on a new MEDIAWIKI_LOCAL_PASSWORD_LOGIN: with SSO on, oidc.php
sets wgPluggableAuth_EnableLocalLogin false, PluggableAuth then unregisters the
local password provider, and changeAuthenticationData answers 'ignored', which
createAndPromote turns into a fatal. Forcing the password unconditionally would
have broken every SSO-enabled deploy. Where local login is on the task reports
changed every run, because MediaWiki re-hashes unconditionally and offers no
verify primitive; a host-side digest marker was rejected because it would
survive a database reset and then skip the account creation.

pgadmin never re-read PGADMIN_DEFAULT_PASSWORD: the image exports it only when
/var/lib/pgadmin/pgadmin4.db is absent, and that path is a named volume. A new
manager-ops task waits for health and calls setup.py update-user. --admin is
mandatory there, because update_user reads data['role'] unguarded, and failures
such as 'User not found' exit 0, so the task detects success positively by the
User Details table. meta/services.yml gains name: pgadmin, without which
container_address hard-fails; it renders the same string entity_name already
produced, so no container is renamed.

suitecrm has no console command for an existing user's password, so the script
writes users.user_hash. SuiteCRM stores bcrypt over the md5 digest, not over the
plaintext, so hashing the plaintext would have locked the administrator out.

wordpress replaces the wp user update --prompt its own TODO.md proposed with
wp eval-file on stdin: the CLI cannot distinguish changed from unchanged and
trims the line it reads, while wp_check_password and wp_set_password do neither.
That TODO entry is removed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-12 21:24:19 +02:00
kevinveenbirkenbach 068a09cd1b Merge branch 'feature/designer' into feature/store-i18n 2026-10-08 22:58:05 +02:00
kevinveenbirkenbachandClaude Opus 5.5 240d27ab21 feat(web-app-wordpress): carry the palette through WordPress's own mechanisms
files/php/design.php, run through wp eval-file from tasks/08_design.yml,
sets the title, site icon and site logo as options and writes a
wp_global_styles post for Twenty Twenty-Five whose presets point at the
design tokens, so the site follows the palette in light and dark
without a role stylesheet.

The new mu-plugin infinito-design registers an admin color scheme
infinito, gives it to new accounts and moves accounts still on the
default scheme once; a scheme a user picked is kept. It also styles the
sign-in page and hands the token sheet to the block editor canvas. The
five sheets under files/css map WordPress's compiled admin colors onto
the tokens, since the color scheme API only repaints the chrome.

The spec signs in through the WordPress form when SSO is off, because
the local administrator does not share the platform password, and
asserts tokens, admin chrome, editor, media grid, sign-in page, focus
rings, logo and title before capturing a gallery of site and wp-admin.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 22:13:05 +02:00
kevinveenbirkenbachandClaude Opus 5 39d8de790b test(web-app-wordpress): cover the OIDC payload instead of exempting it
Two lint suppressions shipped with the multisite OIDC work. Both are replaced
by the thing they were standing in for.

settings.php claimed an exemption from the mirrored-test rule because every
call it makes belongs to the WordPress runtime. The six functions stub in a
few lines, so the claim does not hold. SettingsTest.php declares WP_CLI and
the globals over a state holder and includes the real payload per case:

  * a single site is written without switching blogs
  * every site of a network is written, switch_to_blog paired with restore
  * a site whose write does not persist is named by blog id
  * every site is still visited before that failure is reported
  * a payload that is not a map halts, and so does an absent one

The fourth case is the one worth having: a `break` on the first failure would
pass the other three and silently stop writing.

Neither existing house pattern fits. `eval` suits the tag-less tinker snippet
of web-app-akaunting, `proc_open` the standalone script of web-app-joomla;
this is a tagged file that needs a runtime, so it is stubs plus `include`,
which also avoids the `nocheck: eval` the akaunting test carries.

The installer test's cache-read exemption rested on a cached read serving a
stale call log. Measured, that risk is confined to the suite and sits around
3e-13: the log lives in a fresh TemporaryDirectory per run, mkdtemp verifies
non-existence, and each path is read once. What complying actually costs is
roughly 25 dead entries in an 8192-slot LRU, so the read goes through
utils.cache.files.read_text.

Proven by `make test-unit-php`: 32 tests, 68 assertions. The first version of
this test passed nothing at all -- it sits one directory deeper than the
other PHP tests, so dirname(__DIR__, 6) resolved to tests/ and the include
found no file. Without the test that would have stayed invisible.

The suite drives stubs, so it proves the payload's control flow and not that
WordPress behaves as stubbed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-06 22:54:27 +02:00
kevinveenbirkenbachandClaude Opus 5 e1901fb88e fix(web-app-wordpress): write the OIDC settings to every site of a network
Variant 1 of run 37440146966 leaves the persona on the login page:

    Expected substring: "/wp-admin"
    Received string:    "https://blog.main.infinito.test/wp-login.php"

The rescue bundle shows 62 requests to /wp-login.php, all 200, and not one
OIDC request. Every login asset loads, so the page renders in full and simply
offers no SSO entry point; the flow never reaches Keycloak.

Variant 1 is the Multisite variant, and that is the whole difference. The
plugin reads its configuration with get_option, not get_site_option:

    includes/openid-connect-generic-option-settings.php:125
    $this->values = (array) get_option( self::OPTION_NAME, $this->default_settings );

The option is therefore per site. The addon stored it once, so a subsite read
an empty openid_connect_generic_settings and rendered a login page without an
OIDC entry point. Variants 0 and 2 are single-site, which is why only #1
failed.

Three shell tasks collapse into one PHP payload, following the pattern
ai-engine-stack-host.yml already uses in this role: the settings ride as
base64 in an env var and the body streams on stdin into `wp eval-file -`.
The payload walks the network with get_sites()/switch_to_blog() instead of
enumerating sites in the shell and passing --url per site, which removes the
nested quoting that broke this task once before (03ed52eea5).

The task that seeded a PHP-serialized empty array is gone: update_option
creates an absent option by itself.

Writing to a site whose option does not persist used to be silent. The
payload now reads each site back and names the blog ids that stayed empty, so
a failed write fails the deploy instead of producing a login page nobody can
use.

The settings carry OIDC.CLIENT.SECRET and the three old tasks had no no_log
at all, so the base64 secret reached every deploy log; the task now masks when
MASK_CREDENTIALS_IN_LOGS is on.

Static only. No deploy has run this.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-06 20:47:00 +02:00
kevinveenbirkenbachandClaude Opus 5 dfe7a7fdd4 refactor(roles): sort role scripts into per-language folders
The merged branch moved every role-local script out of files/ into a folder
named for its language (files/python/, files/php/, files/shell/), but the
moves lost their staging and never landed. HEAD still carried the old paths
while the worktree had the new ones.

Move the 24 remaining scripts and update the six places that resolve them:
the mcp-adapter instance/isolation/probe tasks, the litellm key task, the
adapter README and the endpoint-path lint. Every changed line in those
files is a path.

Also drop roles/sys-svc-compose/files/swarm/stack_ready.sh, a stale
duplicate of the copy already tracked under files/shell/swarm/.

The added comments are the branch's own `nocheck: mirrored-unit-test`
directives, which state why a script whose measurement only exists inside
its container cannot have a mirrored unit test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 12:00:59 +02:00