feat(auth): make profile names Bunyip-owned and read-only #481

Merged
Claude-Run merged 1 commit from feat/PMS-512-bunyip-owned-profile-names into main 2026-08-02 04:24:02 +02:00
Member

Bunyip is the identity source of truth, so mokosh's users.first_name / users.last_name become a read-only local cache refreshed from the given_name / family_name claims on every login, and first_name / last_name / phone stop being editable through the mokosh API.

upsert_user_from_oidc's ON CONFLICT (id) DO UPDATE branch now overwrites both name columns from the raw hints instead of leaving them at their seeded value. The hints bind as NULL when absent or empty after trimming, so COALESCE keeps the existing value and neither NOT NULL column is ever written as an empty string or clobbered by the synthetic email-derived placeholder.

That upsert only runs on first sight of a sub, so the already-provisioned path needed the other half: place_bunyip_user now calls the new AuthService::refresh_names_from_oidc when a non-empty claim differs from the cached value. The bunyip branch runs per request rather than per login, hence the change check before the write.

UpdateUserRequest drops first_name, last_name, and phone, which removes them from both PUT /me and the admin PUT /users/{id}; the conditional UPDATE builder and the name-driven profile_completed_at stamp in update_user go with them (that stamp now comes solely from the login whose claims carry both names). GET /me still returns all three for display and the columns stay in place as an inert cache, so every query that joins users for a display name is untouched. No phone_number extraction is added; sourcing phone from Bunyip stays deferred.

Tests pin the contract mechanically: put_me_cannot_mutate_bunyip_owned_profile_fields fails if any of the three fields is re-added to the request type, and three bunyip-login tests cover refresh-on-every-login, the absent/empty-hint guard, and the ON CONFLICT branch directly.

#PMS-512

Bunyip is the identity source of truth, so mokosh's `users.first_name` / `users.last_name` become a read-only local cache refreshed from the `given_name` / `family_name` claims on every login, and `first_name` / `last_name` / `phone` stop being editable through the mokosh API. `upsert_user_from_oidc`'s `ON CONFLICT (id) DO UPDATE` branch now overwrites both name columns from the raw hints instead of leaving them at their seeded value. The hints bind as NULL when absent or empty after trimming, so `COALESCE` keeps the existing value and neither `NOT NULL` column is ever written as an empty string or clobbered by the synthetic email-derived placeholder. That upsert only runs on first sight of a `sub`, so the already-provisioned path needed the other half: `place_bunyip_user` now calls the new `AuthService::refresh_names_from_oidc` when a non-empty claim differs from the cached value. The bunyip branch runs per request rather than per login, hence the change check before the write. `UpdateUserRequest` drops `first_name`, `last_name`, and `phone`, which removes them from both `PUT /me` and the admin `PUT /users/{id}`; the conditional UPDATE builder and the name-driven `profile_completed_at` stamp in `update_user` go with them (that stamp now comes solely from the login whose claims carry both names). `GET /me` still returns all three for display and the columns stay in place as an inert cache, so every query that joins `users` for a display name is untouched. No `phone_number` extraction is added; sourcing phone from Bunyip stays deferred. Tests pin the contract mechanically: `put_me_cannot_mutate_bunyip_owned_profile_fields` fails if any of the three fields is re-added to the request type, and three bunyip-login tests cover refresh-on-every-login, the absent/empty-hint guard, and the `ON CONFLICT` branch directly. #PMS-512
feat(auth): make profile names Bunyip-owned and read-only
All checks were successful
E2E / Playwright against staging (pull_request) Successful in 2m17s
Check / fmt + clippy + build + tests (pull_request) Successful in 3m46s
Integration / integration tests (pull_request) Successful in 13m11s
Create release / Gate (release-branch merges only) (pull_request) Successful in 1s
Create release / Create release from merged PR (pull_request) Has been skipped
aee1f64962
Bunyip is the identity source of truth, so mokosh's `users.first_name` / `users.last_name` become a read-only local cache refreshed from the `given_name` / `family_name` claims on every login, and `first_name` / `last_name` / `phone` stop being editable through the mokosh API.

`upsert_user_from_oidc`'s `ON CONFLICT (id) DO UPDATE` branch now overwrites both name columns from the raw hints instead of leaving them at their seeded value. The hints bind as NULL when absent or empty after trimming, so `COALESCE` keeps the existing value and neither `NOT NULL` column is ever written as an empty string or clobbered by the synthetic email-derived placeholder.

That upsert only runs on first sight of a `sub`, so the already-provisioned path needed the other half: `place_bunyip_user` now calls the new `AuthService::refresh_names_from_oidc` when a non-empty claim differs from the cached value. The bunyip branch runs per request rather than per login, hence the change check before the write.

`UpdateUserRequest` drops `first_name`, `last_name`, and `phone`, which removes them from both `PUT /me` and the admin `PUT /users/{id}`; the conditional UPDATE builder and the name-driven `profile_completed_at` stamp in `update_user` go with them (that stamp now comes solely from the login whose claims carry both names). `GET /me` still returns all three for display and the columns stay in place as an inert cache, so every query that joins `users` for a display name is untouched. No `phone_number` extraction is added; sourcing phone from Bunyip stays deferred.

Tests pin the contract mechanically: `put_me_cannot_mutate_bunyip_owned_profile_fields` fails if any of the three fields is re-added to the request type, and three bunyip-login tests cover refresh-on-every-login, the absent/empty-hint guard, and the `ON CONFLICT` branch directly.

#PMS-512
Claude-Run deleted branch feat/PMS-512-bunyip-owned-profile-names 2026-08-02 04:24:02 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/mokosh-server!481
No description provided.