Project settings: show each member once with all roles; multi-role add/edit dialog

#2529 · open · 0 comments

View on GitHub ↗

ptone

## Problem The **Members** section of project settings was built when a project member held exactly one built-in role. Since then, project-scoped custom roles have been added, and a principal can hold several role bindings in the same project. The section still renders **one row per role binding**, so a user with a built-in role and a custom role appears twice (e.g. one row `project-admin`, one row `test-custom`). Each row has its own edit and delete action. ## Current behavior (main) - `web/src/components/shared/project-members-editor.ts` - renders one table row per binding returned by `GET /api/v1/projects/{id}/members`; - the Add and Change-role dialogs use a single `sl-select`; - the role picker is filtered to the built-in membership roles (`BUILT_IN_PROJECT_MEMBERSHIP_ROLES`); custom roles are explicitly excluded ("managed via the admin role-bindings page"). - `pkg/hub/project_membership_service.go` - `AddMember` and the role update both reject non-built-in roles (`validProjectRoles`); - the one-binding invariant applies to built-in membership bindings only; custom bindings are preserved, not replaced. - Custom project-role bindings can be created only through the hub role-bindings API (`pkg/hub/handlers_roles.go`), which requires hub-level `role_binding.create`. This is the gap in miller79/scion#122. ## Desired behavior 1. **One row per member**, listing all of the member's roles in the project. 2. **Add / Edit member dialog** with multi-role selection: - built-in membership roles as **radio buttons**, at most one; **admin** selected by default; **None** is an option; - custom project-scoped roles as **checkboxes**, any number. 3. **Validation:** a member must keep at least one role binding in the project. If the selection is empty (None and no custom roles), the error offers to **remove the member** instead. 4. **Delete** on a member row removes **all** of that principal's role bindings in the project. 5. Saving applies the change as one operation: no partial state if one binding fails. ## Open questions - **Who may grant custom roles from this dialog?** Project owner only, or also project-admin, and with what delegation ceiling (e.g. only roles whose permissions the actor holds)? This is the miller79/scion#122 decision. An unported implementation exists in miller79/scion PR #127. - **Owner in the radio group?** Ownership changes are a separate Transfer Ownership flow today. Should the radio group show owner, and if so disabled unless the actor can manage owners? - Should the change also cover agent and group principals (same dialog), or users only? ## Acceptance - A member with several bindings shows as one row with all roles. - The dialog enforces at most one built-in role and any number of custom roles, defaults to admin, offers None, and blocks an empty selection with a remove-member option. - Row delete removes every binding for that principal in the project. - The existing governance checks (last-owner, delegation, audit) still apply to each binding change. - Unit tests for grouping, dialog validation and the atomic save; hub tests for any new or extended members endpoints. Addresses miller79/scion#122.

Comments