diff --git a/.agents/runs/2026-04-24-action-csrf-centralization-step2/execution-report.json b/.agents/runs/2026-04-24-action-csrf-centralization-step2/execution-report.json new file mode 100644 index 0000000..f2bb79b --- /dev/null +++ b/.agents/runs/2026-04-24-action-csrf-centralization-step2/execution-report.json @@ -0,0 +1,95 @@ +{ + "task_id": "2026-04-24-action-csrf-centralization-step2", + "plan_ref": ".agents/runs/2026-04-24-action-csrf-centralization-step2/plan.json", + "status": "done", + "changed_files": [ + { "path": "core/Support/helpers/request.php", "summary": "Extended actionRequireCsrf() with optional redirectOnFailure flag for non-redirect POST flows." }, + { "path": "pages/admin/settings/revoke-api-tokens().php", "summary": "Replaced inline POST/CSRF boilerplate with actionRequirePost/actionRequireCsrf." }, + { "path": "pages/admin/settings/expire-remember-tokens().php", "summary": "Replaced inline POST/CSRF boilerplate with actionRequirePost/actionRequireCsrf." }, + { "path": "pages/admin/scheduled-jobs/purge-runs().php", "summary": "Replaced inline POST/CSRF boilerplate with actionRequirePost/actionRequireCsrf." }, + { "path": "pages/admin/scheduled-jobs/run($id).php", "summary": "Centralized method/CSRF guards via actionRequirePost/actionRequireCsrf." }, + { "path": "pages/admin/tenants/custom-field-create($id).php", "summary": "Centralized method/CSRF guard flow via action helpers." }, + { "path": "pages/admin/tenants/custom-field-update($id).php", "summary": "Centralized method/CSRF guard flow via action helpers." }, + { "path": "pages/admin/tenants/custom-field-delete($id).php", "summary": "Centralized method/CSRF guard flow via action helpers." }, + { "path": "pages/admin/users/access-pdf($id).php", "summary": "Kept GET rendering path intact; centralized CSRF check only in POST branch." }, + { "path": "pages/admin/users/access-pdf-bulk().php", "summary": "Replaced inline POST/CSRF boilerplate with actionRequirePost/actionRequireCsrf." }, + { "path": "pages/admin/departments/create().php", "summary": "Replaced inline CSRF block with actionRequireCsrf in POST branch." }, + { "path": "pages/admin/departments/edit($id).php", "summary": "Replaced inline CSRF block with actionRequireCsrf in POST branch." }, + { "path": "pages/admin/permissions/create().php", "summary": "Replaced inline CSRF block with actionRequireCsrf in POST branch." }, + { "path": "pages/admin/permissions/edit($id).php", "summary": "Replaced inline CSRF block with actionRequireCsrf in POST branch." }, + { "path": "pages/admin/roles/create().php", "summary": "Replaced inline CSRF block with actionRequireCsrf in POST branch." }, + { "path": "pages/admin/roles/edit($id).php", "summary": "Replaced inline CSRF block with actionRequireCsrf in POST branch." }, + { "path": "pages/admin/tenants/create().php", "summary": "Replaced inline CSRF block with actionRequireCsrf in POST branch." }, + { "path": "pages/admin/tenants/edit($id).php", "summary": "Replaced inline CSRF block with actionRequireCsrf in POST branch." }, + { "path": "pages/admin/users/edit($id).php", "summary": "Replaced inline CSRF block with actionRequireCsrf in POST branch." }, + { "path": "pages/admin/settings/index().php", "summary": "Replaced inline CSRF block with actionRequireCsrf in POST branch." }, + { "path": "pages/admin/scheduled-jobs/edit($id).php", "summary": "Centralized CSRF validation via actionRequireCsrf(..., redirectOnFailure: false) while preserving in-page error rendering." } + ], + "guard_evidence": [ + { + "guard_id": "GR-CORE-003", + "status": "pass", + "evidence": "Refactor keeps requestInput()-based handling; no superglobal request/session access introduced." + }, + { + "guard_id": "GR-CORE-012", + "status": "pass", + "evidence": "PRG flows in targeted actions remain intact; PostRedirectGetContractTest passes." + }, + { + "guard_id": "GR-SEC-001", + "status": "pass", + "evidence": "All Wave-2 targets now verify CSRF through actionRequireCsrf (or non-redirect variant in scheduled-jobs/edit)." + }, + { + "guard_id": "GR-SEC-008", + "status": "pass", + "evidence": "Existing policy/ability checks remain unchanged; only guard boilerplate was centralized." + }, + { + "guard_id": "GR-SEC-009", + "status": "pass", + "evidence": "Tenant-scope related service and authorization calls were not altered." + }, + { + "guard_id": "GR-TEST-001", + "status": "pass", + "evidence": "Architecture test set and full PHPUnit suite pass." + } + ], + "commands": [ + { "cmd": "docker compose exec -T php vendor/bin/phpunit tests/Architecture/PostEndpointCsrfContractTest.php", "result": "pass" }, + { "cmd": "docker compose exec -T php vendor/bin/phpunit tests/Architecture/PostRedirectGetContractTest.php", "result": "pass" }, + { "cmd": "docker compose exec -T php vendor/bin/phpunit tests/Architecture/AuthzAdminSettingsContractTest.php tests/Architecture/AuthzAdminMasterDataContractTest.php tests/Architecture/AuthzAdminUsersContractTest.php", "result": "pass" }, + { "cmd": "docker compose exec -T php vendor/bin/phpunit", "result": "pass" }, + { "cmd": "docker compose exec -T php vendor/bin/phpstan analyse -c phpstan.neon --no-progress", "result": "pass" }, + { "cmd": "docker compose exec -T php vendor/bin/phpunit tests/Architecture/CoreStarterkitContractTest.php", "result": "pass" }, + { "cmd": "docker compose exec -T php vendor/bin/php-cs-fixer fix --config=tools/php-cs-fixer/.php-cs-fixer.dist.php --dry-run --diff --verbose", "result": "pass" }, + { "cmd": "bin/docs-link-check.sh", "result": "pass" }, + { "cmd": "bin/docs-drift-check.sh", "result": "pass" }, + { "cmd": "bin/codex-skills-sync.sh --check", "result": "pass" } + ], + "quality_gate_results": [ + { "gate_id": "QG-001", "result": "pass", "notes": "PHPUnit full run green: 2023 tests, 28224 assertions." }, + { "gate_id": "QG-002", "result": "pass", "notes": "PHPStan level 5: no errors." }, + { "gate_id": "QG-003", "result": "pass", "notes": "CoreStarterkitContractTest green: 13 tests, 6974 assertions." }, + { "gate_id": "QG-006", "result": "pass", "notes": "php-cs-fixer dry-run green after removing one unused import." }, + { "gate_id": "QG-008", "result": "pass", "notes": "docs-link-check and docs-drift-check green." }, + { "gate_id": "QG-009", "result": "pass", "notes": "codex-skills-sync --check green." } + ], + "metrics": { + "wave2_target_files": 20, + "before_requestinput_method_calls": 9, + "after_requestinput_method_calls": 0, + "before_inline_session_csrf_checks": 20, + "after_inline_session_csrf_checks": 0 + }, + "test_results": [ + { "name": "PostEndpointCsrfContractTest", "result": "pass", "notes": "1 test, 2 assertions." }, + { "name": "PostRedirectGetContractTest", "result": "pass", "notes": "1 test, 2 assertions." }, + { "name": "Authz Admin contract suite", "result": "pass", "notes": "13 tests, 203 assertions." }, + { "name": "CoreStarterkitContractTest", "result": "pass", "notes": "13 tests, 6974 assertions." }, + { "name": "Full PHPUnit suite", "result": "pass", "notes": "2023 tests, 28224 assertions." } + ], + "open_items": [] +} diff --git a/.agents/runs/2026-04-24-action-csrf-centralization-step2/finalize.json b/.agents/runs/2026-04-24-action-csrf-centralization-step2/finalize.json new file mode 100644 index 0000000..8471ddf --- /dev/null +++ b/.agents/runs/2026-04-24-action-csrf-centralization-step2/finalize.json @@ -0,0 +1,12 @@ +{ + "task_id": "2026-04-24-action-csrf-centralization-step2", + "ready_to_finalize": true, + "code_review": "pass", + "security_review": "pass", + "acceptance_review": "pass", + "ci_status": "required-gates-pass", + "final_action": "commit", + "hold_reason": "", + "commit_message": "refactor(actions): centralize wave-2 post/csrf guards", + "notes": "Wave 2.1 + 2.2 executed. Required gates QG-001/002/003/006/008/009 green." +} diff --git a/.agents/runs/2026-04-24-action-csrf-centralization-step2/review-acceptance.json b/.agents/runs/2026-04-24-action-csrf-centralization-step2/review-acceptance.json new file mode 100644 index 0000000..2faa715 --- /dev/null +++ b/.agents/runs/2026-04-24-action-csrf-centralization-step2/review-acceptance.json @@ -0,0 +1,44 @@ +{ + "task_id": "2026-04-24-action-csrf-centralization-step2", + "verdict": "pass", + "checked_criterion_ids": [ + "SC-001", + "SC-002", + "SC-003", + "SC-004", + "SC-005" + ], + "checks": [ + { + "criterion_id": "SC-001", + "criterion": "All Wave-2.1/2.2 targets use actionRequirePost() and actionRequireCsrf() where semantically applicable without changing endpoint behavior.", + "result": "pass", + "evidence": "All planned Wave-2 targets were migrated to helper-based method/CSRF guards; mixed GET/POST access-pdf endpoint kept GET path unchanged." + }, + { + "criterion_id": "SC-002", + "criterion": "No regressions in PRG/authz flows: PostRedirectGetContractTest and Authz architecture suites remain green.", + "result": "pass", + "evidence": "PostRedirectGetContractTest and AuthzAdminSettings/AuthzAdminMasterData/AuthzAdminUsers contract tests pass." + }, + { + "criterion_id": "SC-003", + "criterion": "POST actions without CSRF/helper remain 0 in pages/ after the change.", + "result": "pass", + "evidence": "PostEndpointCsrfContractTest passes with no missing POST handlers." + }, + { + "criterion_id": "SC-004", + "criterion": "Inline method-check and inline CSRF-check occurrences in Wave-2 scope are reduced substantially (target: method checks -60%+, csrf checks -60%+ in targeted files).", + "result": "pass", + "evidence": "Wave-2 file metrics: requestInput()->method() 9 -> 0, Session::checkCsrfToken() 20 -> 0." + }, + { + "criterion_id": "SC-005", + "criterion": "Required gate set QG-001/002/003/006/008/009 passes; non-scope qa-required extras are reported separately if failing.", + "result": "pass", + "evidence": "All required gates were run individually and passed." + } + ], + "missing_or_wrong": [] +} diff --git a/.agents/runs/2026-04-24-action-csrf-centralization-step2/review-code.json b/.agents/runs/2026-04-24-action-csrf-centralization-step2/review-code.json new file mode 100644 index 0000000..aad64d2 --- /dev/null +++ b/.agents/runs/2026-04-24-action-csrf-centralization-step2/review-code.json @@ -0,0 +1,18 @@ +{ + "task_id": "2026-04-24-action-csrf-centralization-step2", + "verdict": "pass", + "checked_guard_ids": [ + "GR-CORE-003", + "GR-CORE-012", + "GR-TEST-001" + ], + "checked_quality_gate_ids": [ + "QG-001", + "QG-002", + "QG-003", + "QG-006", + "QG-008", + "QG-009" + ], + "findings": [] +} diff --git a/.agents/runs/2026-04-24-action-csrf-centralization-step2/review-security.json b/.agents/runs/2026-04-24-action-csrf-centralization-step2/review-security.json new file mode 100644 index 0000000..4f93b5b --- /dev/null +++ b/.agents/runs/2026-04-24-action-csrf-centralization-step2/review-security.json @@ -0,0 +1,10 @@ +{ + "task_id": "2026-04-24-action-csrf-centralization-step2", + "verdict": "pass", + "checked_guard_ids": [ + "GR-SEC-001", + "GR-SEC-008", + "GR-SEC-009" + ], + "findings": [] +} diff --git a/core/Support/helpers/request.php b/core/Support/helpers/request.php index 20e5281..872760e 100644 --- a/core/Support/helpers/request.php +++ b/core/Support/helpers/request.php @@ -78,7 +78,8 @@ function actionRequireCsrf( string $flashKeyPrefix = 'csrf_expired', bool $jsonAware = false, string $jsonError = 'csrf', - bool $flashOnFailure = true + bool $flashOnFailure = true, + bool $redirectOnFailure = true ): bool { if (Session::checkCsrfToken()) { return true; @@ -96,7 +97,9 @@ function actionRequireCsrf( flashFormErrors($errorBag, $flashScope ?? $redirect, $flashKeyPrefix); } - Router::redirect($redirect); + if ($redirectOnFailure) { + Router::redirect($redirect); + } return false; } diff --git a/pages/admin/departments/create().php b/pages/admin/departments/create().php index 7918337..c3ad762 100644 --- a/pages/admin/departments/create().php +++ b/pages/admin/departments/create().php @@ -4,7 +4,6 @@ use MintyPHP\Buffer; use MintyPHP\Http\SessionStoreInterface; use MintyPHP\Router; use MintyPHP\Service\Access\DepartmentAuthorizationPolicy; -use MintyPHP\Session; use MintyPHP\Support\Flash; use MintyPHP\Support\Guard; @@ -50,9 +49,7 @@ if ($allowedTenantIds) { $tenants = []; } -if ($request->isMethod('POST') && !Session::checkCsrfToken()) { - Flash::error(t('Form expired, please try again'), $createTarget, 'csrf_expired'); - Router::redirect($createTarget); +if ($request->isMethod('POST') && !actionRequireCsrf($createTarget, $createTarget, 'csrf_expired')) { return; } diff --git a/pages/admin/departments/edit($id).php b/pages/admin/departments/edit($id).php index c85157d..064633b 100644 --- a/pages/admin/departments/edit($id).php +++ b/pages/admin/departments/edit($id).php @@ -4,7 +4,6 @@ use MintyPHP\Buffer; use MintyPHP\Http\SessionStoreInterface; use MintyPHP\Router; use MintyPHP\Service\Access\DepartmentAuthorizationPolicy; -use MintyPHP\Session; use MintyPHP\Support\Flash; use MintyPHP\Support\Guard; @@ -81,9 +80,7 @@ if ($allowedTenantIds) { $form['tenant_id'] = 0; } -if ($request->isMethod('POST') && !Session::checkCsrfToken()) { - Flash::error(t('Form expired, please try again'), $editTarget, 'csrf_expired'); - Router::redirect($editTarget); +if ($request->isMethod('POST') && !actionRequireCsrf($editTarget, $editTarget, 'csrf_expired')) { return; } diff --git a/pages/admin/permissions/create().php b/pages/admin/permissions/create().php index 5bd68ef..9cb10ce 100644 --- a/pages/admin/permissions/create().php +++ b/pages/admin/permissions/create().php @@ -4,7 +4,6 @@ use MintyPHP\Buffer; use MintyPHP\Http\SessionStoreInterface; use MintyPHP\Router; use MintyPHP\Service\Access\PermissionAuthorizationPolicy; -use MintyPHP\Session; use MintyPHP\Support\Flash; use MintyPHP\Support\Guard; @@ -33,9 +32,7 @@ $form = [ 'is_system' => 0, ]; -if ($request->isMethod('POST') && !Session::checkCsrfToken()) { - Flash::error(t('Form expired, please try again'), $createTarget, 'csrf_expired'); - Router::redirect($createTarget); +if ($request->isMethod('POST') && !actionRequireCsrf($createTarget, $createTarget, 'csrf_expired')) { return; } diff --git a/pages/admin/permissions/edit($id).php b/pages/admin/permissions/edit($id).php index 39d4e65..1f01e0a 100644 --- a/pages/admin/permissions/edit($id).php +++ b/pages/admin/permissions/edit($id).php @@ -5,7 +5,6 @@ use MintyPHP\Http\SessionStoreInterface; use MintyPHP\Repository\Access\RoleRepository; use MintyPHP\Router; use MintyPHP\Service\Access\PermissionAuthorizationPolicy; -use MintyPHP\Session; use MintyPHP\Support\Flash; use MintyPHP\Support\Guard; @@ -48,9 +47,7 @@ $userWriteRepository = app(\MintyPHP\Repository\User\UserWriteRepository::class) $selectedRoleIds = $rolePermissionRepository->listRoleIdsByPermissionId($id); $previousRoleIds = $selectedRoleIds; -if ($request->isMethod('POST') && !Session::checkCsrfToken()) { - Flash::error(t('Form expired, please try again'), $editTarget, 'csrf_expired'); - Router::redirect($editTarget); +if ($request->isMethod('POST') && !actionRequireCsrf($editTarget, $editTarget, 'csrf_expired')) { return; } diff --git a/pages/admin/roles/create().php b/pages/admin/roles/create().php index 0f3e080..5171944 100644 --- a/pages/admin/roles/create().php +++ b/pages/admin/roles/create().php @@ -4,7 +4,6 @@ use MintyPHP\Buffer; use MintyPHP\Http\SessionStoreInterface; use MintyPHP\Router; use MintyPHP\Service\Access\RoleAuthorizationPolicy; -use MintyPHP\Session; use MintyPHP\Support\Flash; use MintyPHP\Support\Guard; @@ -37,9 +36,7 @@ $rolePermissionRepository = app(\MintyPHP\Repository\Access\RolePermissionReposi $permissions = $permissionRepository->listActive(); $selectedPermissionIds = []; -if ($request->isMethod('POST') && !Session::checkCsrfToken()) { - Flash::error(t('Form expired, please try again'), $createTarget, 'csrf_expired'); - Router::redirect($createTarget); +if ($request->isMethod('POST') && !actionRequireCsrf($createTarget, $createTarget, 'csrf_expired')) { return; } diff --git a/pages/admin/roles/edit($id).php b/pages/admin/roles/edit($id).php index e0ac029..1af7ff7 100644 --- a/pages/admin/roles/edit($id).php +++ b/pages/admin/roles/edit($id).php @@ -4,7 +4,6 @@ use MintyPHP\Buffer; use MintyPHP\Http\SessionStoreInterface; use MintyPHP\Router; use MintyPHP\Service\Access\RoleAuthorizationPolicy; -use MintyPHP\Session; use MintyPHP\Support\Flash; use MintyPHP\Support\Guard; @@ -53,9 +52,7 @@ $assignableRoleRepo = app(\MintyPHP\Repository\Access\RoleAssignableRoleReposito $allRolesForAssignable = app(\MintyPHP\Service\Access\RoleService::class)->listActive(); $selectedAssignableRoleIds = $assignableRoleRepo->listAssignableRoleIdsByRoleId($roleId); -if ($request->isMethod('POST') && !Session::checkCsrfToken()) { - Flash::error(t('Form expired, please try again'), $editTarget, 'csrf_expired'); - Router::redirect($editTarget); +if ($request->isMethod('POST') && !actionRequireCsrf($editTarget, $editTarget, 'csrf_expired')) { return; } diff --git a/pages/admin/scheduled-jobs/edit($id).php b/pages/admin/scheduled-jobs/edit($id).php index ae66d15..04c16a1 100644 --- a/pages/admin/scheduled-jobs/edit($id).php +++ b/pages/admin/scheduled-jobs/edit($id).php @@ -3,17 +3,18 @@ use MintyPHP\Buffer; use MintyPHP\Http\SessionStoreInterface; use MintyPHP\Router; -use MintyPHP\Session; use MintyPHP\Support\Flash; use MintyPHP\Support\Guard; $session = app(SessionStoreInterface::class)->all(); Guard::requireLogin(); Guard::requireAbility(\MintyPHP\Service\Access\OperationsAuthorizationPolicy::ABILITY_ADMIN_JOBS_VIEW); +$request = requestInput(); $scheduledJobService = app(\MintyPHP\Service\Scheduler\ScheduledJobService::class); $jobId = (int) ($id ?? 0); +$editTarget = "admin/scheduled-jobs/edit/$jobId"; $job = $jobId > 0 ? $scheduledJobService->find($jobId) : null; if (!$job) { Flash::error(t('Scheduled job not found'), 'admin/scheduled-jobs', 'scheduled_job_not_found'); @@ -47,22 +48,22 @@ if (!$isRegistered) { $errorBag->addGlobal(t('This scheduled job is no longer registered and cannot be edited. It has been superseded by a newer version.')); } -if (requestInput()->method() === 'POST') { +if ($request->isMethod('POST')) { if (!$canManage) { Guard::deny(); } - if (!Session::checkCsrfToken()) { + if (!actionRequireCsrf($editTarget, flashOnFailure: false, redirectOnFailure: false)) { $errorBag->addGlobal(t('Form expired, please try again')); } if (!$errorBag->hasAny()) { - $saved = $scheduledJobService->updateFromAdmin($jobId, requestInput()->bodyAll()); + $saved = $scheduledJobService->updateFromAdmin($jobId, $request->bodyAll()); if (!($saved['ok'] ?? false)) { $errorBag->merge((array) ($saved['errors'] ?? [t('Scheduled job could not be saved')])); $form = array_merge($form, (array) ($saved['form'] ?? [])); } else { - Flash::success(t('Scheduled job saved'), "admin/scheduled-jobs/edit/$jobId", 'scheduled_job_saved'); + Flash::success(t('Scheduled job saved'), $editTarget, 'scheduled_job_saved'); $job = $saved['job'] ?? $job; $form = [ 'enabled' => (int) ($job['enabled'] ?? 0), diff --git a/pages/admin/scheduled-jobs/purge-runs().php b/pages/admin/scheduled-jobs/purge-runs().php index f73129f..91869aa 100644 --- a/pages/admin/scheduled-jobs/purge-runs().php +++ b/pages/admin/scheduled-jobs/purge-runs().php @@ -1,21 +1,18 @@ method())) !== 'POST') { - Router::redirect('admin/scheduled-jobs'); +if (!actionRequirePost('admin/scheduled-jobs')) { + return; } -$errorBag = formErrors(); -if (!Session::checkCsrfToken()) { - $errorBag->addGlobal(t('Form expired, please try again')); - flashFormErrors($errorBag, 'admin/scheduled-jobs', 'scheduled_job_purge'); - Router::redirect('admin/scheduled-jobs'); + +if (!actionRequireCsrf('admin/scheduled-jobs', 'admin/scheduled-jobs', 'scheduled_job_purge')) { + return; } $deleted = app(\MintyPHP\Service\Scheduler\ScheduledJobService::class)->purgeRunsExpired(); diff --git a/pages/admin/scheduled-jobs/run($id).php b/pages/admin/scheduled-jobs/run($id).php index 8fcc5e0..e135159 100644 --- a/pages/admin/scheduled-jobs/run($id).php +++ b/pages/admin/scheduled-jobs/run($id).php @@ -4,7 +4,6 @@ use MintyPHP\Domain\Taxonomy\ScheduledJobRunStatus; use MintyPHP\Domain\Taxonomy\SchedulerRuntimeResult; use MintyPHP\Http\SessionStoreInterface; use MintyPHP\Router; -use MintyPHP\Session; use MintyPHP\Support\Flash; use MintyPHP\Support\Guard; @@ -13,16 +12,16 @@ Guard::requireLogin(); Guard::requireAbility(\MintyPHP\Service\Access\OperationsAuthorizationPolicy::ABILITY_ADMIN_JOBS_RUN_NOW); $jobId = (int) ($id ?? 0); -if (strtoupper((string) (requestInput()->method())) !== 'POST') { - Router::redirect("admin/scheduled-jobs/edit/$jobId"); -} -$errorBag = formErrors(); -if (!Session::checkCsrfToken()) { - $errorBag->addGlobal(t('Form expired, please try again')); - flashFormErrors($errorBag, "admin/scheduled-jobs/edit/$jobId", 'scheduled_job_run'); - Router::redirect("admin/scheduled-jobs/edit/$jobId"); +$redirectTarget = "admin/scheduled-jobs/edit/$jobId"; +if (!actionRequirePost($redirectTarget)) { + return; } +if (!actionRequireCsrf($redirectTarget, $redirectTarget, 'scheduled_job_run')) { + return; +} + +$errorBag = formErrors(); $result = app(\MintyPHP\Service\Scheduler\SchedulerRunService::class)->runJobNow($jobId, (int) ($session['user']['id'] ?? 0)); if (!($result['ok'] ?? false)) { $error = (string) ($result['error'] ?? ''); diff --git a/pages/admin/settings/expire-remember-tokens().php b/pages/admin/settings/expire-remember-tokens().php index 19b1f28..c3de039 100644 --- a/pages/admin/settings/expire-remember-tokens().php +++ b/pages/admin/settings/expire-remember-tokens().php @@ -3,7 +3,6 @@ use MintyPHP\Http\SessionStoreInterface; use MintyPHP\Router; use MintyPHP\Service\Access\SettingsAuthorizationPolicy; -use MintyPHP\Session; use MintyPHP\Support\Flash; use MintyPHP\Support\Guard; @@ -19,15 +18,12 @@ if (!$decision->isAllowed()) { Guard::deny(); } -if (requestInput()->method() !== 'POST') { - Router::redirect('admin/settings'); +if (!actionRequirePost('admin/settings')) { + return; } -$errorBag = formErrors(); -if (!Session::checkCsrfToken()) { - $errorBag->addGlobal(t('Form expired, please try again')); - flashFormErrors($errorBag, 'admin/settings', 'settings_tokens_expire'); - Router::redirect('admin/settings'); +if (!actionRequireCsrf('admin/settings', 'admin/settings', 'settings_tokens_expire')) { + return; } $rememberMeService = app(\MintyPHP\Service\Auth\RememberMeService::class); diff --git a/pages/admin/settings/index().php b/pages/admin/settings/index().php index 0855507..1de6f99 100644 --- a/pages/admin/settings/index().php +++ b/pages/admin/settings/index().php @@ -5,7 +5,6 @@ use MintyPHP\Http\SessionStoreInterface; use MintyPHP\Router; use MintyPHP\Service\Access\SettingsAuthorizationPolicy; use MintyPHP\Service\Settings\AdminSettingsService; -use MintyPHP\Session; use MintyPHP\Support\Flash; use MintyPHP\Support\Guard; @@ -34,9 +33,7 @@ $settings = is_array($pageData['settings'] ?? null) ? $pageData['settings'] : [] $activeLoginTokens = (int) ($pageData['active_login_tokens'] ?? 0); $activeApiTokens = (int) ($pageData['active_api_tokens'] ?? 0); -if ($request->isMethod('POST') && !Session::checkCsrfToken()) { - Flash::error(t('Form expired, please try again'), 'admin/settings', 'csrf_expired'); - Router::redirect('admin/settings'); +if ($request->isMethod('POST') && !actionRequireCsrf('admin/settings', 'admin/settings', 'csrf_expired')) { return; } diff --git a/pages/admin/settings/revoke-api-tokens().php b/pages/admin/settings/revoke-api-tokens().php index d5b7ba2..14c884a 100644 --- a/pages/admin/settings/revoke-api-tokens().php +++ b/pages/admin/settings/revoke-api-tokens().php @@ -3,7 +3,6 @@ use MintyPHP\Http\SessionStoreInterface; use MintyPHP\Router; use MintyPHP\Service\Access\SettingsAuthorizationPolicy; -use MintyPHP\Session; use MintyPHP\Support\Flash; use MintyPHP\Support\Guard; @@ -19,15 +18,12 @@ if (!$decision->isAllowed()) { Guard::deny(); } -if (requestInput()->method() !== 'POST') { - Router::redirect('admin/settings'); +if (!actionRequirePost('admin/settings')) { + return; } -$errorBag = formErrors(); -if (!Session::checkCsrfToken()) { - $errorBag->addGlobal(t('Form expired, please try again')); - flashFormErrors($errorBag, 'admin/settings', 'settings_tokens_revoke'); - Router::redirect('admin/settings'); +if (!actionRequireCsrf('admin/settings', 'admin/settings', 'settings_tokens_revoke')) { + return; } $apiTokenService = app(\MintyPHP\Service\Auth\ApiTokenService::class); diff --git a/pages/admin/tenants/create().php b/pages/admin/tenants/create().php index a6ec18f..634241f 100644 --- a/pages/admin/tenants/create().php +++ b/pages/admin/tenants/create().php @@ -4,7 +4,6 @@ use MintyPHP\Buffer; use MintyPHP\Http\SessionStoreInterface; use MintyPHP\Router; use MintyPHP\Service\Access\TenantAuthorizationPolicy; -use MintyPHP\Session; use MintyPHP\Support\Flash; use MintyPHP\Support\Guard; @@ -56,9 +55,7 @@ $form = [ ]; $ssoUiState = $canManageSso ? $tenantSsoService->buildMicrosoftUiState(0, $form) : []; -if ($request->isMethod('POST') && !Session::checkCsrfToken()) { - Flash::error(t('Form expired, please try again'), $createTarget, 'csrf_expired'); - Router::redirect($createTarget); +if ($request->isMethod('POST') && !actionRequireCsrf($createTarget, $createTarget, 'csrf_expired')) { return; } diff --git a/pages/admin/tenants/custom-field-create($id).php b/pages/admin/tenants/custom-field-create($id).php index 78b2cee..7f01474 100644 --- a/pages/admin/tenants/custom-field-create($id).php +++ b/pages/admin/tenants/custom-field-create($id).php @@ -4,7 +4,6 @@ use MintyPHP\Http\SessionStoreInterface; use MintyPHP\Router; use MintyPHP\Service\Access\TenantAuthorizationPolicy; use MintyPHP\Service\CustomField\TenantCustomFieldService; -use MintyPHP\Session; use MintyPHP\Support\Flash; use MintyPHP\Support\Guard; @@ -35,13 +34,11 @@ if (!$decision->isAllowed()) { $flashScope = 'admin/tenants/edit/' . $tenantUuid; $redirect = $flashScope . '?tab=custom_fields'; -if (!$request->isMethod('POST')) { - Router::redirect($redirect); +if (!actionRequirePost($redirect)) { return; } -if (!Session::checkCsrfToken()) { - Flash::error(t('Form expired, please try again'), $flashScope, 'tenant_custom_fields_csrf'); - Router::redirect($redirect); + +if (!actionRequireCsrf($redirect, $flashScope, 'tenant_custom_fields_csrf')) { return; } diff --git a/pages/admin/tenants/custom-field-delete($id).php b/pages/admin/tenants/custom-field-delete($id).php index a78ae29..5b571e3 100644 --- a/pages/admin/tenants/custom-field-delete($id).php +++ b/pages/admin/tenants/custom-field-delete($id).php @@ -4,7 +4,6 @@ use MintyPHP\Http\SessionStoreInterface; use MintyPHP\Router; use MintyPHP\Service\Access\TenantAuthorizationPolicy; use MintyPHP\Service\CustomField\TenantCustomFieldService; -use MintyPHP\Session; use MintyPHP\Support\Flash; use MintyPHP\Support\Guard; @@ -44,14 +43,11 @@ if ($tenantUuid === '') { $flashScope = 'admin/tenants/edit/' . $tenantUuid; $redirect = $flashScope . '?tab=custom_fields'; -if ((requestInput()->method()) !== 'POST') { - Router::redirect($redirect); +if (!actionRequirePost($redirect)) { return; } -if (!Session::checkCsrfToken()) { - $errorBag->addGlobal(t('Form expired, please try again')); - flashFormErrors($errorBag, $flashScope, 'tenant_custom_fields'); - Router::redirect($redirect); + +if (!actionRequireCsrf($redirect, $flashScope, 'tenant_custom_fields')) { return; } diff --git a/pages/admin/tenants/custom-field-update($id).php b/pages/admin/tenants/custom-field-update($id).php index 5aec3aa..5cffa95 100644 --- a/pages/admin/tenants/custom-field-update($id).php +++ b/pages/admin/tenants/custom-field-update($id).php @@ -4,7 +4,6 @@ use MintyPHP\Http\SessionStoreInterface; use MintyPHP\Router; use MintyPHP\Service\Access\TenantAuthorizationPolicy; use MintyPHP\Service\CustomField\TenantCustomFieldService; -use MintyPHP\Session; use MintyPHP\Support\Flash; use MintyPHP\Support\Guard; @@ -42,13 +41,11 @@ if ($tenantUuid === '') { $flashScope = 'admin/tenants/edit/' . $tenantUuid; $redirect = $flashScope . '?tab=custom_fields'; -if (!$request->isMethod('POST')) { - Router::redirect($redirect); +if (!actionRequirePost($redirect)) { return; } -if (!Session::checkCsrfToken()) { - Flash::error(t('Form expired, please try again'), $flashScope, 'tenant_custom_fields_csrf'); - Router::redirect($redirect); + +if (!actionRequireCsrf($redirect, $flashScope, 'tenant_custom_fields_csrf')) { return; } diff --git a/pages/admin/tenants/edit($id).php b/pages/admin/tenants/edit($id).php index 0f97d4d..52a552e 100644 --- a/pages/admin/tenants/edit($id).php +++ b/pages/admin/tenants/edit($id).php @@ -5,7 +5,6 @@ use MintyPHP\Http\SessionStoreInterface; use MintyPHP\Router; use MintyPHP\Service\Access\TenantAuthorizationPolicy; use MintyPHP\Service\CustomField\TenantCustomFieldService; -use MintyPHP\Session; use MintyPHP\Support\Flash; use MintyPHP\Support\Guard; @@ -77,9 +76,7 @@ $form = array_merge($tenant, $ssoConfig); $form['microsoft_enabled'] = $ssoConfig['enabled'] ?? false; $form['ldap_enabled'] = $ldapConfig['enabled'] ?? false; -if ($request->isMethod('POST') && !Session::checkCsrfToken()) { - Flash::error(t('Form expired, please try again'), $editTarget, 'csrf_expired'); - Router::redirect($editTarget); +if ($request->isMethod('POST') && !actionRequireCsrf($editTarget, $editTarget, 'csrf_expired')) { return; } diff --git a/pages/admin/users/access-pdf($id).php b/pages/admin/users/access-pdf($id).php index 8750050..b63d089 100644 --- a/pages/admin/users/access-pdf($id).php +++ b/pages/admin/users/access-pdf($id).php @@ -4,7 +4,6 @@ use MintyPHP\Http\SessionStoreInterface; use MintyPHP\Router; use MintyPHP\Service\Access\UserAuthorizationPolicy; use MintyPHP\Service\User\UserAccessPdfService; -use MintyPHP\Session; use MintyPHP\Support\Guard; $session = app(SessionStoreInterface::class)->all(); @@ -15,22 +14,20 @@ Guard::requireLogin(); $authorizationService = app(\MintyPHP\Service\Access\AuthorizationService::class); $userAccountService = app(\MintyPHP\Service\User\UserAccountService::class); $userAccessPdfService = app(UserAccessPdfService::class); +$request = requestInput(); $errorBag = formErrors(); // Support both route param and POSTed uuid (POST avoids exposing uuid in URL/history). $uuid = trim((string) ($id ?? '')); -if ($uuid === '' && strtoupper((string) (requestInput()->method())) === 'POST') { - if (!Session::checkCsrfToken()) { - $errorBag->addGlobal(t('Form expired, please try again')); - flashFormErrors($errorBag, 'admin/users', 'users_access_pdf'); - Router::redirect('admin/users'); +if ($uuid === '' && $request->isMethod('POST')) { + if (!actionRequireCsrf('admin/users', 'admin/users', 'users_access_pdf')) { return; } - $uuid = trim((string) (requestInput()->bodyAll()['uuid'] ?? '')); + $uuid = trim((string) ($request->bodyAll()['uuid'] ?? '')); } $user = $uuid !== '' ? $userAccountService->findByUuid($uuid) : null; if (!$user) { - if (strtoupper((string) (requestInput()->method())) === 'POST') { + if ($request->isMethod('POST')) { $errorBag->addGlobal(t('User not found')); flashFormErrors($errorBag, 'admin/users', 'users_access_pdf'); Router::redirect('admin/users'); diff --git a/pages/admin/users/access-pdf-bulk().php b/pages/admin/users/access-pdf-bulk().php index df6058e..1e7bcc3 100644 --- a/pages/admin/users/access-pdf-bulk().php +++ b/pages/admin/users/access-pdf-bulk().php @@ -4,8 +4,6 @@ use MintyPHP\Http\SessionStoreInterface; use MintyPHP\Router; use MintyPHP\Service\Access\UserAuthorizationPolicy; use MintyPHP\Service\User\UserAccessPdfService; -use MintyPHP\Session; -use MintyPHP\Support\Flash; use MintyPHP\Support\Guard; $session = app(SessionStoreInterface::class)->all(); @@ -24,14 +22,11 @@ if (!$decision->isAllowed()) { $userAccountService = app(\MintyPHP\Service\User\UserAccountService::class); $userAccessPdfService = app(UserAccessPdfService::class); -if (strtoupper((string) (requestInput()->method())) !== 'POST') { - Router::redirect('admin/users'); +if (!actionRequirePost('admin/users')) { return; } -if (!Session::checkCsrfToken()) { - Flash::error(t('Form expired, please try again'), 'admin/users', 'csrf_expired'); - Router::redirect('admin/users'); +if (!actionRequireCsrf('admin/users', 'admin/users', 'csrf_expired')) { return; } diff --git a/pages/admin/users/edit($id).php b/pages/admin/users/edit($id).php index 49e788d..a6e6f29 100644 --- a/pages/admin/users/edit($id).php +++ b/pages/admin/users/edit($id).php @@ -8,7 +8,6 @@ use MintyPHP\Repository\Auth\RememberTokenRepository; use MintyPHP\Router; use MintyPHP\Service\Access\UserAuthorizationPolicy; use MintyPHP\Service\CustomField\UserCustomFieldValueService; -use MintyPHP\Session; use MintyPHP\Support\Flash; use MintyPHP\Support\Guard; @@ -155,9 +154,7 @@ if ($canViewSecurityArtifacts) { $canManageApiTokens = $canManageApiTokens && $canViewSecurityArtifacts; $showApiTokens = $canViewSecurityArtifacts; -if ($request->isMethod('POST') && !Session::checkCsrfToken()) { - Flash::error(t('Form expired, please try again'), $editTarget, 'csrf_expired'); - Router::redirect($editTarget); +if ($request->isMethod('POST') && !actionRequireCsrf($editTarget, $editTarget, 'csrf_expired')) { return; }