Third pilot of the cluster rollout — first action that exercises
forbiddenStrategy:'deny' in production. The CONTEXT-stage vorspiel
collapses into one declarative actionEditContext call; the SUBMIT
branch keeps its explicit Guard::deny() (Two-Level-Authorize).
Confirms the analyst hypothesis: actionEditContext with
forbiddenStrategy:'deny' is enough — no helper extension needed.
Helper file stays 0-diff. The 'deny' path was built in step 1 and
verified by testAuthorizeAndExtractCapabilitiesUsesGuardDenyStrategy,
but only now proven against a real production caller.
Three drift decisions from steps 2/3 reproduced:
* notFoundFlashScopeKey: 'role_not_found' to preserve the existing
Flash dedup-scope-key.
* t() consistency: both Flash::success('Role updated', …) calls now
flow through t() — German users see fully translated messages.
* Defensive scope consumption: $canManageAllRoles reads
$tenantScope['scope'] === 'all'. Roles are global, so
$tenantScope['ids'] is intentionally not consumed; an inline
comment documents that.
Roles-specific: PermissionService-warmup absence is preserved (Roles
don't need it — Departments/Tenants do, but cross-cluster consistency
is not a goal here, behavioral identity is).
ActionContextCsrfPairingContractTest now covers three callers
(departments-edit, tenants-edit, roles-edit) and stays green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Consolidates the scattered `array_values(array_unique(array_map('intval',
$x)))` + manual positive-filter pattern behind a single lenient helper
`toIntIds(mixed $value): array` in core/Support/helpers/array.php.
- `RepositoryArrayHelper::sanitizePositiveIds()` now delegates (keeps
strict array-input contract + existing tests intact).
- Drops two private duplicates: `UserProfileViewService::normalizeIds()`
and `AddressBookService::normalizeIds()`.
- Replaces 12 inline occurrences across admin action pages with the
helper, cutting boilerplate by 3-5 lines per site.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move breadcrumb rendering from individual page templates into the core
topbar. Each page now sets $breadcrumbs in its action .php file; the
topbar renders it automatically via the shared partial.
- Remove global back/forward buttons and app-nav-history.js component
- Remove Alt+Arrow keyboard shortcuts for history navigation
- Render breadcrumb in topbar-left section (replaces button area)
- Clean up breadcrumb CSS: context-neutral base (flex, no margin)
- Recalculate sticky titlebar offset in details container
- Migrate all 41 pages (core + helpdesk, audit, addressbook, api-docs)
- Add missing breadcrumbs to addressbook detail view
- Update architecture contract tests (nav-history references removed)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix 5 broken FQCN references (runtime errors) where core pages referenced
non-existent MintyPHP\Service\Audit\AuditMetadataEnricher and SystemAuditService.
Add AuditMetadataEnricherInterface with NullAuditMetadataEnricher fallback so
deactivating the audit module no longer crashes core edit pages.
Move audit search resources from core Search*Provider files into the module
via a new AuditSearchResourceProvider implementing the existing
SearchResourceProvider contract. Add module-specific purge permissions
(audit.api.purge, audit.imports.purge) replacing core ABILITY_ADMIN_SETTINGS_UPDATE.
Also fixes: session key prefix convention, hardcoded English string, $_SERVER
superglobal fallback, and manifest schema drift (i18n_path, group in ui_slots).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Titlebar, breadcrumbs, and page title now reflect the user's actual
permission level across all 6 edit pages (roles, users, departments,
tenants, permissions, scheduled jobs). Added i18n keys for de and en.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CSRF guards added to 16 admin pages that previously accepted POST requests
without token verification (GR-SEC-001): departments, permissions, roles,
settings, tenants, users (create/edit/bulk/tokens), and lang switch.
New contract tests:
- PostEndpointCsrfContractTest: scans all pages/ for POST handlers and
verifies checkCsrfToken is present (API/data endpoints exempt).
- DatabaseUpdatesContractTest: validates db/updates/ scripts follow naming
convention and use idempotent SQL patterns (GR-CORE-010).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Session timeout: configurable idle (default 30min) and absolute (default 8h)
timeouts via DB settings, enforced in web/index.php on every request.
Timestamps set at login in action layer; graceful fallback for pre-existing
sessions.
Transaction wrapping: UserAccountService.createFromAdmin/register, roles
create/edit, and permissions edit now wrap multi-step DB operations in
transactions to guarantee atomicity.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>