feat(coderd): add tasks rbac object (#20234)

This change adds RBAC for tasks.

Updates coder/internal#948
Supersedes coder/coder#20212
This commit is contained in:
Mathias Fredriksson
2025-10-13 12:02:22 +03:00
committed by GitHub
parent d9f95f2285
commit 299a54a99b
19 changed files with 155 additions and 3 deletions
+12
View File
@@ -11932,6 +11932,11 @@ const docTemplate = `{
"tailnet_coordinator:delete",
"tailnet_coordinator:read",
"tailnet_coordinator:update",
"task:*",
"task:create",
"task:delete",
"task:read",
"task:update",
"template:*",
"template:create",
"template:delete",
@@ -12123,6 +12128,11 @@ const docTemplate = `{
"APIKeyScopeTailnetCoordinatorDelete",
"APIKeyScopeTailnetCoordinatorRead",
"APIKeyScopeTailnetCoordinatorUpdate",
"APIKeyScopeTaskAll",
"APIKeyScopeTaskCreate",
"APIKeyScopeTaskDelete",
"APIKeyScopeTaskRead",
"APIKeyScopeTaskUpdate",
"APIKeyScopeTemplateAll",
"APIKeyScopeTemplateCreate",
"APIKeyScopeTemplateDelete",
@@ -16977,6 +16987,7 @@ const docTemplate = `{
"replicas",
"system",
"tailnet_coordinator",
"task",
"template",
"usage_event",
"user",
@@ -17020,6 +17031,7 @@ const docTemplate = `{
"ResourceReplicas",
"ResourceSystem",
"ResourceTailnetCoordinator",
"ResourceTask",
"ResourceTemplate",
"ResourceUsageEvent",
"ResourceUser",
+12
View File
@@ -10636,6 +10636,11 @@
"tailnet_coordinator:delete",
"tailnet_coordinator:read",
"tailnet_coordinator:update",
"task:*",
"task:create",
"task:delete",
"task:read",
"task:update",
"template:*",
"template:create",
"template:delete",
@@ -10827,6 +10832,11 @@
"APIKeyScopeTailnetCoordinatorDelete",
"APIKeyScopeTailnetCoordinatorRead",
"APIKeyScopeTailnetCoordinatorUpdate",
"APIKeyScopeTaskAll",
"APIKeyScopeTaskCreate",
"APIKeyScopeTaskDelete",
"APIKeyScopeTaskRead",
"APIKeyScopeTaskUpdate",
"APIKeyScopeTemplateAll",
"APIKeyScopeTemplateCreate",
"APIKeyScopeTemplateDelete",
@@ -15499,6 +15509,7 @@
"replicas",
"system",
"tailnet_coordinator",
"task",
"template",
"usage_event",
"user",
@@ -15542,6 +15553,7 @@
"ResourceReplicas",
"ResourceSystem",
"ResourceTailnetCoordinator",
"ResourceTask",
"ResourceTemplate",
"ResourceUsageEvent",
"ResourceUser",
+3 -1
View File
@@ -219,7 +219,9 @@ var (
rbac.ResourceUser.Type: {policy.ActionRead, policy.ActionReadPersonal, policy.ActionUpdatePersonal},
rbac.ResourceWorkspaceDormant.Type: {policy.ActionDelete, policy.ActionRead, policy.ActionUpdate, policy.ActionWorkspaceStop},
rbac.ResourceWorkspace.Type: {policy.ActionDelete, policy.ActionRead, policy.ActionUpdate, policy.ActionWorkspaceStart, policy.ActionWorkspaceStop, policy.ActionCreateAgent},
rbac.ResourceApiKey.Type: {policy.WildcardSymbol},
// Provisionerd needs to read and update tasks associated with workspaces.
rbac.ResourceTask.Type: {policy.ActionRead, policy.ActionUpdate},
rbac.ResourceApiKey.Type: {policy.WildcardSymbol},
// When org scoped provisioner credentials are implemented,
// this can be reduced to read a specific org.
rbac.ResourceOrganization.Type: {policy.ActionRead},
+6 -1
View File
@@ -197,7 +197,12 @@ CREATE TYPE api_key_scope AS ENUM (
'workspace_agent_devcontainers:*',
'workspace_agent_resource_monitor:*',
'workspace_dormant:*',
'workspace_proxy:*'
'workspace_proxy:*',
'task:create',
'task:read',
'task:update',
'task:delete',
'task:*'
);
CREATE TYPE app_sharing_level AS ENUM (
@@ -0,0 +1,3 @@
-- Revert Tasks RBAC.
-- No-op: enum values remain to avoid churn. Removing enum values requires
-- doing a create/cast/drop cycle which is intentionally omitted here.
@@ -0,0 +1,6 @@
-- Tasks RBAC.
ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'task:create';
ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'task:read';
ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'task:update';
ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'task:delete';
ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS 'task:*';
+7
View File
@@ -132,6 +132,13 @@ func (w ConnectionLog) RBACObject() rbac.Object {
return obj
}
func (t Task) RBACObject() rbac.Object {
return rbac.ResourceTask.
WithID(t.ID).
WithOwner(t.OwnerID.String()).
InOrg(t.OrganizationID)
}
func (s APIKeyScope) ToRBAC() rbac.ScopeName {
switch s {
case ApiKeyScopeCoderAll:
+16 -1
View File
@@ -206,6 +206,11 @@ const (
ApiKeyScopeWorkspaceAgentResourceMonitor APIKeyScope = "workspace_agent_resource_monitor:*"
ApiKeyScopeWorkspaceDormant APIKeyScope = "workspace_dormant:*"
ApiKeyScopeWorkspaceProxy APIKeyScope = "workspace_proxy:*"
ApiKeyScopeTaskCreate APIKeyScope = "task:create"
ApiKeyScopeTaskRead APIKeyScope = "task:read"
ApiKeyScopeTaskUpdate APIKeyScope = "task:update"
ApiKeyScopeTaskDelete APIKeyScope = "task:delete"
ApiKeyScopeTask APIKeyScope = "task:*"
)
func (e *APIKeyScope) Scan(src interface{}) error {
@@ -431,7 +436,12 @@ func (e APIKeyScope) Valid() bool {
ApiKeyScopeWorkspaceAgentDevcontainers,
ApiKeyScopeWorkspaceAgentResourceMonitor,
ApiKeyScopeWorkspaceDormant,
ApiKeyScopeWorkspaceProxy:
ApiKeyScopeWorkspaceProxy,
ApiKeyScopeTaskCreate,
ApiKeyScopeTaskRead,
ApiKeyScopeTaskUpdate,
ApiKeyScopeTaskDelete,
ApiKeyScopeTask:
return true
}
return false
@@ -626,6 +636,11 @@ func AllAPIKeyScopeValues() []APIKeyScope {
ApiKeyScopeWorkspaceAgentResourceMonitor,
ApiKeyScopeWorkspaceDormant,
ApiKeyScopeWorkspaceProxy,
ApiKeyScopeTaskCreate,
ApiKeyScopeTaskRead,
ApiKeyScopeTaskUpdate,
ApiKeyScopeTaskDelete,
ApiKeyScopeTask,
}
}
+11
View File
@@ -286,6 +286,16 @@ var (
Type: "tailnet_coordinator",
}
// ResourceTask
// Valid Actions
// - "ActionCreate" :: create a new task
// - "ActionDelete" :: delete task
// - "ActionRead" :: read task data or output to view on the UI or CLI
// - "ActionUpdate" :: edit task settings or send input to an existing task
ResourceTask = Object{
Type: "task",
}
// ResourceTemplate
// Valid Actions
// - "ActionCreate" :: create a template
@@ -430,6 +440,7 @@ func AllResources() []Objecter {
ResourceReplicas,
ResourceSystem,
ResourceTailnetCoordinator,
ResourceTask,
ResourceTemplate,
ResourceUsageEvent,
ResourceUser,
+10
View File
@@ -63,6 +63,13 @@ var workspaceActions = map[Action]ActionDefinition{
ActionDeleteAgent: "delete an existing workspace agent",
}
var taskActions = map[Action]ActionDefinition{
ActionCreate: "create a new task",
ActionRead: "read task data or output to view on the UI or CLI",
ActionUpdate: "edit task settings or send input to an existing task",
ActionDelete: "delete task",
}
// RBACPermissions is indexed by the type
var RBACPermissions = map[string]PermissionDefinition{
// Wildcard is every object, and the action "*" provides all actions.
@@ -86,6 +93,9 @@ var RBACPermissions = map[string]PermissionDefinition{
"workspace": {
Actions: workspaceActions,
},
"task": {
Actions: taskActions,
},
// Dormant workspaces have the same perms as workspaces.
"workspace_dormant": {
Actions: workspaceActions,
+9
View File
@@ -505,6 +505,15 @@ func TestRolePermissions(t *testing.T) {
false: {setOtherOrg, userAdmin, memberMe, orgUserAdmin, orgAuditor, orgMemberMe},
},
},
{
Name: "Task",
Actions: crud,
Resource: rbac.ResourceTask.WithID(uuid.New()).InOrg(orgID).WithOwner(memberMe.Actor.ID),
AuthorizeMap: map[bool][]hasAuthSubjects{
true: {owner, orgAdmin, orgMemberMe},
false: {setOtherOrg, userAdmin, templateAdmin, memberMe, orgTemplateAdmin, orgUserAdmin, orgAuditor},
},
},
// Some admin style resources
{
Name: "Licenses",
+7
View File
@@ -50,6 +50,13 @@ var externalLowLevel = map[ScopeName]struct{}{
"user_secret:update": {},
"user_secret:delete": {},
"user_secret:*": {},
// Tasks
"task:create": {},
"task:read": {},
"task:update": {},
"task:delete": {},
"task:*": {},
}
// Public composite coder:* scopes exposed to users.
+12
View File
@@ -95,6 +95,10 @@ const (
ScopeTailnetCoordinatorDelete ScopeName = "tailnet_coordinator:delete"
ScopeTailnetCoordinatorRead ScopeName = "tailnet_coordinator:read"
ScopeTailnetCoordinatorUpdate ScopeName = "tailnet_coordinator:update"
ScopeTaskCreate ScopeName = "task:create"
ScopeTaskDelete ScopeName = "task:delete"
ScopeTaskRead ScopeName = "task:read"
ScopeTaskUpdate ScopeName = "task:update"
ScopeTemplateCreate ScopeName = "template:create"
ScopeTemplateDelete ScopeName = "template:delete"
ScopeTemplateRead ScopeName = "template:read"
@@ -244,6 +248,10 @@ func (e ScopeName) Valid() bool {
ScopeTailnetCoordinatorDelete,
ScopeTailnetCoordinatorRead,
ScopeTailnetCoordinatorUpdate,
ScopeTaskCreate,
ScopeTaskDelete,
ScopeTaskRead,
ScopeTaskUpdate,
ScopeTemplateCreate,
ScopeTemplateDelete,
ScopeTemplateRead,
@@ -394,6 +402,10 @@ func AllScopeNameValues() []ScopeName {
ScopeTailnetCoordinatorDelete,
ScopeTailnetCoordinatorRead,
ScopeTailnetCoordinatorUpdate,
ScopeTaskCreate,
ScopeTaskDelete,
ScopeTaskRead,
ScopeTaskUpdate,
ScopeTemplateCreate,
ScopeTemplateDelete,
ScopeTemplateRead,
+10
View File
@@ -133,6 +133,11 @@ const (
APIKeyScopeTailnetCoordinatorDelete APIKeyScope = "tailnet_coordinator:delete"
APIKeyScopeTailnetCoordinatorRead APIKeyScope = "tailnet_coordinator:read"
APIKeyScopeTailnetCoordinatorUpdate APIKeyScope = "tailnet_coordinator:update"
APIKeyScopeTaskAll APIKeyScope = "task:*"
APIKeyScopeTaskCreate APIKeyScope = "task:create"
APIKeyScopeTaskDelete APIKeyScope = "task:delete"
APIKeyScopeTaskRead APIKeyScope = "task:read"
APIKeyScopeTaskUpdate APIKeyScope = "task:update"
APIKeyScopeTemplateAll APIKeyScope = "template:*"
APIKeyScopeTemplateCreate APIKeyScope = "template:create"
APIKeyScopeTemplateDelete APIKeyScope = "template:delete"
@@ -214,6 +219,11 @@ var PublicAPIKeyScopes = []APIKeyScope{
APIKeyScopeFileAll,
APIKeyScopeFileCreate,
APIKeyScopeFileRead,
APIKeyScopeTaskAll,
APIKeyScopeTaskCreate,
APIKeyScopeTaskDelete,
APIKeyScopeTaskRead,
APIKeyScopeTaskUpdate,
APIKeyScopeTemplateAll,
APIKeyScopeTemplateCreate,
APIKeyScopeTemplateDelete,
+2
View File
@@ -35,6 +35,7 @@ const (
ResourceReplicas RBACResource = "replicas"
ResourceSystem RBACResource = "system"
ResourceTailnetCoordinator RBACResource = "tailnet_coordinator"
ResourceTask RBACResource = "task"
ResourceTemplate RBACResource = "template"
ResourceUsageEvent RBACResource = "usage_event"
ResourceUser RBACResource = "user"
@@ -102,6 +103,7 @@ var RBACResourceActions = map[RBACResource][]RBACAction{
ResourceReplicas: {ActionRead},
ResourceSystem: {ActionCreate, ActionDelete, ActionRead, ActionUpdate},
ResourceTailnetCoordinator: {ActionCreate, ActionDelete, ActionRead, ActionUpdate},
ResourceTask: {ActionCreate, ActionDelete, ActionRead, ActionUpdate},
ResourceTemplate: {ActionCreate, ActionDelete, ActionRead, ActionUpdate, ActionUse, ActionViewInsights},
ResourceUsageEvent: {ActionCreate, ActionRead, ActionUpdate},
ResourceUser: {ActionCreate, ActionDelete, ActionRead, ActionReadPersonal, ActionUpdate, ActionUpdatePersonal},
+5
View File
@@ -213,6 +213,7 @@ Status Code **200**
| `resource_type` | `replicas` |
| `resource_type` | `system` |
| `resource_type` | `tailnet_coordinator` |
| `resource_type` | `task` |
| `resource_type` | `template` |
| `resource_type` | `usage_event` |
| `resource_type` | `user` |
@@ -386,6 +387,7 @@ Status Code **200**
| `resource_type` | `replicas` |
| `resource_type` | `system` |
| `resource_type` | `tailnet_coordinator` |
| `resource_type` | `task` |
| `resource_type` | `template` |
| `resource_type` | `usage_event` |
| `resource_type` | `user` |
@@ -559,6 +561,7 @@ Status Code **200**
| `resource_type` | `replicas` |
| `resource_type` | `system` |
| `resource_type` | `tailnet_coordinator` |
| `resource_type` | `task` |
| `resource_type` | `template` |
| `resource_type` | `usage_event` |
| `resource_type` | `user` |
@@ -701,6 +704,7 @@ Status Code **200**
| `resource_type` | `replicas` |
| `resource_type` | `system` |
| `resource_type` | `tailnet_coordinator` |
| `resource_type` | `task` |
| `resource_type` | `template` |
| `resource_type` | `usage_event` |
| `resource_type` | `user` |
@@ -1065,6 +1069,7 @@ Status Code **200**
| `resource_type` | `replicas` |
| `resource_type` | `system` |
| `resource_type` | `tailnet_coordinator` |
| `resource_type` | `task` |
| `resource_type` | `template` |
| `resource_type` | `usage_event` |
| `resource_type` | `user` |
+6
View File
@@ -909,6 +909,11 @@
| `tailnet_coordinator:delete` |
| `tailnet_coordinator:read` |
| `tailnet_coordinator:update` |
| `task:*` |
| `task:create` |
| `task:delete` |
| `task:read` |
| `task:update` |
| `template:*` |
| `template:create` |
| `template:delete` |
@@ -7110,6 +7115,7 @@ Only certain features set these fields: - FeatureManagedAgentLimit|
| `replicas` |
| `system` |
| `tailnet_coordinator` |
| `task` |
| `template` |
| `usage_event` |
| `user` |
+6
View File
@@ -156,6 +156,12 @@ export const RBACResourceActions: Partial<
read: "view info about a Tailnet coordinator",
update: "update a Tailnet coordinator",
},
task: {
create: "create a new task",
delete: "delete task",
read: "read task data or output to view on the UI or CLI",
update: "edit task settings or send input to an existing task",
},
template: {
create: "create a template",
delete: "delete a template",
+12
View File
@@ -247,6 +247,11 @@ export type APIKeyScope =
| "tailnet_coordinator:delete"
| "tailnet_coordinator:read"
| "tailnet_coordinator:update"
| "task:*"
| "task:create"
| "task:delete"
| "task:read"
| "task:update"
| "template:*"
| "template:create"
| "template:delete"
@@ -438,6 +443,11 @@ export const APIKeyScopes: APIKeyScope[] = [
"tailnet_coordinator:delete",
"tailnet_coordinator:read",
"tailnet_coordinator:update",
"task:*",
"task:create",
"task:delete",
"task:read",
"task:update",
"template:*",
"template:create",
"template:delete",
@@ -2915,6 +2925,7 @@ export type RBACResource =
| "replicas"
| "system"
| "tailnet_coordinator"
| "task"
| "template"
| "usage_event"
| "user"
@@ -2958,6 +2969,7 @@ export const RBACResources: RBACResource[] = [
"replicas",
"system",
"tailnet_coordinator",
"task",
"template",
"usage_event",
"user",