refactor: extract organization-workspace-access role (#25929)

<!-- Authored by Coder Agents on behalf of @Emyrk. -->

Refs
[PLAT-217](https://linear.app/codercom/issue/PLAT-217/rfc-for-gateway-accounts).

Extracts an `organization-workspace-access` role so workspace elevation
can be split off the organization-member floor without changing
behavior.

- New role holds the workspace-side resources currently granted by
`organization-member`.
- The `MinimumImplicitMember` floor preserves the existing behavior
until #26027 shrinks it.
- Prebuilds orchestrator inserts memberships via
`dbauthz.AsSystemRestricted` and no longer needs `OrganizationMember` or
`AssignOrgRole` grants.

<details><summary>Agent context</summary>

- `coderd/rbac/roles.go`: `OrgWorkspaceAccessMemberPerms()` grants
`Workspace`, `WorkspaceDormant`, `File` (Create+Read),
`ProvisionerDaemon` (Create+Read), and `Task`. Deliberate omissions
(`Template`, `Group`, `WorkspaceProxy`, etc.) are documented inline.
- `coderd/rbac/roles_test.go`: `orgWorkspaceAccessUser` is added to
`requiredSubjects`. `UserProvisionerDaemons` is split into
`UserProvisionerDaemonsCreate` and `UserProvisionerDaemonsUpdateDelete`
because the new role grants Create+Read only and the test framework
requires uniform pass/fail per case.
- `codersdk/rbacroles.go`: exposes `RoleOrganizationWorkspaceAccess`.
- `enterprise/coderd/prebuilds/membership.go`:
`InsertOrganizationMember` runs under `dbauthz.AsSystemRestricted`. The
orchestrator never acts with the elevation role; the membership row only
exists so prebuilt workspaces have a valid owner.
- `coderd/database/dbauthz/dbauthz.go`: drops the now-dead
`OrganizationMember` and `AssignOrgRole` permissions from the
prebuilds-orchestrator role and the orchestrator's entry in
`assignRoles`.

</details>

---

<sub>Coder Agents on behalf of @Emyrk.</sub>
This commit is contained in:
Steven Masley
2026-06-05 10:30:31 -05:00
committed by GitHub
parent 63cd8a8c01
commit bec2115e75
7 changed files with 251 additions and 228 deletions
+3 -1
View File
@@ -9,6 +9,7 @@ import (
"cdr.dev/slog/v3"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbauthz"
"github.com/coder/quartz"
)
@@ -63,7 +64,8 @@ func (s StoreMembershipReconciler) ReconcileAll(ctx context.Context, userID uuid
// Add user to org if needed
if !orgStatus.HasPrebuildUser {
_, err = s.store.InsertOrganizationMember(ctx, database.InsertOrganizationMemberParams{
//nolint:gocritic // Must use AsSystemRestricted when creating a new org member as it also assigns roles.
_, err = s.store.InsertOrganizationMember(dbauthz.AsSystemRestricted(ctx), database.InsertOrganizationMemberParams{
OrganizationID: orgStatus.OrganizationID,
UserID: userID,
CreatedAt: s.clock.Now(),
+3
View File
@@ -505,6 +505,7 @@ func TestListRoles(t *testing.T) {
{Name: codersdk.RoleOrganizationTemplateAdmin, OrganizationID: owner.OrganizationID}: false,
{Name: codersdk.RoleOrganizationUserAdmin, OrganizationID: owner.OrganizationID}: false,
{Name: codersdk.RoleOrganizationWorkspaceCreationBan, OrganizationID: owner.OrganizationID}: false,
{Name: codersdk.RoleOrganizationWorkspaceAccess, OrganizationID: owner.OrganizationID}: false,
{Name: codersdk.RoleAgentsAccess, OrganizationID: owner.OrganizationID}: false,
}),
},
@@ -539,6 +540,7 @@ func TestListRoles(t *testing.T) {
{Name: codersdk.RoleOrganizationTemplateAdmin, OrganizationID: owner.OrganizationID}: true,
{Name: codersdk.RoleOrganizationUserAdmin, OrganizationID: owner.OrganizationID}: true,
{Name: codersdk.RoleOrganizationWorkspaceCreationBan, OrganizationID: owner.OrganizationID}: true,
{Name: codersdk.RoleOrganizationWorkspaceAccess, OrganizationID: owner.OrganizationID}: true,
{Name: codersdk.RoleAgentsAccess, OrganizationID: owner.OrganizationID}: true,
}),
},
@@ -573,6 +575,7 @@ func TestListRoles(t *testing.T) {
{Name: codersdk.RoleOrganizationTemplateAdmin, OrganizationID: owner.OrganizationID}: true,
{Name: codersdk.RoleOrganizationUserAdmin, OrganizationID: owner.OrganizationID}: true,
{Name: codersdk.RoleOrganizationWorkspaceCreationBan, OrganizationID: owner.OrganizationID}: true,
{Name: codersdk.RoleOrganizationWorkspaceAccess, OrganizationID: owner.OrganizationID}: true,
{Name: codersdk.RoleAgentsAccess, OrganizationID: owner.OrganizationID}: true,
}),
},