Validation: block lead role from DM at controller level

validation-55-2026-07-26 Doc

validation pass

Verdict: PASS

Ticket

#55 — Block lead role from DM at controller level. Board item #1825 on board-iss.
Shipped: before_action :require_client_or_admin guard in MessagesController that redirects lead-role users to /communications with flash "Available after your first meeting". Replaces UI-only gating with defense-in-depth controller enforcement.
Merged PR: #100 (commit 4d8a96e, merged to main)

Environment

Production cluster, namespace intelligentstaffingsystems, URL https://intelligentstaffingsystems.ai.
Pod: intelligentstaffingsystems-69c4f699d6-vp89s — 1/1 Running, 0 restarts, 9h uptime.
Image: harbor.tail5b443a.ts.net/intelligentstaffingsystems/app:b46c43399b520241eece48595c114501489fc7f7

Checks

# Criterion How Verified Result Evidence
1 Lead-role users redirected from /messages to /communications Code review: <code>require_client_or_admin</code> before_action at line 17 of messages_controller.rb; method at lines 100-105 checks <code>current_user_has_role?("client") || current_user_has_role?("admin")</code> and redirects otherwise PASS Controller source verified on main
2 Flash message "Available after your first meeting" shown to leads Code review: line 104 — <code>redirect_to communications_path, alert: "Available after your first meeting"</code> PASS Controller source verified on main
3 Client and admin roles can still access /messages Guard returns early for client/admin roles (line 102). Tests verify client GET/POST /messages succeed. Pipeline #117 test step green. PASS Branch pipeline #117: test step SUCCESS
4 Uses Keycloak realm role (not Lead.role column) Code uses <code>current_user_has_role?</code> which reads from session realm_access, not the database column PASS Controller source lines 100-102
5 Lead POST /messages blocked (no message created) Tests assert <code>assert_no_difference "Message.count"</code> for lead POST. 10 lead-redirect tests pass in CI. PASS Branch pipeline #117: test step SUCCESS
6 Docs updated to reflect controller enforcement <code>docs/messaging.md</code> line 3 and access control table updated; references <code>before_action :require_client_or_admin</code> PASS messaging.md verified on main
7 CI pipeline green (branch) Woodpecker pipeline #117 (55-lead-dm-blocking branch): clone, database, bundle-install, lint, security, test — all SUCCESS PASS Pipeline #117 status: success
8 Code deployed to production <code>git merge-base --is-ancestor 4d8a96e b46c433</code> confirms PR #100 is in deployed image. Pipeline #188 (latest main push) built and pushed successfully. PASS Image tag b46c433 includes commit 4d8a96e
9 Pod healthy in production <code>kubectl get pods -n intelligentstaffingsystems</code>: 1/1 Running, 0 restarts PASS Pod intelligentstaffingsystems-69c4f699d6-vp89s Running
10 Production endpoint responsive <code>curl</code> root URL returns HTTP 200; <code>/messages</code> returns 302 (redirect to login for unauth — correct) PASS HTTP 200 at /, HTTP 302 at /messages

Regression Check

Root URL (/) returns 200 — landing page healthy. Production pod logs show no error spikes related to messaging. Communications route (/communications) returns 302 for unauthenticated users (expected). Pod has 0 restarts over 9h. Latest main pipeline #188 passes all 7 steps including tests that exercise the full messages controller test suite.
Note: Pipeline #129 (the push-to-main for this specific merge) shows failure with steps skipped due to CI infrastructure issues (known push-event gap). The branch pipeline #117 and the latest main pipeline #188 are both fully green, confirming code correctness.

Discovered Issues

None. The implementation is clean and well-tested.