Validation: block lead role from DM at controller level
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:b46c43399b520241eece48595c114501489fc7f7Checks
| # | 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.