Commit Graph

59 Commits

Author SHA1 Message Date
fs
2a4ccc8f1a chore(qa): clear stale PHPStan baseline entry and PHPUnit deprecations
Two pre-existing findings surfaced during the export-feature test run.
Both are trivial housekeeping, not related to the feature:

- phpstan-baseline.neon: drop the stale ignore for
  RequestInput::wantsJson — the method is now called from the
  helpdesk security-level endpoint and the new export flavor helper,
  so PHPStan no longer matched the pattern and failed with
  `ignore.unmatched` (non-ignorable).

- tests/Service/Auth/SsoUserLinkServiceTest.php: replace the deprecated
  isType('array') / isType('string') assertions with the
  PHPUnit 13-compatible isArray() / isString() equivalents.

Gates after cleanup: PHPUnit 1880 OK (0 deprecations), PHPStan 0 errors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 22:01:12 +02:00
fs
12356e2266 feat(core): generic CSV export primitive + helpdesk domains consumer
Add a reusable CSV-export building block to the core so any Grid.js list
page can gain a filter/sort-aware download with two clicks of glue.

Core primitive:
- core/Service/Export/CsvExportService: flavor-aware writer (plain CSV
  or Excel-compatible UTF-8+BOM+semicolon), with OWASP-aligned formula-
  injection escape (=, @, +, -, TAB, CR) applied to both rows *and*
  headers. Value objects for columns (ExportColumn) carry an extractor
  closure and an allowSignedNumeric flag for phone-number-shaped cells.
- core/Support/helpers/export.php: thin HTTP layer (exportSendCsv,
  exportCapLimit, exportResolveFlavor, exportRequireGetRequest) reusing
  the requestInput() contract for GR-CORE-003 consistency. Filename is
  sanitized and length-clamped; callers must still enforce auth.
- templates/partials/app-list-export-dropdown.phtml: zero-config
  <details class="dropdown"> with CSV + Excel triggers.
- web/js/core/app-list-export.js: initListExport({ gridConfig, exportUrl })
  mirrors the current grid filters + sort onto the export URL and
  navigates, preserving session cookies for download.

First consumer — Helpdesk domains:
- DomainListService extracts the shared filter/enrich/sort logic from
  domains-data so the grid endpoint and the new domains/export endpoint
  cannot drift.
- domains/export endpoint delegates to DomainListService, declares
  ExportColumns (with translated level labels), and exits via
  exportSendCsv.
- domains-data now ~20 lines, delegating to DomainListService.

Tests:
- CsvExportServiceTest (10 cases): both flavors, BOM/no-BOM, formula
  escapes incl. TAB/CR, header escape, signed-numeric allowlist,
  quoting, multiline, empty columns, generator-compatible iterable.
- ExportHelpersTest (5 cases): exportCapLimit bounds.
- DomainListServiceTest (8 cases): filter, search, "all" sentinel,
  sort, paging, enrichment, BC failure, tenant-scoped security filter.

Gates: PHPUnit green, PHPStan clean on touched files, module:sync ok.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 21:57:19 +02:00
fs
3f0db68b27 refactor: fix all 105 PHPStan 2 strict type findings across core and modules
Resolve all non-false-positive PHPStan 2 findings, reducing the baseline
from 486 to 382 entries (only unused-public false positives remain).

Categories fixed:
- Remove 39 redundant type checks (is_string, is_array, is_object, method_exists)
- Remove 12 no-op array_values() on already-sequential lists
- Simplify 13 null-coalesce on guaranteed offsets
- Remove 8 always-true instanceof checks
- Replace 12 redundant test assertions (assertTrue(true) → addToAssertionCount)
- Simplify 6 unnecessary nullsafe operators (?-> → ->)
- Fix 6 always-true !== '' comparisons
- Fix remaining: unset.offset, return.unusedType, staticMethod narrowing

53 files changed across core/, modules/, pages/, and tests/.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 12:54:20 +02:00
fs
98afac24b1 test: add 92 tests for 12 untested gateway and service classes
Cover Settings gateways (Session, LoginPersistence, SystemAudit, Smtp,
FrontendTelemetry, Metadata, App), Auth gateways (ExternalIdentity,
Tenant), DirectorySettings, UserSettings, and HotkeyService.
Gateway test coverage rises from 25% to 52%, overall service/gateway
coverage from 58.6% to 70.7%.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 20:46:20 +02:00
fs
4967095bb8 fix: resolve all quality gate failures — i18n key, typography tokens, PHPStan errors, style
- Add missing i18n key "LDAP attribute map could not be encoded" (de+en)
- Replace raw font-weight/line-height with design tokens in app-topbar.css
- Fix import ordering in SchedulerRunService.php (php-cs-fixer)
- Fix PHPStan mock chain errors in DebitorDetailServiceTest and TenantSsoServiceTest (PHPUnit 12 API)
- Simplify redundant comparison in NotificationService dedupe window
- Add PHPStan type hint for mock callback array in SchedulerJobFailedNotificationListenerTest
- Fix undefined variable and add flow-analysis suppression in login().php
- Increase dev PHP memory_limit to 1G for PHPStan (prod stays 256M)

All 6 quality gates now pass: QG-001 (1618 tests), QG-002 (0 errors),
QG-003 (6630 assertions), QG-006 (0 fixable), QG-008, QG-009.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 19:06:12 +02:00
fs
312d43d9d9 feat(scheduler): delete orphaned jobs and block editing of unregistered jobs
Adds deleteOrphanedJobs() to clean up stale job_keys during ensureSystemJobs, and prevents editing jobs that are no longer registered in the runtime registry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 14:13:09 +02:00
fs
1c784efd5c feat(auth): add domain-based SSO discovery for unprovisioned users
Users who exist in Microsoft Entra ID or LDAP but have no local account
were blocked at the email-first login step because the system required a
local user record before showing SSO options. This adds a domain-based
fallback: when the email is unknown locally, the system checks tenant SSO
configurations for matching allowed_domains and presents SSO-only login
options, enabling the existing JIT provisioning to trigger on callback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:35:57 +02:00
fs
5699bc6c5b refactor(config): remove runtime config files and centralize route/bootstrap 2026-04-01 20:27:42 +02:00
fs
7a222f0b1f fix(audit): harden module isolation and fix post-extraction drift
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>
2026-03-26 10:03:27 +01:00
fs
0c351f6aff refactor(audit): extract audit domain into self-contained module
Move the entire audit subsystem (system audit, API audit, import audit,
user lifecycle audit, frontend telemetry) from core into modules/audit/.

Core decoupling via interface-based injection:
- AuditRecorderInterface replaces SystemAuditService in 10+ core services
- UserLifecycleAuditInterface / ImportAuditInterface for specialized flows
- NullAuditRecorder fallback when audit module is disabled
- ApiBootstrap/ApiResponse use null-safe callable resolvers

Module structure (modules/audit/):
- Manifest with routes, permissions, scheduler jobs, authorization policy
- 9 services, 8 repositories, 6 domain enums, 4 job handlers
- 33 page files, 4 JS files, 8 test files, migration scripts, i18n

Core cleanup:
- OperationsAuthorizationPolicy, UiCapabilityMap, PermissionService
  surgically cleaned of audit-specific constants
- Sidebar template cleared of hardcoded audit navigation
- AuditModuleIsolationContractTest ensures no future core→module coupling

All quality gates pass: 1346 tests (19,276 assertions), PHPStan level 5
clean, architecture contracts verified.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 21:12:49 +01:00
fs
c609753570 refactor(tests): remove redundant tests and fix assertion style
Remove subset/duplicate architecture tests already covered by broader
checks, and replace assertTrue(true) with self::addToAssertionCount(1)
for explicit no-exception-thrown intent.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 18:46:49 +01:00
fs
cb5f7037b2 Harden module runtime: audited listener failures and DI-first resolver 2026-03-25 10:02:03 +01:00
fs
d1eeac6692 feat: add LDAP authentication as per-tenant login method
Add LDAP as a third authentication option alongside local password and
Microsoft Entra ID SSO. Per-tenant configuration supports Active Directory,
OpenLDAP, and FreeIPA with configurable attribute mapping, search-then-bind
and direct-bind methods, encrypted bind credentials (AES-256-GCM),
profile sync on login, and user auto-provisioning via external identity
linking.

Includes admin UI for connection settings with test-connection button,
login page LDAP form, 25 new PHPUnit tests, and de/en translations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 07:26:04 +01:00
fs
07fbb96246 test: add PHPUnit coverage for 7 critical Tier 1 services (GR-TEST-001)
199 new tests across 9 files covering auth, user lifecycle, and settings:
- ApiTokenService: create/validate/rotate/revoke, timing-safe hash, tenant scope
- ApiTokenEndpointService: pagination, scope filtering, tenant resolution, expiry
- SsoUserLinkService: 2-phase identity lookup, provisioning, profile+avatar sync
- UserAssignmentService: sync methods, assignable-role freeze, transactions
- UserLifecycleAuditService: encrypted snapshots, enum normalization, retention
- UserLifecycleRestoreService: full restore flow, 9 error exits, rollback
- AdminSettingsService: API/lifecycle, Microsoft/telemetry, color/SMTP validation

Workflow: TEST-TIER1-001 (Analyst → Planner → Executor → Code Review → Security Review → Acceptance → Finalizer — all pass)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 23:29:07 +01:00
fs
c429ff43cd refactor: fix 7 CRUD system inconsistencies for robustness and maintainability
- Add user-assignment dependency check to DepartmentService.deleteByUuid (409 when users assigned)
- Standardize POST detection to isMethod('POST') in 10 create/edit actions
- Align RoleService code uniqueness to warning pattern (matching departments)
- Normalize repository create() return types from mixed to int|false (3 repos + 3 interfaces)
- Add JSON response branches to 4 non-user delete actions
- Document delete authorization ordering pattern
- Decompose AdminSettingsService.updateFromAdmin into domain-scoped private methods

Workflow: .agents/runs/CRUD-CONSISTENCY-001/
Reviewed by: Code Reviewer (pass), Security Reviewer (pass), Acceptance Tester (pass)
Quality gates: QG-001 PHPUnit pass, QG-002 PHPStan pass, QG-003 Architecture pass

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 18:25:33 +01:00
fs
cf8c59d3f8 feat: add read-only System Info admin page with health checks and module inventory
New page at /admin/system-info with three tabs:
- Overview: PHP version, SAPI, environment, 6 health checks (DB, schema,
  storage, RBAC baseline, admin role, scheduler heartbeat)
- Modules: table of active modules with version, dependencies, permission count
- Permissions: active/inactive counts and per-source breakdown

Gated behind new system_info.view permission assigned to Admin role.
No mutations — purely diagnostic/observability.

Includes SystemHealthService, SystemInfoService, SystemHealthRepository
with interface, DI registration, i18n keys (de+en), idempotent DB update
script, and 17 new PHPUnit tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 15:08:02 +01:00
fs
555199b217 fix: PHPStan and test strictness improvements
Add bin/ scripts to PHPStan scanFiles, suppress property.onlyWritten for
by-ref test properties, add exhaustive default match arm, use explicit
expects() on mock stubs, fix data provider return type, and simplify
redundant null check in NotificationServiceTest.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 14:15:36 +01:00
fs
975651b183 fix: eliminate $_SESSION drift in SessionProvider and add notification UI states
Pass AppContainer to SessionProvider::clear() so all module providers use
SessionStoreInterface consistently instead of raw $_SESSION. Also add
loading and error states to the notification bell dropdown (GR-UI-014).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 22:49:03 +01:00
fs
ae74fc3251 Stabilize service suite hygiene 2026-03-19 20:35:15 +01:00
fs
b873efc973 Strengthen settings gateway tests 2026-03-19 20:32:34 +01:00
fs
56c3e30f43 Strengthen service workflow tests 2026-03-19 20:28:04 +01:00
fs
011d662dfc Drop redundant crypto gateway tests 2026-03-19 20:17:11 +01:00
fs
72c7f1b337 Tighten service test signal 2026-03-19 20:15:48 +01:00
fs
28008c008c Trim remember me delegation tests 2026-03-19 20:04:14 +01:00
fs
c89501bbed Remove low-value service delegation tests 2026-03-19 19:46:07 +01:00
fs
5c7a1148ac Consolidate settings session policy tests 2026-03-19 19:45:16 +01:00
fs
bc72fa50a6 Consolidate settings normalization tests 2026-03-19 19:43:55 +01:00
fs
83aadb3535 Deduplicate crypto gateway tests 2026-03-19 19:36:17 +01:00
fs
ef72b34c40 feat: module architecture improvements — session keys, dependency graph, event dispatcher, deactivation hooks
Six targeted improvements to the modular monolith platform:

1. Fix AddressBook session key prefix to follow module.<id>.* convention
2. Move ADDRESS_BOOK_VIEW permission constant from core PermissionService into module
3. Add declarative JSON schema for module manifests (.agents/contracts/)
4. Add `requires` field with missing-dependency and circular-dependency detection
5. Add lightweight fire-and-forget event dispatcher (user.created/deleted/login/logout)
6. Add module deactivation hook interface and CLI script (bin/module-deactivate.php)

Includes 15 new architecture/unit tests covering all new functionality.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 18:20:13 +01:00
fs
c328067aa6 refactor: align addressbook module to MintyPHP\Module\AddressBook namespace
Migrates addressbook service classes from core namespace
(MintyPHP\Service\AddressBook\*) to proper module namespace
(MintyPHP\Module\AddressBook\Service\*), consistent with the
bookmarks module pattern.

Moved to module:
- AddressBookService → modules/addressbook/lib/Module/AddressBook/Service/
- AddressBookServicesFactory → modules/addressbook/lib/Module/AddressBook/Service/
- AddressBookServiceTest → modules/addressbook/tests/Module/AddressBook/Service/
- Pages, templates, CSS, JS all in module directory

All module PHP classes now live under MintyPHP\Module\<Name>\*,
enforced by ModuleStructureContractTest::testModuleClassesUseModuleNamespace.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:20:54 +01:00
fs
4871c6032e feat: extract bookmarks as standalone module with MintyPHP\Module\Bookmarks namespace
Moves bookmarks from core hardcoding into modules/bookmarks/ as a
fully self-contained module, following the same pattern as addressbook.

Module contributions via platform slots:
- aside.tab_panel: bookmark sidebar panel
- topbar.right_item: bookmark toggle button
- layout.body_end_template: bookmark/group dialogs
- layout.head_style: bookmark CSS (form + sidebar)
- runtime.component: bookmark-save and bookmark-panel (phase: late)
- search.resource_item: bookmarks in global search (user-scoped via {{userId}})

Backend fully in module namespace (MintyPHP\Module\Bookmarks\*):
- Service: BookmarkService, BookmarkServicesFactory, BookmarkRepositoryFactory
- Repository: BookmarkRepository, BookmarkGroupRepository, BookmarkNavigationRepository
- Support: BookmarkUrlNormalizer
- Providers: BookmarksSessionProvider, BookmarksLayoutProvider, BookmarksSearchProvider

Core cleanup:
- Removed all bookmark-specific markup from core templates
- Removed core DI registrations for bookmark services
- Removed core bookmark pages, JS, CSS
- AuthSessionTenantContextService delegates to module SessionProvider

Architecture guards:
- NoBookmarksHardcodingTest: verifies zero bookmark references in core
- testModuleClassesUseModuleNamespace: prevents core namespace leakage

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:20:20 +01:00
fs
c7b8fd516a feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.

New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering

New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
  module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
  FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
fs
d9805c45d3 fix: address code review findings for bookmark feature (H1–L5)
Fix broken tests (H1), align interface signatures with implementations (H3),
remove dead code (H4), add missing input guard (M1), replace raw $_SESSION
access with SessionStoreInterface (M2), sync update script schema (M4),
use shared getAppBase utility (L2), document fragment stripping (L3),
and apply app- CSS class prefix convention (L5).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 22:58:07 +01:00
fs
9688848401 Global Boomarks 2026-03-14 21:45:58 +01:00
fs
d4978d1504 test(services): add 88 unit tests for 7 critical service classes
RateLimiterServiceTest (17): hit/isBlocked/reset/registerFailure, fail-open,
sliding window, scope normalization.
PermissionServiceTest (24): userHas, getUserPermissions, cache hit/miss/refresh,
clearUserCache, CRUD, system permission protection.
RoleServiceTest (9): createFromAdmin, updateFromAdmin, deleteByUuid,
Admin role protection, duplicate code rejection.
TenantServiceTest (8): CRUD, department-dependent deletion blocking.
DepartmentServiceTest (14): listPaged scope filtering, groupActiveByTenantIds,
createFromAdmin, deleteByUuid, syncTenants.
MailServiceTest (8): send logging, MailerException handling, template metadata.
UserLifecycleServiceTest (8): advisory locking, deactivation, deletion,
snapshot failure skip, privileged user exclusion, manual trigger type.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 13:57:54 +01:00
fs
f6777113ec test(gateways): add 27 unit tests for 3 security-critical gateway classes
AuthCryptoGatewayTest (10 tests): encrypt/decrypt round-trip, unique IVs,
Crypto payload format verification (GR-SEC-005), error handling.
SettingsCryptoGatewayTest (9 tests): interface compliance, round-trip,
AES-256-GCM format, tampered ciphertext handling.
OidcHttpGatewayTest (8 tests): success, 401/500, network error, malformed JSON.

Plan amended: PermissionGateway removed (does not exist in codebase).
SC-002 amended: payload format verification accepted as Crypto delegation proof.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 13:57:33 +01:00
fs
cf4a7ec9d5 test(access): finalize operations authorization policy coverage 2026-03-13 13:35:34 +01:00
fs
010690de19 refactor(actions): deduplicate audit metadata and grid user count enrichment 2026-03-13 12:05:11 +01:00
fs
892da0048d refactor(arch): enforce gateway compliance and remove service-wrapping gateways 2026-03-13 11:31:33 +01:00
fs
17a72af5f2 guard permission fix 2026-03-12 13:01:06 +01:00
fs
24a6b79f7a Fix tenant SSO status mode and refactor status UI tiles 2026-03-12 10:01:24 +01:00
fs
964c07a9de feat(auth): add microsoft auto-remember policy with tenant override and configurable remember TTL 2026-03-10 22:48:10 +01:00
fs
af8ee10930 feat(auth): keep remember tokens on session timeout
- add timeout-specific logout path without remember-token revocation

- reset session timers after remember-me auto login

- compact session policy wording in admin settings (DE/EN)

- extend auth tests for timeout/manual logout behavior
2026-03-09 20:47:21 +01:00
fs
792af5a532 feat(settings): add admin session policy management 2026-03-09 20:07:55 +01:00
fs
01c05d997f fix(security): enforce atomic role/permission writes and user assignment rollback 2026-03-09 19:54:58 +01:00
fs
11ca546eae feat(security): add session timeout + transaction wrapping (B1)
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>
2026-03-09 19:16:26 +01:00
fs
7153fec05c feat(auth): harden login flows and finalize quality-check coverage 2026-03-07 22:54:33 +01:00
fs
fb6f87374f fix(theme): stabilize tenant switch theme propagation and toggle race handling
Task: REVIEW-USER-THEME-001
2026-03-07 20:14:29 +01:00
fs
549d9dd6f4 test(auth): add 33 unit tests for AuthService and RememberMeService
AuthServiceTest (17 tests): canLoginToTenant, refreshSessionAuthState,
loginUserById, login email-not-verified branch.
RememberMeServiceTest (16 tests): rememberUser, autoLoginFromCookie
(all 11 branches), forgetCurrentUser, forgetAllForUser,
expireAllTokensByAdmin.

Also fixes: 3 PHPStan errors in FrontendTelemetryIngestService,
8 CS-Fixer violations in out-of-scope files (ordered_imports,
braces_position).

All quality gates green: QG-001 (429 tests/0 failures),
QG-002 (0 errors), QG-003 (11 arch tests pass), QG-006 (0 violations).

Task: AUTH-LOGIN-REVIEW-001

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 11:43:31 +01:00
fs
9a08f96c11 agent foundation 2026-03-06 00:44:52 +01:00