fix(reconcile): close the PlatformUser Rule 2 and Rule 5 gaps#1795
Conversation
Sudo returned nil for an id that is neither an existing user nor an email, reporting the platform grant as done while nothing changed. A reconcile add for a missing user id then reported success and re-planned the same add on every run, never converging (rule 4/rule 2). Return a not-found error so the add fails loudly and the operator sees the bad ref. Only the not-found non-email path changes; an existing id and an email both behave as before.
📝 WalkthroughSummary by CodeRabbit
WalkthroughChangesSudo user lookup behavior
Platform-user email references
Estimated code review effort: 3 (Moderate) | ~20 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Coverage Report for CI Build 29994649480Warning Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes. Coverage increased (+0.05%) to 46.674%Details
Uncovered ChangesNo uncovered changes found. Coverage Regressions1 previously-covered line in 1 file lost coverage.
Coverage Stats
💛 - Coveralls |
The server can store a user's email as a display-name form ("Alice <a@x.com>"), so exporting the stored email verbatim produced a document the strict validation rejected, breaking the rule 5 round trip. Compare emails by their parsed address instead: validate accepts a uuid or any email, matching compares the lowercased address (so a display-name or differently-cased ref matches the stored address), and export emits the normalized address, or the id when the stored value is not an email. A uuid never parses as an email, so a uuid ref matches only by id and a user whose email is another user's id can neither be matched nor exported as that id. Closes the PlatformUser rule 5 gap over every reachable email form.
Uses mail.ParseAddress per review discussion; this does not fold plus-addressing (abc+1 and abc+2 stay distinct), only display name and case.
c6c1aaa to
c36bb61
Compare
Two edges the address-based matching missed, found by an independent review. (1) An add for an unmatched email spec sent the ref verbatim, so a display-name ref like "Alice <a@x.com>" made the server create a shadow user and grant it, never the real user, and the drift never showed again. canonicalRef now normalizes an email ref to its plain address, so the add resolves to the real user. (2) A quoted local part ("john smith"@x.com) unquotes to an address mail.ParseAddress cannot read back, so exporting it emitted a ref that failed its own validation. emailAddress now requires the address to re-parse, so such stored emails fall back to the id on export and match, keeping the rule 5 round trip.
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: f0fb6148-e1a9-4435-a6b6-c4f202f9aa9e
📒 Files selected for processing (6)
core/user/service.gocore/user/service_test.gointernal/reconcile/platformuser.gointernal/reconcile/platformuser_reconciler.gointernal/reconcile/platformuser_reconciler_test.gointernal/reconcile/platformuser_test.go
What
Closes the two remaining PlatformUser gaps on the rules-v2 audit: Rule 2 (an add for a missing user id reported phantom success) and Rule 5 (a stored display-name email broke the export round-trip). One commit per fix.
Rule 2: fail an unknown non-email
Sudoref (server-side)user.Sudoreturnednilfor an id that is neither an existing user nor an email (the "skip" branch). It reported the platform grant as done while granting nothing. So a reconcile add for a missing user id reported success and re-planned the same add on every run, never converging. It now returns a not-found error, so the add fails loudly and names the bad ref. Only the not-found non-email path changes; an existing id and an email both behave as before. The serviceuserSudoalready fails loudly on a missing id, so it needs no change.Rule 5: match and export platform-user emails by address (reconcile-side)
The server can store a user's email as a display-name form (
Alice <a@x.com>;AddPlatformUseraccepts it becauseisValidEmailusesmail.ParseAddress). Exporting that stored email verbatim produced a document the validation rejected, breaking the Rule 5 round-trip over a reachable state.The fix compares emails by their parsed address instead of as exact strings:
mail.ParseAddressparses, so a display-name ref is allowed."john smith"@x.comunquotes to an addressmail.ParseAddresscannot read back, so it exports by id and still round-trips.A uuid never parses as an email, so a uuid ref matches only by id, and a user whose email happens to be another user's id can neither be matched by that id's ref nor exported as that id (which would otherwise grant the other user on re-reconcile). The uuid-as-email case, the add normalization, and the quoted-local-part fallback were each found by independent review and are fixed here.
Testing
Sudocall for an unknown non-email id now errors, where it used to skip silently. Rule 5: a display-name-email user round-trips, a display-name ref matches the stored principal, a uuid ref does not match a user whose email is that uuid, a user whose email is another user's id exports by its own id, an add for a new email ref uses the normalized address, and a user with a quoted-local-part email exports by id and round-trips.go build,go vet,go test ./internal/reconcile/... ./core/user/...,gofmt, andgolangci-lintall pass.Notes
AddPlatformUserhandler maps aSudoerror toInternaltoday. That is enough for the reconciler (it stops on any error), but a not-found could map to a cleaner status code. Small follow-up if wanted.abc+1@x.comandabc+2@x.comstay distinct.a@x.comandAlice <a@x.com>, which the server allows because email uniqueness is on the raw string) both export to that one address, so reconciling that export cross-grants their relations. It is an accepted edge; the alternative (a strict bare-email check with an id fallback) avoids it but rejects hand-written display-name refs.Status
The documented PlatformUser Rule 2 gap (the
Sudophantom success) and the Rule 5 email round-trip are closed here. An independent review surfaced one more reachable Rule 2 gap this PR does not fix: a disabled platform admin is dropped byListPlatformUsers(the store filters disabled users), so the reconciler never sees the grant, cannot show it in a plan, and cannot remove it by omission. Closing that needs either a server-side change to list disabled principals or a documented carve-out, so it is a follow-up. After this PR the audit has no Non-compliant cells left; the remaining Partial cells are that disabled-admin case and Permission's namespace-charset gap (Rules 2 and 4). Targetsmain.