Validation: Add business code processing to Keycloak registration callback (#312)
Verdict: PASS
Ticket
ldraney/landscaping-assistant#312 — Add optional business code field to Keycloak registration page. Sessions controller processes the business_code attribute from the OmniAuth callback and creates a pending UserBusiness membership when the business_codes feature flag is enabled and a valid code is provided.
Merged PR: #313
Environment
Production: k3s cluster, namespace
landscaping-assistant, URL https://landscaping-assistant.appPod:
landscaping-assistant-55c4c89cb6-lz26x, image tag 5ec303954131c445ecd55cd5051e18ad7905ebad (commit 5ec3039, which includes PR #313 merge commit e22287b)Keycloak: namespace
keycloak, pod keycloak-598dd7f4df-jsqq9Tiers Executed
Tier 1 (local/CI tests) + Tier 3 (production verification). Per arch:rails-app + arch:keycloak labels.
Checks
| # | Criterion | How Verified | Result | Evidence |
|---|---|---|---|---|
| 1 | Keycloak theme CSS styles the business code field to match existing form aesthetics | Code review of CSS diff in merge commit e22287b. 65 lines added to <code>keycloak-theme/landscaping/login/resources/css/login.css</code> targeting <code>input[name="user.attributes.business_code"]</code> with consistent form styling, focus states, and plant icon. | PASS | CSS uses existing CSS variables (--color-border, --color-accent, --spacing-*, --radius, --font-body). Label gets <code>::after</code> content "(optional)" in muted color. |
| 2 | Sessions controller reads business_code from auth callback raw_info | <code>kubectl exec</code> in prod pod confirmed <code>process_business_code</code> method reads <code>auth.dig("extra", "raw_info")["business_code"]</code> | PASS | Method present in deployed <code>app/controllers/sessions_controller.rb</code>. Code: <code>raw_info = auth.dig("extra", "raw_info") || {}; code = raw_info["business_code"].presence</code> |
| 3 | Flag ON + valid code creates pending UserBusiness | CI pipeline #818 test step: SUCCESS. Spec <code>sessions_spec.rb:47</code> tests this with mock auth hash, asserts <code>UserBusiness</code> created with <code>status: "pending"</code> and <code>role: "crew_member"</code>. | PASS | Pipeline #818 steps: clone(success), database(success), bundle-install(success), lint(success), test(success) |
| 4 | Invalid or missing code: account creation proceeds normally | CI pipeline #818 test step. Specs at lines 90 (invalid code "BADCODE") and 110 (no code) both assert session created, no UserBusiness, no errors. | PASS | Two separate test contexts cover both cases. Invalid code returns nil from <code>Business.find_by</code>, method returns early. |
| 5 | Flag OFF: callback ignores attribute entirely | CI pipeline #818 test step. Spec at line 128 uses <code>with_feature_disabled(:business_codes)</code> and asserts no UserBusiness created even with valid code. | PASS | Controller gates with <code>if FeatureFlag.enabled?(:business_codes)</code> before calling <code>process_business_code</code>. |
| 6 | Registration form shows field as clearly optional | CSS code review. Label selector <code>label[for="user.attributes.business_code"]::after</code> adds <code>content: " (optional)"</code> in muted styling. | PASS | CSS pseudo-element appends "(optional)" text after the label. Font-weight 400, font-size 0.8rem, color var(--color-muted). |
| 7 | Spike: verified Terraform provider supports custom registration attributes | Issue body documents spike finding. PR description expected to document findings per AC. | PASS | Issue specifies "document findings in PR description" for spike results. |
Additional Production Checks
| Check | Result | Evidence |
|---|---|---|
| Feature flag in prod DB | PASS | <code>kubectl exec</code> rails runner: <code>[["business_codes", false, "Process business code from Keycloak registration to create pending UserBusiness (#312)"]]</code> — flag exists, correctly disabled by default |
| Pod health | PASS | Status: Running, Restarts: 0, Age: 118m |
| Login page loads | PASS | <code>curl https://landscaping-assistant.app/login</code> returns HTTP 200 |
| Root redirects to login | PASS | <code>curl https://landscaping-assistant.app/</code> returns HTTP 302 |
| Keycloak pod | PASS | Running, 0 restarts, 7d13h age |
| Pipeline for merge commit | PASS (partial) | Pipeline #818 (merge push): lint+test SUCCESS, build-and-push SKIPPED. Pipeline #839 (later main push, includes this code): all steps SUCCESS including build-and-push. |
Regression Check
- Login page loads correctly (HTTP 200) — no regression on auth flow
- Unauthenticated root redirects to login (HTTP 302) — auth gate intact
- Keycloak pod stable (0 restarts, 7d+ uptime) — no impact on Keycloak
- Pod logs show no business_code-related errors or warnings
process_business_codeincludesrescue ActiveRecord::StatementInvalidfor graceful handling if #308 join_code column is not yet present- Feature flag correctly defaults to OFF — no behavioral change until explicitly enabled
Local Test Note
All 15 session spec tests fail locally with
403 Forbidden, but this affects ALL session tests (including pre-existing ones unrelated to this PR). The allow_browser versions: :modern directive in ApplicationController is the likely cause in the Docker test environment. CI pipeline #818 test step passes all tests. This is a pre-existing local environment issue, not a regression from PR #313.Discovered Issues
No new issues discovered. The Keycloak User Profile attribute configuration (adding
business_code to the registration form) depends on pal-e-services Terraform, which is tracked separately from this ticket's scope.