mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix: introduce ResourceAiSeat for fine-grained AI seat RBAC (#24613)
Fixes: https://github.com/coder/internal/issues/1444
This commit is contained in:
@@ -15,6 +15,14 @@ var (
|
||||
Type: "*",
|
||||
}
|
||||
|
||||
// ResourceAiSeat
|
||||
// Valid Actions
|
||||
// - "ActionCreate" :: record AI seat usage
|
||||
// - "ActionRead" :: read AI seat state
|
||||
ResourceAiSeat = Object{
|
||||
Type: "ai_seat",
|
||||
}
|
||||
|
||||
// ResourceAibridgeInterception
|
||||
// Valid Actions
|
||||
// - "ActionCreate" :: create aibridge interceptions & related records
|
||||
@@ -433,6 +441,7 @@ var (
|
||||
func AllResources() []Objecter {
|
||||
return []Objecter{
|
||||
ResourceWildcard,
|
||||
ResourceAiSeat,
|
||||
ResourceAibridgeInterception,
|
||||
ResourceApiKey,
|
||||
ResourceAssignOrgRole,
|
||||
|
||||
@@ -392,6 +392,12 @@ var RBACPermissions = map[string]PermissionDefinition{
|
||||
ActionCreate: "create aibridge interceptions & related records",
|
||||
},
|
||||
},
|
||||
"ai_seat": {
|
||||
Actions: map[Action]ActionDefinition{
|
||||
ActionCreate: "record AI seat usage",
|
||||
ActionRead: "read AI seat state",
|
||||
},
|
||||
},
|
||||
"boundary_usage": {
|
||||
Actions: map[Action]ActionDefinition{
|
||||
ActionRead: "read boundary usage statistics",
|
||||
|
||||
@@ -294,7 +294,7 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
|
||||
// Workspace dormancy and workspace are omitted.
|
||||
// Workspace is specifically handled based on the opts.NoOwnerWorkspaceExec.
|
||||
// Owners cannot access other users' secrets.
|
||||
allPermsExcept(ResourceWorkspaceDormant, ResourcePrebuiltWorkspace, ResourceWorkspace, ResourceUserSecret, ResourceUsageEvent, ResourceBoundaryUsage),
|
||||
allPermsExcept(ResourceWorkspaceDormant, ResourcePrebuiltWorkspace, ResourceWorkspace, ResourceUserSecret, ResourceUsageEvent, ResourceBoundaryUsage, ResourceAiSeat),
|
||||
// This adds back in the Workspace permissions.
|
||||
Permissions(map[string][]policy.Action{
|
||||
ResourceWorkspace.Type: ownerWorkspaceActions,
|
||||
@@ -322,7 +322,7 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
|
||||
denyPermissions...,
|
||||
),
|
||||
User: append(
|
||||
allPermsExcept(ResourceWorkspaceDormant, ResourcePrebuiltWorkspace, ResourceWorkspace, ResourceUser, ResourceOrganizationMember, ResourceBoundaryUsage, ResourceAibridgeInterception, ResourceChat),
|
||||
allPermsExcept(ResourceWorkspaceDormant, ResourcePrebuiltWorkspace, ResourceWorkspace, ResourceUser, ResourceOrganizationMember, ResourceBoundaryUsage, ResourceAibridgeInterception, ResourceChat, ResourceAiSeat),
|
||||
Permissions(map[string][]policy.Action{
|
||||
// Users cannot do create/update/delete on themselves, but they
|
||||
// can read their own details.
|
||||
@@ -454,7 +454,7 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
|
||||
// Org admins should not have workspace exec perms.
|
||||
organizationID.String(): {
|
||||
Org: append(
|
||||
allPermsExcept(ResourceWorkspace, ResourceWorkspaceDormant, ResourcePrebuiltWorkspace, ResourceAssignRole, ResourceUserSecret, ResourceBoundaryUsage),
|
||||
allPermsExcept(ResourceWorkspace, ResourceWorkspaceDormant, ResourcePrebuiltWorkspace, ResourceAssignRole, ResourceUserSecret, ResourceBoundaryUsage, ResourceAiSeat),
|
||||
Permissions(map[string][]policy.Action{
|
||||
ResourceWorkspace.Type: slice.Omit(ResourceWorkspace.AvailableActions(), policy.ActionApplicationConnect, policy.ActionSSH),
|
||||
ResourceWorkspaceDormant.Type: {policy.ActionRead, policy.ActionDelete, policy.ActionCreate, policy.ActionUpdate, policy.ActionWorkspaceStop, policy.ActionCreateAgent, policy.ActionDeleteAgent, policy.ActionUpdateAgent},
|
||||
|
||||
@@ -1104,6 +1104,14 @@ func TestRolePermissions(t *testing.T) {
|
||||
false: {owner, setOtherOrg, setOrgNotMe, memberMe, agentsAccessUser, templateAdmin, userAdmin},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "AiSeat",
|
||||
Actions: []policy.Action{policy.ActionCreate, policy.ActionRead},
|
||||
Resource: rbac.ResourceAiSeat,
|
||||
AuthorizeMap: map[bool][]hasAuthSubjects{
|
||||
false: {owner, setOtherOrg, setOrgNotMe, memberMe, agentsAccessUser, templateAdmin, userAdmin},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "ChatUsageCRU",
|
||||
Actions: []policy.Action{policy.ActionCreate, policy.ActionRead, policy.ActionUpdate},
|
||||
|
||||
@@ -7,6 +7,8 @@ package rbac
|
||||
// declared in code, not here, to avoid duplication.
|
||||
|
||||
const (
|
||||
ScopeAiSeatCreate ScopeName = "ai_seat:create"
|
||||
ScopeAiSeatRead ScopeName = "ai_seat:read"
|
||||
ScopeAibridgeInterceptionCreate ScopeName = "aibridge_interception:create"
|
||||
ScopeAibridgeInterceptionRead ScopeName = "aibridge_interception:read"
|
||||
ScopeAibridgeInterceptionUpdate ScopeName = "aibridge_interception:update"
|
||||
@@ -171,6 +173,8 @@ func (e ScopeName) Valid() bool {
|
||||
case ScopeName("coder:all"),
|
||||
ScopeName("coder:application_connect"),
|
||||
ScopeName("no_user_data"),
|
||||
ScopeAiSeatCreate,
|
||||
ScopeAiSeatRead,
|
||||
ScopeAibridgeInterceptionCreate,
|
||||
ScopeAibridgeInterceptionRead,
|
||||
ScopeAibridgeInterceptionUpdate,
|
||||
@@ -336,6 +340,8 @@ func AllScopeNameValues() []ScopeName {
|
||||
ScopeName("coder:all"),
|
||||
ScopeName("coder:application_connect"),
|
||||
ScopeName("no_user_data"),
|
||||
ScopeAiSeatCreate,
|
||||
ScopeAiSeatRead,
|
||||
ScopeAibridgeInterceptionCreate,
|
||||
ScopeAibridgeInterceptionRead,
|
||||
ScopeAibridgeInterceptionUpdate,
|
||||
|
||||
Reference in New Issue
Block a user