Auth prereqs: session sub, accepted status, CrewMember auto-create #196
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "auth-session-prereqs"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
sub(UUID) to session hash for owner_sub matchingacceptedstatus to ServiceRequest (quoted → accepted → paid/scheduled)current_crew_memberhelper to ApplicationControllerChanges
app/controllers/sessions_controller.rb: sub in session, CrewMember find_or_create on loginapp/controllers/application_controller.rb: current_crew_member helper methodapp/models/service_request.rb: accepted status, updated transitions (quoted → accepted, not quoted → paid)spec/models/service_request_spec.rb: new transition specs for accepted stateTest Plan
Review Checklist
Related Notes
PR #196 Review
DOMAIN REVIEW
Tech stack: Ruby on Rails 8.1, RSpec, OmniAuth/Keycloak, PostgreSQL, PaperTrail.
1. Session hash change --
subadditionThe
sub: auth.uidaddition tosession[:user]insessions_controller.rb:18is critical and correct. Two existing consumers already readcurrent_user[:sub]:PropertiesController#create(line 21) -- setsowner_subon new propertiesPropertiesController#authorize_property_access(line 115) -- gates show/edit accessWithout this PR, those paths would silently get
nilforsub, breaking owner-based property matching. The OmniAuth test mock already providesuid: "test-uid-123", andprofile_spec.rbtests the full chain (auth.uid->session[:user][:sub]->current_user[:sub]->property.owner_sub). No existing code is broken by the addition --session[:user]is a hash; adding a new key is non-destructive.2. ServiceRequest transitions --
acceptedstateThe transition map is correct and complete:
requested -> quoted -> accepted -> paid -> scheduled -> completed(happy path)accepted -> scheduled(work before payment -- intentional business rule)quoted -> declinedandrequested -> declinedpreservedquoted -> paidcorrectly removed (must go throughaccepted)accepted -> declinedcorrectly excluded (once accepted, can't decline)completed,declined) remain lockedNo DB migration needed --
statusis aVARCHARcolumn andacceptedis just a new string value in the application-level allowlist. TheSTATUSESconstant andVALID_TRANSITIONShash are the single source of truth.3. CrewMember auto-create via
find_or_create_by!find_or_create_by!insessions_controller.rb:24is safe here:keycloak_usernamecolumn has a unique index (index_crew_members_on_keycloak_username,db/schema.rb:52). If two concurrent requests for the same user hit this, one will raiseActiveRecord::RecordNotUniquewhich Rails propagates as a 500. This is acceptable for a login path -- concurrent logins for the same user are an edge case, and the bang method ensures data integrity. No silent corruption.admin > lead > member > client) maps the first matching Keycloak realm role. Order is correct -- most privileged wins. Falls back toclientfor users with no matching role.4.
current_crew_memberhelpernilwhen not logged in (guard clause on line 47).@current_crew_member ||=-- correct per-request caching. Each request gets a fresh controller instance, so no stale data across requests.keycloak_username, which matches thefind_or_create_by!key in the sessions controller. Consistent.helper_methodso views can use it.5. Test coverage
ServiceRequest transition specs are thorough:
from acceptedblock covers all 6 possible transitions (2 valid, 4 rejected) -- good exhaustive coveragefrom quotedblock updated to testacceptedinstead ofpaid, and adds explicit rejection ofquoted -> paid#allowed_transitionstest dynamically iterates all statuses, so it automatically coversacceptedSTATUSESincludingacceptedBLOCKERS
None.
NITS
Role mapping ternary chain (
sessions_controller.rb:26): The nested ternary is functional but hard to scan. Consider extracting to a method or using an array-find pattern for readability:This is semantically equivalent and reads in one pass. Non-blocking.
No spec for CrewMember auto-create on login: The sessions request spec (
spec/requests/sessions_spec.rb) tests session population but does not assert that aCrewMemberrecord is created during the OmniAuth callback. A test likeexpect { post "/auth/keycloak"; follow_redirect! }.to change(CrewMember, :count).by(1)would close this gap. The model validations are well-tested, but the integration path through the controller is not. Non-blocking for this prereq PR since the feature is exercised transitively (profile specs callsign_in_aswhich triggers the callback), but worth adding.No spec for
current_crew_memberhelper: No request spec asserts thatcurrent_crew_memberreturns the correct record. Again non-blocking for a prereq PR -- the downstream service request tickets will exercise this -- but noting the gap.Comment on
accepted -> declinedexclusion: The business decision to disallowaccepted -> declinedis intentional but undocumented. A brief code comment on the transition map explaining why (e.g., "once accepted, cancellation goes through a different workflow") would help future readers. Non-blocking.SOP COMPLIANCE
PROCESS OBSERVATIONS
subaddition to the session hash is additive (no existing keys changed). Theacceptedstatus is application-level only (no migration). Thefind_or_create_by!is idempotent on subsequent logins.acceptedstatus will be available immediately on deploy. Existingservice_requestsinquotedstatus will now transition toacceptedinstead ofpaid-- any UI or API code that attemptsquoted -> paidwill get a validation error. This is the intended behavior per the PR description.VERDICT: APPROVED
b1dda1fa6fcd6b00c79b