feat(members): per-member permission overrides - #262
Merged
Merged
Conversation
… remove stale comment
The unchanged-list comparison ignored permission_allow/permission_deny, so an override change with no other team change was discarded and cacheVaultRoles mirrored stale bits into the keychain the Rust vault-write gate reads.
…red bit clearable Hide the section entirely when the server serves neither mask, so an older server does not render 16 live rows that 404 on click. Recompute undo and redo from current store state instead of replaying a whole-mask snapshot, so the full-replace PUT no longer erases a concurrent admin's change. Render CREATE_CUSTOM_ROLES when either mask carries it, so an offending bit always has a row that can clear it.
Also note the 32-bit ceiling on PERM_BITS.
… member at() defaulted to 0/0 when the member was missing from membersByTeam, silently discarding real permission bits on undo/redo.
…ermissions.ts minRolePosition + the guardrail chain move beside crossesVaultKeyGate, the file that already carries the "stay in sync with server/src/permissions.rs" contract. Returns the reason enum, not a translated string; READONLY_REASON_KEYS moves to module scope so it isn't rebuilt every render. Behaviour-preserving.
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.
Client half of per-member permission overrides. Each member gains two bitmasks,
allowanddeny, layered over their role union, resolving aseffective = (roleUnion | allow) & ~denywith deny applied last — identical to the server.Server side shipped as VoltiusApp/server#19, merged and deployed (migration 042 live). This client half is inert against an older server, and now hides the section entirely in that case rather than offering controls that 404.
What's here
effectivePermissionsapplies the overlay — the single implementation;resolveCan's team fallback andteamDataManagerpass theTeamitself so a team-level deny is not silently dropped on the cold-start path.effectivePermissions, so the Rust vault-write gate agrees with the server instead of carrying a second copy of the formula.member.permissions_changed.Deliberate deviations from the spec
COPY_SECRETSalone never rotates, and clearing an allow grant can. The dialog fires on gate-crossed, not on "the bit was effective".Verified live
Against an isolated stack running the exact merged server image:
View secretswhile the role still grantsConnectwrites cleanly with no dialog — the gate is not crossed.Connectraises the dialog; confirming writesallow=0 deny=5, and the server queues exactly one rotation request. The client kicksrotation-statusimmediately after the write commits.Not verified live: the roster marker, and deny-persistence across an app reload — the test fixture's second member was inserted directly into the database rather than joined through the real invite flow, so the client lost its vault record on reload. Server-side persistence is confirmed in the DB. Both are covered by unit tests.
Behaviour changes inherited from the server half
Known gap, not addressed here
assign_member_rolehas none of the four guardrails and can assign the builtin owner role, including to self — so the escalation guardrail remains bypassable by that route. Pre-existing and out of scope; flagged so no reader assumes the threat model holds end to end.Follow-ups
at()falls back to empty masks when the member is absent from the store — safe today because the panel has one caller that always sources the member from it, but it should bail out rather than write0/0.MemberDetailPanel.tsxintoservices/permissions.ts, returning a reason enum.Tests:
src/services1230/1232 (2 pre-existingdeepLinkfailures inherited from the branch point, already fixed ondevby #260),src/stores464,src/components/members124,src/hooks158,src/components/logs6,src/i18n9.tsc --noEmitclean.