mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
refactor(coderd/rbac): enumerate org-member and org-service-account perms
Replace allPermsExcept in OrgMemberPermissions and OrgServiceAccountPermissions with explicit per-resource enumerations. allPermsExcept granted wildcard actions on every resource not in its exclusion list, which auto-granted any new resource added to the codebase and made the actual perm surface hard to audit. The enumeration grants only the resources actually relevant to member/service-account operations: workspace lifecycle and runtime support, template apply, file upload/read for builds, provisioner jobs, tasks, group reads for ACL eval, org-member read-self, AI Bridge interception writes, own API keys, user-scoped notification surfaces, and replica metadata. Behavior-preserving: all rbac, dbauthz, coderd workspace/template/ user/org/notification/key/provisioner/audit/proxy/task tests, and enterprise/coderd tests pass.
This commit is contained in:
+122
-75
@@ -1055,44 +1055,69 @@ func OrgMemberPermissions(org OrgSettings) OrgRolePermissions {
|
||||
})
|
||||
}
|
||||
|
||||
// Uses allPermsExcept to automatically include permissions for new resources.
|
||||
memberPerms := append(
|
||||
allPermsExcept(
|
||||
ResourceWorkspaceDormant,
|
||||
ResourcePrebuiltWorkspace,
|
||||
ResourceUser,
|
||||
ResourceOrganizationMember,
|
||||
ResourceBoundaryLog,
|
||||
ResourceAibridgeInterception,
|
||||
// Chat access requires the agents-access role.
|
||||
ResourceChat,
|
||||
),
|
||||
// Enumerate the per-member resources explicitly so new resources do
|
||||
// not auto-grant to org members. Adding a resource to the codebase
|
||||
// requires an explicit decision to expose it here.
|
||||
memberPerms := Permissions(map[string][]policy.Action{
|
||||
// Workspace lifecycle on resources owned by this member.
|
||||
ResourceWorkspace.Type: ResourceWorkspace.AvailableActions(),
|
||||
|
||||
Permissions(map[string][]policy.Action{
|
||||
// Reduced permission set on dormant workspaces. No build,
|
||||
// ssh, or exec.
|
||||
ResourceWorkspaceDormant.Type: {
|
||||
policy.ActionRead,
|
||||
policy.ActionDelete,
|
||||
policy.ActionCreate,
|
||||
policy.ActionUpdate,
|
||||
policy.ActionWorkspaceStop,
|
||||
policy.ActionCreateAgent,
|
||||
policy.ActionDeleteAgent,
|
||||
policy.ActionUpdateAgent,
|
||||
},
|
||||
// Can read their own organization member record.
|
||||
ResourceOrganizationMember.Type: {
|
||||
policy.ActionRead,
|
||||
},
|
||||
// Members can create and update AI Bridge interceptions but
|
||||
// cannot read them back.
|
||||
ResourceAibridgeInterception.Type: {
|
||||
policy.ActionCreate,
|
||||
policy.ActionUpdate,
|
||||
},
|
||||
})...,
|
||||
)
|
||||
// Dormant workspaces share the workspace action set minus the
|
||||
// build, ssh, and exec actions.
|
||||
ResourceWorkspaceDormant.Type: {
|
||||
policy.ActionRead,
|
||||
policy.ActionDelete,
|
||||
policy.ActionCreate,
|
||||
policy.ActionUpdate,
|
||||
policy.ActionWorkspaceStop,
|
||||
policy.ActionCreateAgent,
|
||||
policy.ActionDeleteAgent,
|
||||
policy.ActionUpdateAgent,
|
||||
},
|
||||
|
||||
// Workspace runtime support: proxies, agent monitors,
|
||||
// devcontainer setup, and tailnet coordination.
|
||||
ResourceWorkspaceProxy.Type: {policy.ActionRead},
|
||||
ResourceWorkspaceAgentResourceMonitor.Type: ResourceWorkspaceAgentResourceMonitor.AvailableActions(),
|
||||
ResourceWorkspaceAgentDevcontainers.Type: ResourceWorkspaceAgentDevcontainers.AvailableActions(),
|
||||
ResourceTailnetCoordinator.Type: ResourceTailnetCoordinator.AvailableActions(),
|
||||
|
||||
// Apply templates; full template lifecycle is restricted to
|
||||
// template-admin.
|
||||
ResourceTemplate.Type: {policy.ActionRead, policy.ActionUse},
|
||||
|
||||
// Upload and read template files used during workspace build.
|
||||
ResourceFile.Type: {policy.ActionCreate, policy.ActionRead},
|
||||
|
||||
// Provisioner jobs back workspace builds.
|
||||
ResourceProvisionerJobs.Type: ResourceProvisionerJobs.AvailableActions(),
|
||||
|
||||
// Tasks ride along with workspaces.
|
||||
ResourceTask.Type: ResourceTask.AvailableActions(),
|
||||
|
||||
// Read groups and group memberships for ACL evaluation.
|
||||
ResourceGroup.Type: {policy.ActionRead},
|
||||
ResourceGroupMember.Type: {policy.ActionRead},
|
||||
|
||||
// Read-self org-member record.
|
||||
ResourceOrganizationMember.Type: {policy.ActionRead},
|
||||
|
||||
// Members can create and update AI Bridge interceptions but
|
||||
// cannot read them back. Chat access requires the agents-access
|
||||
// role and is intentionally not granted here.
|
||||
ResourceAibridgeInterception.Type: {policy.ActionCreate, policy.ActionUpdate},
|
||||
|
||||
// Own session tokens and workspace agent auth keys.
|
||||
ResourceApiKey.Type: ResourceApiKey.AvailableActions(),
|
||||
|
||||
// User-scoped notification surfaces.
|
||||
ResourceNotificationMessage.Type: {policy.ActionRead, policy.ActionUpdate},
|
||||
ResourceNotificationPreference.Type: ResourceNotificationPreference.AvailableActions(),
|
||||
ResourceInboxNotification.Type: ResourceInboxNotification.AvailableActions(),
|
||||
|
||||
// Replica metadata (read-only is the only defined action).
|
||||
ResourceReplicas.Type: {policy.ActionRead},
|
||||
})
|
||||
|
||||
if org.ShareableWorkspaceOwners != ShareableWorkspaceOwnersEveryone {
|
||||
memberPerms = append(memberPerms, Permission{
|
||||
@@ -1140,45 +1165,67 @@ func OrgServiceAccountPermissions(org OrgSettings) OrgRolePermissions {
|
||||
}
|
||||
|
||||
// service account-scoped permissions (resources owned by the
|
||||
// service account). Uses allPermsExcept to automatically include
|
||||
// permissions for new resources.
|
||||
memberPerms := append(
|
||||
allPermsExcept(
|
||||
ResourceWorkspaceDormant,
|
||||
ResourcePrebuiltWorkspace,
|
||||
ResourceUser,
|
||||
ResourceOrganizationMember,
|
||||
ResourceBoundaryLog,
|
||||
ResourceAibridgeInterception,
|
||||
// Chat access requires the agents-access role.
|
||||
ResourceChat,
|
||||
),
|
||||
// service account). Enumerated explicitly so new resources do not
|
||||
// auto-grant to service accounts.
|
||||
memberPerms := Permissions(map[string][]policy.Action{
|
||||
// Workspace lifecycle on resources owned by this service account.
|
||||
ResourceWorkspace.Type: ResourceWorkspace.AvailableActions(),
|
||||
|
||||
Permissions(map[string][]policy.Action{
|
||||
// Reduced permission set on dormant workspaces. No build,
|
||||
// ssh, or exec.
|
||||
ResourceWorkspaceDormant.Type: {
|
||||
policy.ActionRead,
|
||||
policy.ActionDelete,
|
||||
policy.ActionCreate,
|
||||
policy.ActionUpdate,
|
||||
policy.ActionWorkspaceStop,
|
||||
policy.ActionCreateAgent,
|
||||
policy.ActionDeleteAgent,
|
||||
policy.ActionUpdateAgent,
|
||||
},
|
||||
// Can read their own organization member record.
|
||||
ResourceOrganizationMember.Type: {
|
||||
policy.ActionRead,
|
||||
},
|
||||
// Service accounts can create and update AI Bridge
|
||||
// interceptions but cannot read them back.
|
||||
ResourceAibridgeInterception.Type: {
|
||||
policy.ActionCreate,
|
||||
policy.ActionUpdate,
|
||||
},
|
||||
})...,
|
||||
)
|
||||
// Dormant workspaces share the workspace action set minus the
|
||||
// build, ssh, and exec actions.
|
||||
ResourceWorkspaceDormant.Type: {
|
||||
policy.ActionRead,
|
||||
policy.ActionDelete,
|
||||
policy.ActionCreate,
|
||||
policy.ActionUpdate,
|
||||
policy.ActionWorkspaceStop,
|
||||
policy.ActionCreateAgent,
|
||||
policy.ActionDeleteAgent,
|
||||
policy.ActionUpdateAgent,
|
||||
},
|
||||
|
||||
// Workspace runtime support.
|
||||
ResourceWorkspaceProxy.Type: {policy.ActionRead},
|
||||
ResourceWorkspaceAgentResourceMonitor.Type: ResourceWorkspaceAgentResourceMonitor.AvailableActions(),
|
||||
ResourceWorkspaceAgentDevcontainers.Type: ResourceWorkspaceAgentDevcontainers.AvailableActions(),
|
||||
ResourceTailnetCoordinator.Type: ResourceTailnetCoordinator.AvailableActions(),
|
||||
|
||||
// Apply templates; full template lifecycle is restricted to
|
||||
// template-admin.
|
||||
ResourceTemplate.Type: {policy.ActionRead, policy.ActionUse},
|
||||
|
||||
// Upload and read template files used during workspace build.
|
||||
ResourceFile.Type: {policy.ActionCreate, policy.ActionRead},
|
||||
|
||||
// Provisioner jobs back workspace builds.
|
||||
ResourceProvisionerJobs.Type: ResourceProvisionerJobs.AvailableActions(),
|
||||
|
||||
// Tasks ride along with workspaces.
|
||||
ResourceTask.Type: ResourceTask.AvailableActions(),
|
||||
|
||||
// Read groups and group memberships for ACL evaluation.
|
||||
ResourceGroup.Type: {policy.ActionRead},
|
||||
ResourceGroupMember.Type: {policy.ActionRead},
|
||||
|
||||
// Read-self org-member record.
|
||||
ResourceOrganizationMember.Type: {policy.ActionRead},
|
||||
|
||||
// Service accounts can create and update AI Bridge interceptions
|
||||
// but cannot read them back. Chat access requires the
|
||||
// agents-access role and is intentionally not granted here.
|
||||
ResourceAibridgeInterception.Type: {policy.ActionCreate, policy.ActionUpdate},
|
||||
|
||||
// Own session tokens and workspace agent auth keys.
|
||||
ResourceApiKey.Type: ResourceApiKey.AvailableActions(),
|
||||
|
||||
// User-scoped notification surfaces.
|
||||
ResourceNotificationMessage.Type: {policy.ActionRead, policy.ActionUpdate},
|
||||
ResourceNotificationPreference.Type: ResourceNotificationPreference.AvailableActions(),
|
||||
ResourceInboxNotification.Type: ResourceInboxNotification.AvailableActions(),
|
||||
|
||||
// Replica metadata (read-only is the only defined action).
|
||||
ResourceReplicas.Type: {policy.ActionRead},
|
||||
})
|
||||
|
||||
return OrgRolePermissions{Org: orgPerms, Member: memberPerms}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user