feat(state): declarative org members with deferred backfill (issue #94)#277
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
state.organizations.<name>.members = [{ user, role }](Nix + JSON) so operators can manage org membership declaratively.POST /useror first-log-in via OIDC.membersis non-empty, it is the source of truth — drift reconciliation removes memberships no longer in the list (cache-style). Emptymemberspreserves legacy behavior (created_byauto-added as Admin).Admin/Write/View) or a state-managed custom org role declared understate.rolesfor the same organization.Closes #94.
Implementation notes
apply_organizationsintoapply_organizations_without_members(runs early) andapply_organization_members(runs afterapply_roles) so custom org roles inserted in the same apply pass are resolvable.PendingOrgMembershipsmap returned fromload_and_apply_state, stashed onServerState, drained per-username by the newapply_pending_org_membershipshelper invoked from both the registration endpoint and OIDC first-login (inside the existing OIDC transaction so user creation and membership stay atomic).Test plan
cargo check --workspace --all-targetscargo clippy --workspace --all-targets -- -D warningscargo clippy --workspace --tests -- -D warningsstate_org_members_serde_round_trip,state_org_members_default_emptyaccepts_builtin_role,accepts_custom_org_role,rejects_unknown_role,ignores_unknown_user,rejects_duplicate_userpending_membership_tests::apply_pending_returns_zero_for_unknown_userPOST /user, verify theorganization_userrow appears.