mirror of https://github.com/coder/coder.git
feat: Option to remove WorkspaceExec from `owner` role (#7050)
* chore: Add AllResources option for listing all RBAC objects * Owners cannot do workspace exec site wide * Fix FE authchecks to valid RBAC resources
This commit is contained in:
parent
ad2353c3d8
commit
9d39371ee0
9
Makefile
9
Makefile
|
@ -423,6 +423,7 @@ gen: \
|
|||
provisionersdk/proto/provisioner.pb.go \
|
||||
provisionerd/proto/provisionerd.pb.go \
|
||||
site/src/api/typesGenerated.ts \
|
||||
coderd/rbac/object_gen.go \
|
||||
docs/admin/prometheus.md \
|
||||
docs/cli.md \
|
||||
docs/admin/audit-logs.md \
|
||||
|
@ -443,6 +444,7 @@ gen/mark-fresh:
|
|||
provisionersdk/proto/provisioner.pb.go \
|
||||
provisionerd/proto/provisionerd.pb.go \
|
||||
site/src/api/typesGenerated.ts \
|
||||
coderd/rbac/object_gen.go \
|
||||
docs/admin/prometheus.md \
|
||||
docs/cli.md \
|
||||
docs/admin/audit-logs.md \
|
||||
|
@ -495,6 +497,9 @@ site/src/api/typesGenerated.ts: scripts/apitypings/main.go $(shell find ./coders
|
|||
cd site
|
||||
yarn run format:types
|
||||
|
||||
coderd/rbac/object_gen.go: scripts/rbacgen/main.go coderd/rbac/object.go
|
||||
go run scripts/rbacgen/main.go ./coderd/rbac > coderd/rbac/object_gen.go
|
||||
|
||||
docs/admin/prometheus.md: scripts/metricsdocgen/main.go scripts/metricsdocgen/metrics
|
||||
go run scripts/metricsdocgen/main.go
|
||||
cd site
|
||||
|
@ -505,12 +510,12 @@ docs/cli.md: scripts/clidocgen/main.go $(GO_SRC_FILES) docs/manifest.json
|
|||
cd site
|
||||
yarn run format:write:only ../docs/cli.md ../docs/cli/*.md ../docs/manifest.json
|
||||
|
||||
docs/admin/audit-logs.md: scripts/auditdocgen/main.go enterprise/audit/table.go
|
||||
docs/admin/audit-logs.md: scripts/auditdocgen/main.go enterprise/audit/table.go coderd/rbac/object_gen.go
|
||||
go run scripts/auditdocgen/main.go
|
||||
cd site
|
||||
yarn run format:write:only ../docs/admin/audit-logs.md
|
||||
|
||||
coderd/apidoc/swagger.json: $(shell find ./scripts/apidocgen $(FIND_EXCLUSIONS) -type f) $(wildcard coderd/*.go) $(wildcard enterprise/coderd/*.go) $(wildcard codersdk/*.go) .swaggo docs/manifest.json
|
||||
coderd/apidoc/swagger.json: $(shell find ./scripts/apidocgen $(FIND_EXCLUSIONS) -type f) $(wildcard coderd/*.go) $(wildcard enterprise/coderd/*.go) $(wildcard codersdk/*.go) .swaggo docs/manifest.json coderd/rbac/object_gen.go
|
||||
./scripts/apidocgen/generate.sh
|
||||
yarn run --cwd=site format:write:only ../docs/api ../docs/manifest.json ../coderd/apidoc/swagger.json
|
||||
|
||||
|
|
|
@ -16,6 +16,12 @@ Start a Coder server
|
|||
$CACHE_DIRECTORY is set, it will be used for compatibility with
|
||||
systemd.
|
||||
|
||||
--disable-owner-workspace-access bool, $CODER_DISABLE_OWNER_WORKSPACE_ACCESS
|
||||
Remove the permission for the 'owner' role to have workspace execution
|
||||
on all workspaces. This prevents the 'owner' from ssh, apps, and
|
||||
terminal access based on the 'owner' role. They still have their user
|
||||
permissions to access their own workspaces.
|
||||
|
||||
--disable-path-apps bool, $CODER_DISABLE_PATH_APPS
|
||||
Disable workspace apps that are not served from subdomains. Path-based
|
||||
apps can make requests to the Coder API and pose a security risk when
|
||||
|
|
|
@ -315,6 +315,12 @@ agentFallbackTroubleshootingURL: https://coder.com/docs/coder-oss/latest/templat
|
|||
# --wildcard-access-url is configured.
|
||||
# (default: <unset>, type: bool)
|
||||
disablePathApps: false
|
||||
# Remove the permission for the 'owner' role to have workspace execution on all
|
||||
# workspaces. This prevents the 'owner' from ssh, apps, and terminal access based
|
||||
# on the 'owner' role. They still have their user permissions to access their own
|
||||
# workspaces.
|
||||
# (default: <unset>, type: bool)
|
||||
disableOwnerWorkspaceAccess: false
|
||||
# These options change the behavior of how clients interact with the Coder.
|
||||
# Clients include the coder cli, vs code extension, and the web UI.
|
||||
client:
|
||||
|
|
|
@ -6290,7 +6290,11 @@ const docTemplate = `{
|
|||
},
|
||||
"resource_type": {
|
||||
"description": "ResourceType is the name of the resource.\n` + "`" + `./coderd/rbac/object.go` + "`" + ` has the list of valid resource types.",
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/codersdk.RBACResource"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -6985,6 +6989,9 @@ const docTemplate = `{
|
|||
"derp": {
|
||||
"$ref": "#/definitions/codersdk.DERP"
|
||||
},
|
||||
"disable_owner_workspace_exec": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"disable_password_auth": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
@ -8023,6 +8030,57 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"codersdk.RBACResource": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"workspace",
|
||||
"workspace_proxy",
|
||||
"workspace_execution",
|
||||
"application_connect",
|
||||
"audit_log",
|
||||
"template",
|
||||
"group",
|
||||
"file",
|
||||
"provisioner_daemon",
|
||||
"organization",
|
||||
"assign_role",
|
||||
"assign_org_role",
|
||||
"api_key",
|
||||
"user",
|
||||
"user_data",
|
||||
"organization_member",
|
||||
"license",
|
||||
"deployment_config",
|
||||
"deployment_stats",
|
||||
"replicas",
|
||||
"debug_info",
|
||||
"system"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"ResourceWorkspace",
|
||||
"ResourceWorkspaceProxy",
|
||||
"ResourceWorkspaceExecution",
|
||||
"ResourceWorkspaceApplicationConnect",
|
||||
"ResourceAuditLog",
|
||||
"ResourceTemplate",
|
||||
"ResourceGroup",
|
||||
"ResourceFile",
|
||||
"ResourceProvisionerDaemon",
|
||||
"ResourceOrganization",
|
||||
"ResourceRoleAssignment",
|
||||
"ResourceOrgRoleAssignment",
|
||||
"ResourceAPIKey",
|
||||
"ResourceUser",
|
||||
"ResourceUserData",
|
||||
"ResourceOrganizationMember",
|
||||
"ResourceLicense",
|
||||
"ResourceDeploymentValues",
|
||||
"ResourceDeploymentStats",
|
||||
"ResourceReplicas",
|
||||
"ResourceDebugInfo",
|
||||
"ResourceSystem"
|
||||
]
|
||||
},
|
||||
"codersdk.RateLimitConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
@ -5600,7 +5600,11 @@
|
|||
},
|
||||
"resource_type": {
|
||||
"description": "ResourceType is the name of the resource.\n`./coderd/rbac/object.go` has the list of valid resource types.",
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/codersdk.RBACResource"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -6237,6 +6241,9 @@
|
|||
"derp": {
|
||||
"$ref": "#/definitions/codersdk.DERP"
|
||||
},
|
||||
"disable_owner_workspace_exec": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"disable_password_auth": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
@ -7182,6 +7189,57 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"codersdk.RBACResource": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"workspace",
|
||||
"workspace_proxy",
|
||||
"workspace_execution",
|
||||
"application_connect",
|
||||
"audit_log",
|
||||
"template",
|
||||
"group",
|
||||
"file",
|
||||
"provisioner_daemon",
|
||||
"organization",
|
||||
"assign_role",
|
||||
"assign_org_role",
|
||||
"api_key",
|
||||
"user",
|
||||
"user_data",
|
||||
"organization_member",
|
||||
"license",
|
||||
"deployment_config",
|
||||
"deployment_stats",
|
||||
"replicas",
|
||||
"debug_info",
|
||||
"system"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"ResourceWorkspace",
|
||||
"ResourceWorkspaceProxy",
|
||||
"ResourceWorkspaceExecution",
|
||||
"ResourceWorkspaceApplicationConnect",
|
||||
"ResourceAuditLog",
|
||||
"ResourceTemplate",
|
||||
"ResourceGroup",
|
||||
"ResourceFile",
|
||||
"ResourceProvisionerDaemon",
|
||||
"ResourceOrganization",
|
||||
"ResourceRoleAssignment",
|
||||
"ResourceOrgRoleAssignment",
|
||||
"ResourceAPIKey",
|
||||
"ResourceUser",
|
||||
"ResourceUserData",
|
||||
"ResourceOrganizationMember",
|
||||
"ResourceLicense",
|
||||
"ResourceDeploymentValues",
|
||||
"ResourceDeploymentStats",
|
||||
"ResourceReplicas",
|
||||
"ResourceDebugInfo",
|
||||
"ResourceSystem"
|
||||
]
|
||||
},
|
||||
"codersdk.RateLimitConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
@ -168,7 +168,7 @@ func (api *API) checkAuthorization(rw http.ResponseWriter, r *http.Request) {
|
|||
obj := rbac.Object{
|
||||
Owner: v.Object.OwnerID,
|
||||
OrgID: v.Object.OrganizationID,
|
||||
Type: v.Object.ResourceType,
|
||||
Type: v.Object.ResourceType.String(),
|
||||
}
|
||||
if obj.Owner == "me" {
|
||||
obj.Owner = auth.Actor.ID
|
||||
|
@ -188,7 +188,7 @@ func (api *API) checkAuthorization(rw http.ResponseWriter, r *http.Request) {
|
|||
var dbObj rbac.Objecter
|
||||
var dbErr error
|
||||
// Only support referencing some resources by ID.
|
||||
switch v.Object.ResourceType {
|
||||
switch v.Object.ResourceType.String() {
|
||||
case rbac.ResourceWorkspaceExecution.Type:
|
||||
wrkSpace, err := api.Database.GetWorkspaceByID(ctx, id)
|
||||
if err == nil {
|
||||
|
|
|
@ -46,34 +46,34 @@ func TestCheckPermissions(t *testing.T) {
|
|||
params := map[string]codersdk.AuthorizationCheck{
|
||||
readAllUsers: {
|
||||
Object: codersdk.AuthorizationObject{
|
||||
ResourceType: "users",
|
||||
ResourceType: codersdk.ResourceUser,
|
||||
},
|
||||
Action: "read",
|
||||
},
|
||||
readMyself: {
|
||||
Object: codersdk.AuthorizationObject{
|
||||
ResourceType: "users",
|
||||
ResourceType: codersdk.ResourceUser,
|
||||
OwnerID: "me",
|
||||
},
|
||||
Action: "read",
|
||||
},
|
||||
readOwnWorkspaces: {
|
||||
Object: codersdk.AuthorizationObject{
|
||||
ResourceType: "workspaces",
|
||||
ResourceType: codersdk.ResourceWorkspace,
|
||||
OwnerID: "me",
|
||||
},
|
||||
Action: "read",
|
||||
},
|
||||
readOrgWorkspaces: {
|
||||
Object: codersdk.AuthorizationObject{
|
||||
ResourceType: "workspaces",
|
||||
ResourceType: codersdk.ResourceWorkspace,
|
||||
OrganizationID: adminUser.OrganizationID.String(),
|
||||
},
|
||||
Action: "read",
|
||||
},
|
||||
updateSpecificTemplate: {
|
||||
Object: codersdk.AuthorizationObject{
|
||||
ResourceType: rbac.ResourceTemplate.Type,
|
||||
ResourceType: codersdk.ResourceTemplate,
|
||||
ResourceID: template.ID.String(),
|
||||
},
|
||||
Action: "update",
|
||||
|
@ -103,7 +103,7 @@ func TestCheckPermissions(t *testing.T) {
|
|||
Client: orgAdminClient,
|
||||
UserID: orgAdminUser.ID,
|
||||
Check: map[string]bool{
|
||||
readAllUsers: false,
|
||||
readAllUsers: true,
|
||||
readMyself: true,
|
||||
readOwnWorkspaces: true,
|
||||
readOrgWorkspaces: true,
|
||||
|
@ -115,7 +115,7 @@ func TestCheckPermissions(t *testing.T) {
|
|||
Client: memberClient,
|
||||
UserID: memberUser.ID,
|
||||
Check: map[string]bool{
|
||||
readAllUsers: false,
|
||||
readAllUsers: true,
|
||||
readMyself: true,
|
||||
readOwnWorkspaces: true,
|
||||
readOrgWorkspaces: false,
|
||||
|
|
|
@ -171,6 +171,12 @@ func New(options *Options) *API {
|
|||
options = &Options{}
|
||||
}
|
||||
|
||||
if options.DeploymentValues.DisableOwnerWorkspaceExec {
|
||||
rbac.ReloadBuiltinRoles(&rbac.RoleOptions{
|
||||
NoOwnerWorkspaceExec: true,
|
||||
})
|
||||
}
|
||||
|
||||
if options.Authorizer == nil {
|
||||
options.Authorizer = rbac.NewCachingAuthorizer(options.PrometheusRegistry)
|
||||
}
|
||||
|
|
|
@ -203,6 +203,8 @@ func NewOptions(t *testing.T, options *Options) (func(http.Handler), context.Can
|
|||
if options.DeploymentValues == nil {
|
||||
options.DeploymentValues = DeploymentValues(t)
|
||||
}
|
||||
// This value is not safe to run in parallel. Force it to be false.
|
||||
options.DeploymentValues.DisableOwnerWorkspaceExec = false
|
||||
|
||||
// If no ratelimits are set, disable all rate limiting for tests.
|
||||
if options.APIRateLimit == 0 {
|
||||
|
|
|
@ -14,6 +14,12 @@ type Objecter interface {
|
|||
// Resources are just typed objects. Making resources this way allows directly
|
||||
// passing them into an Authorize function and use the chaining api.
|
||||
var (
|
||||
// ResourceWildcard represents all resource types
|
||||
// Try to avoid using this where possible.
|
||||
ResourceWildcard = Object{
|
||||
Type: WildcardSymbol,
|
||||
}
|
||||
|
||||
// ResourceWorkspace CRUD. Org + User owner
|
||||
// create/delete = make or delete workspaces
|
||||
// read = access workspace
|
||||
|
@ -136,11 +142,6 @@ var (
|
|||
Type: "organization_member",
|
||||
}
|
||||
|
||||
// ResourceWildcard represents all resource types
|
||||
ResourceWildcard = Object{
|
||||
Type: WildcardSymbol,
|
||||
}
|
||||
|
||||
// ResourceLicense is the license in the 'licenses' table.
|
||||
// ResourceLicense is site wide.
|
||||
// create/delete = add or remove license from site.
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
// Code generated by rbacgen/main.go. DO NOT EDIT.
|
||||
package rbac
|
||||
|
||||
func AllResources() []Object {
|
||||
return []Object{
|
||||
ResourceAPIKey,
|
||||
ResourceAuditLog,
|
||||
ResourceDebugInfo,
|
||||
ResourceDeploymentStats,
|
||||
ResourceDeploymentValues,
|
||||
ResourceFile,
|
||||
ResourceGroup,
|
||||
ResourceLicense,
|
||||
ResourceOrgRoleAssignment,
|
||||
ResourceOrganization,
|
||||
ResourceOrganizationMember,
|
||||
ResourceProvisionerDaemon,
|
||||
ResourceReplicas,
|
||||
ResourceRoleAssignment,
|
||||
ResourceSystem,
|
||||
ResourceTemplate,
|
||||
ResourceUser,
|
||||
ResourceUserData,
|
||||
ResourceWildcard,
|
||||
ResourceWorkspace,
|
||||
ResourceWorkspaceApplicationConnect,
|
||||
ResourceWorkspaceExecution,
|
||||
ResourceWorkspaceProxy,
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/coder/coder/coderd/rbac"
|
||||
"github.com/coder/coder/coderd/util/slice"
|
||||
)
|
||||
|
||||
func TestObjectEqual(t *testing.T) {
|
||||
|
@ -174,3 +175,22 @@ func TestObjectEqual(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestAllResources ensures that all resources have a unique type name.
|
||||
func TestAllResources(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var typeNames []string
|
||||
resources := rbac.AllResources()
|
||||
for _, r := range resources {
|
||||
if r.Type == "" {
|
||||
t.Errorf("empty type name: %s", r.Type)
|
||||
continue
|
||||
}
|
||||
if slice.Contains(typeNames, r.Type) {
|
||||
t.Errorf("duplicate type name: %s", r.Type)
|
||||
continue
|
||||
}
|
||||
typeNames = append(typeNames, r.Type)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,11 @@ const (
|
|||
orgMember string = "organization-member"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Always load defaults
|
||||
ReloadBuiltinRoles(nil)
|
||||
}
|
||||
|
||||
// RoleNames is a list of user assignable role names. The role names must be
|
||||
// in the builtInRoles map. Any non-user assignable roles will generate an
|
||||
// error on Expand.
|
||||
|
@ -62,6 +67,33 @@ func RoleOrgMember(organizationID uuid.UUID) string {
|
|||
return roleName(orgMember, organizationID.String())
|
||||
}
|
||||
|
||||
func allPermsExcept(excepts ...Object) []Permission {
|
||||
resources := AllResources()
|
||||
var perms []Permission
|
||||
skip := make(map[string]bool)
|
||||
for _, e := range excepts {
|
||||
skip[e.Type] = true
|
||||
}
|
||||
|
||||
for _, r := range resources {
|
||||
// Exceptions
|
||||
if skip[r.Type] {
|
||||
continue
|
||||
}
|
||||
// This should always be skipped.
|
||||
if r.Type == ResourceWildcard.Type {
|
||||
continue
|
||||
}
|
||||
// Owners can do everything else
|
||||
perms = append(perms, Permission{
|
||||
Negate: false,
|
||||
ResourceType: r.Type,
|
||||
Action: WildcardSymbol,
|
||||
})
|
||||
}
|
||||
return perms
|
||||
}
|
||||
|
||||
// builtInRoles are just a hard coded set for now. Ideally we store these in
|
||||
// the database. Right now they are functions because the org id should scope
|
||||
// certain roles. When we store them in the database, each organization should
|
||||
|
@ -70,145 +102,163 @@ func RoleOrgMember(organizationID uuid.UUID) string {
|
|||
//
|
||||
// This map will be replaced by database storage defined by this ticket.
|
||||
// https://github.com/coder/coder/issues/1194
|
||||
var builtInRoles = map[string]func(orgID string) Role{
|
||||
// admin grants all actions to all resources.
|
||||
owner: func(_ string) Role {
|
||||
return Role{
|
||||
Name: owner,
|
||||
DisplayName: "Owner",
|
||||
Site: Permissions(map[string][]Action{
|
||||
ResourceWildcard.Type: {WildcardSymbol},
|
||||
}),
|
||||
Org: map[string][]Permission{},
|
||||
User: []Permission{},
|
||||
}
|
||||
},
|
||||
var builtInRoles map[string]func(orgID string) Role
|
||||
|
||||
// member grants all actions to all resources owned by the user
|
||||
member: func(_ string) Role {
|
||||
return Role{
|
||||
Name: member,
|
||||
DisplayName: "",
|
||||
Site: Permissions(map[string][]Action{
|
||||
// All users can read all other users and know they exist.
|
||||
ResourceUser.Type: {ActionRead},
|
||||
ResourceRoleAssignment.Type: {ActionRead},
|
||||
// All users can see the provisioner daemons.
|
||||
ResourceProvisionerDaemon.Type: {ActionRead},
|
||||
}),
|
||||
Org: map[string][]Permission{},
|
||||
User: Permissions(map[string][]Action{
|
||||
ResourceWildcard.Type: {WildcardSymbol},
|
||||
}),
|
||||
}
|
||||
},
|
||||
type RoleOptions struct {
|
||||
NoOwnerWorkspaceExec bool
|
||||
}
|
||||
|
||||
// auditor provides all permissions required to effectively read and understand
|
||||
// audit log events.
|
||||
// TODO: Finish the auditor as we add resources.
|
||||
auditor: func(_ string) Role {
|
||||
return Role{
|
||||
Name: auditor,
|
||||
DisplayName: "Auditor",
|
||||
Site: Permissions(map[string][]Action{
|
||||
// Should be able to read all template details, even in orgs they
|
||||
// are not in.
|
||||
ResourceTemplate.Type: {ActionRead},
|
||||
ResourceAuditLog.Type: {ActionRead},
|
||||
}),
|
||||
Org: map[string][]Permission{},
|
||||
User: []Permission{},
|
||||
}
|
||||
},
|
||||
// ReloadBuiltinRoles loads the static roles into the builtInRoles map.
|
||||
// This can be called again with a different config to change the behavior.
|
||||
//
|
||||
// TODO: @emyrk This would be great if it was instanced to a coderd rather
|
||||
// than a global. But that is a much larger refactor right now.
|
||||
// Essentially we did not foresee different deployments needing slightly
|
||||
// different role permissions.
|
||||
func ReloadBuiltinRoles(opts *RoleOptions) {
|
||||
if opts == nil {
|
||||
opts = &RoleOptions{}
|
||||
}
|
||||
|
||||
templateAdmin: func(_ string) Role {
|
||||
return Role{
|
||||
Name: templateAdmin,
|
||||
DisplayName: "Template Admin",
|
||||
Site: Permissions(map[string][]Action{
|
||||
ResourceTemplate.Type: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
|
||||
// CRUD all files, even those they did not upload.
|
||||
ResourceFile.Type: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
|
||||
ResourceWorkspace.Type: {ActionRead},
|
||||
// CRUD to provisioner daemons for now.
|
||||
ResourceProvisionerDaemon.Type: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
|
||||
// Needs to read all organizations since
|
||||
ResourceOrganization.Type: {ActionRead},
|
||||
}),
|
||||
Org: map[string][]Permission{},
|
||||
User: []Permission{},
|
||||
}
|
||||
},
|
||||
var ownerAndAdminExceptions []Object
|
||||
if opts.NoOwnerWorkspaceExec {
|
||||
ownerAndAdminExceptions = append(ownerAndAdminExceptions,
|
||||
ResourceWorkspaceExecution,
|
||||
ResourceWorkspaceApplicationConnect,
|
||||
)
|
||||
}
|
||||
|
||||
userAdmin: func(_ string) Role {
|
||||
return Role{
|
||||
Name: userAdmin,
|
||||
DisplayName: "User Admin",
|
||||
Site: Permissions(map[string][]Action{
|
||||
ResourceRoleAssignment.Type: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
|
||||
ResourceUser.Type: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
|
||||
// Full perms to manage org members
|
||||
ResourceOrganizationMember.Type: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
|
||||
ResourceGroup.Type: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
|
||||
}),
|
||||
Org: map[string][]Permission{},
|
||||
User: []Permission{},
|
||||
}
|
||||
},
|
||||
builtInRoles = map[string]func(orgID string) Role{
|
||||
// admin grants all actions to all resources.
|
||||
owner: func(_ string) Role {
|
||||
return Role{
|
||||
Name: owner,
|
||||
DisplayName: "Owner",
|
||||
Site: allPermsExcept(ownerAndAdminExceptions...),
|
||||
Org: map[string][]Permission{},
|
||||
User: []Permission{},
|
||||
}
|
||||
},
|
||||
|
||||
// orgAdmin returns a role with all actions allows in a given
|
||||
// organization scope.
|
||||
orgAdmin: func(organizationID string) Role {
|
||||
return Role{
|
||||
Name: roleName(orgAdmin, organizationID),
|
||||
DisplayName: "Organization Admin",
|
||||
Site: []Permission{},
|
||||
Org: map[string][]Permission{
|
||||
organizationID: {
|
||||
{
|
||||
Negate: false,
|
||||
ResourceType: "*",
|
||||
Action: "*",
|
||||
// member grants all actions to all resources owned by the user
|
||||
member: func(_ string) Role {
|
||||
return Role{
|
||||
Name: member,
|
||||
DisplayName: "",
|
||||
Site: Permissions(map[string][]Action{
|
||||
// All users can read all other users and know they exist.
|
||||
ResourceUser.Type: {ActionRead},
|
||||
ResourceRoleAssignment.Type: {ActionRead},
|
||||
// All users can see the provisioner daemons.
|
||||
ResourceProvisionerDaemon.Type: {ActionRead},
|
||||
}),
|
||||
Org: map[string][]Permission{},
|
||||
User: allPermsExcept(),
|
||||
}
|
||||
},
|
||||
|
||||
// auditor provides all permissions required to effectively read and understand
|
||||
// audit log events.
|
||||
// TODO: Finish the auditor as we add resources.
|
||||
auditor: func(_ string) Role {
|
||||
return Role{
|
||||
Name: auditor,
|
||||
DisplayName: "Auditor",
|
||||
Site: Permissions(map[string][]Action{
|
||||
// Should be able to read all template details, even in orgs they
|
||||
// are not in.
|
||||
ResourceTemplate.Type: {ActionRead},
|
||||
ResourceAuditLog.Type: {ActionRead},
|
||||
}),
|
||||
Org: map[string][]Permission{},
|
||||
User: []Permission{},
|
||||
}
|
||||
},
|
||||
|
||||
templateAdmin: func(_ string) Role {
|
||||
return Role{
|
||||
Name: templateAdmin,
|
||||
DisplayName: "Template Admin",
|
||||
Site: Permissions(map[string][]Action{
|
||||
ResourceTemplate.Type: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
|
||||
// CRUD all files, even those they did not upload.
|
||||
ResourceFile.Type: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
|
||||
ResourceWorkspace.Type: {ActionRead},
|
||||
// CRUD to provisioner daemons for now.
|
||||
ResourceProvisionerDaemon.Type: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
|
||||
// Needs to read all organizations since
|
||||
ResourceOrganization.Type: {ActionRead},
|
||||
}),
|
||||
Org: map[string][]Permission{},
|
||||
User: []Permission{},
|
||||
}
|
||||
},
|
||||
|
||||
userAdmin: func(_ string) Role {
|
||||
return Role{
|
||||
Name: userAdmin,
|
||||
DisplayName: "User Admin",
|
||||
Site: Permissions(map[string][]Action{
|
||||
ResourceRoleAssignment.Type: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
|
||||
ResourceUser.Type: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
|
||||
// Full perms to manage org members
|
||||
ResourceOrganizationMember.Type: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
|
||||
ResourceGroup.Type: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
|
||||
}),
|
||||
Org: map[string][]Permission{},
|
||||
User: []Permission{},
|
||||
}
|
||||
},
|
||||
|
||||
// orgAdmin returns a role with all actions allows in a given
|
||||
// organization scope.
|
||||
orgAdmin: func(organizationID string) Role {
|
||||
return Role{
|
||||
Name: roleName(orgAdmin, organizationID),
|
||||
DisplayName: "Organization Admin",
|
||||
Site: []Permission{},
|
||||
Org: map[string][]Permission{
|
||||
// Org admins should not have workspace exec perms.
|
||||
organizationID: allPermsExcept(ResourceWorkspaceExecution),
|
||||
},
|
||||
User: []Permission{},
|
||||
}
|
||||
},
|
||||
|
||||
// orgMember has an empty set of permissions, this just implies their membership
|
||||
// in an organization.
|
||||
orgMember: func(organizationID string) Role {
|
||||
return Role{
|
||||
Name: roleName(orgMember, organizationID),
|
||||
DisplayName: "",
|
||||
Site: []Permission{},
|
||||
Org: map[string][]Permission{
|
||||
organizationID: {
|
||||
{
|
||||
// All org members can read the other members in their org.
|
||||
ResourceType: ResourceOrganizationMember.Type,
|
||||
Action: ActionRead,
|
||||
},
|
||||
{
|
||||
// All org members can read the organization
|
||||
ResourceType: ResourceOrganization.Type,
|
||||
Action: ActionRead,
|
||||
},
|
||||
{
|
||||
// Can read available roles.
|
||||
ResourceType: ResourceOrgRoleAssignment.Type,
|
||||
Action: ActionRead,
|
||||
},
|
||||
{
|
||||
ResourceType: ResourceGroup.Type,
|
||||
Action: ActionRead,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
User: []Permission{},
|
||||
}
|
||||
},
|
||||
|
||||
// orgMember has an empty set of permissions, this just implies their membership
|
||||
// in an organization.
|
||||
orgMember: func(organizationID string) Role {
|
||||
return Role{
|
||||
Name: roleName(orgMember, organizationID),
|
||||
DisplayName: "",
|
||||
Site: []Permission{},
|
||||
Org: map[string][]Permission{
|
||||
organizationID: {
|
||||
{
|
||||
// All org members can read the other members in their org.
|
||||
ResourceType: ResourceOrganizationMember.Type,
|
||||
Action: ActionRead,
|
||||
},
|
||||
{
|
||||
// All org members can read the organization
|
||||
ResourceType: ResourceOrganization.Type,
|
||||
Action: ActionRead,
|
||||
},
|
||||
{
|
||||
// Can read available roles.
|
||||
ResourceType: ResourceOrgRoleAssignment.Type,
|
||||
Action: ActionRead,
|
||||
},
|
||||
{
|
||||
ResourceType: ResourceGroup.Type,
|
||||
Action: ActionRead,
|
||||
},
|
||||
},
|
||||
},
|
||||
User: []Permission{},
|
||||
}
|
||||
},
|
||||
User: []Permission{},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// assignRoles is a map of roles that can be assigned if a user has a given
|
||||
|
|
|
@ -19,6 +19,42 @@ type authSubject struct {
|
|||
Actor rbac.Subject
|
||||
}
|
||||
|
||||
//nolint:tparallel,paralleltest
|
||||
func TestOwnerExec(t *testing.T) {
|
||||
owner := rbac.Subject{
|
||||
ID: uuid.NewString(),
|
||||
Roles: rbac.RoleNames{rbac.RoleMember(), rbac.RoleOwner()},
|
||||
Scope: rbac.ScopeAll,
|
||||
}
|
||||
|
||||
t.Run("NoExec", func(t *testing.T) {
|
||||
rbac.ReloadBuiltinRoles(&rbac.RoleOptions{
|
||||
NoOwnerWorkspaceExec: true,
|
||||
})
|
||||
t.Cleanup(func() { rbac.ReloadBuiltinRoles(nil) })
|
||||
|
||||
auth := rbac.NewCachingAuthorizer(prometheus.NewRegistry())
|
||||
// Exec a random workspace
|
||||
err := auth.Authorize(context.Background(), owner, rbac.ActionCreate,
|
||||
rbac.ResourceWorkspaceExecution.WithID(uuid.New()).InOrg(uuid.New()).WithOwner(uuid.NewString()))
|
||||
require.ErrorAsf(t, err, &rbac.UnauthorizedError{}, "expected unauthorized error")
|
||||
})
|
||||
|
||||
t.Run("Exec", func(t *testing.T) {
|
||||
rbac.ReloadBuiltinRoles(&rbac.RoleOptions{
|
||||
NoOwnerWorkspaceExec: false,
|
||||
})
|
||||
t.Cleanup(func() { rbac.ReloadBuiltinRoles(nil) })
|
||||
|
||||
auth := rbac.NewCachingAuthorizer(prometheus.NewRegistry())
|
||||
|
||||
// Exec a random workspace
|
||||
err := auth.Authorize(context.Background(), owner, rbac.ActionCreate,
|
||||
rbac.ResourceWorkspaceExecution.WithID(uuid.New()).InOrg(uuid.New()).WithOwner(uuid.NewString()))
|
||||
require.NoError(t, err, "expected owner can")
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: add the SYSTEM to the MATRIX
|
||||
func TestRolePermissions(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
@ -111,8 +147,8 @@ func TestRolePermissions(t *testing.T) {
|
|||
Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionRead, rbac.ActionUpdate, rbac.ActionDelete},
|
||||
Resource: rbac.ResourceWorkspaceExecution.WithID(workspaceID).InOrg(orgID).WithOwner(currentUser.String()),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {owner, orgAdmin, orgMemberMe},
|
||||
false: {memberMe, otherOrgAdmin, otherOrgMember, templateAdmin, userAdmin},
|
||||
true: {owner, orgMemberMe},
|
||||
false: {orgAdmin, memberMe, otherOrgAdmin, otherOrgMember, templateAdmin, userAdmin},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -43,7 +43,7 @@ type AuthorizationCheck struct {
|
|||
type AuthorizationObject struct {
|
||||
// ResourceType is the name of the resource.
|
||||
// `./coderd/rbac/object.go` has the list of valid resource types.
|
||||
ResourceType string `json:"resource_type"`
|
||||
ResourceType RBACResource `json:"resource_type"`
|
||||
// OwnerID (optional) adds the set constraint to all resources owned by a given user.
|
||||
OwnerID string `json:"owner_id,omitempty"`
|
||||
// OrganizationID (optional) adds the set constraint to all resources owned by a given organization.
|
||||
|
|
|
@ -162,6 +162,7 @@ type DeploymentValues struct {
|
|||
GitAuthProviders clibase.Struct[[]GitAuthConfig] `json:"git_auth,omitempty" typescript:",notnull"`
|
||||
SSHConfig SSHConfig `json:"config_ssh,omitempty" typescript:",notnull"`
|
||||
WgtunnelHost clibase.String `json:"wgtunnel_host,omitempty" typescript:",notnull"`
|
||||
DisableOwnerWorkspaceExec clibase.Bool `json:"disable_owner_workspace_exec,omitempty" typescript:",notnull"`
|
||||
|
||||
Config clibase.YAMLConfigPath `json:"config,omitempty" typescript:",notnull"`
|
||||
WriteConfig clibase.Bool `json:"write_config,omitempty" typescript:",notnull"`
|
||||
|
@ -1320,6 +1321,15 @@ when required by your organization's security policy.`,
|
|||
Value: &c.DisablePathApps,
|
||||
YAML: "disablePathApps",
|
||||
},
|
||||
{
|
||||
Name: "Disable Owner Workspace Access",
|
||||
Description: "Remove the permission for the 'owner' role to have workspace execution on all workspaces. This prevents the 'owner' from ssh, apps, and terminal access based on the 'owner' role. They still have their user permissions to access their own workspaces.",
|
||||
Flag: "disable-owner-workspace-access",
|
||||
Env: "CODER_DISABLE_OWNER_WORKSPACE_ACCESS",
|
||||
|
||||
Value: &c.DisableOwnerWorkspaceExec,
|
||||
YAML: "disableOwnerWorkspaceAccess",
|
||||
},
|
||||
{
|
||||
Name: "Session Duration",
|
||||
Description: "The token expiry duration for browser sessions. Sessions may last longer if they are actively making requests, but this functionality can be disabled via --disable-session-expiry-refresh.",
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package codersdk
|
||||
|
||||
type RBACResource string
|
||||
|
||||
const (
|
||||
ResourceWorkspace RBACResource = "workspace"
|
||||
ResourceWorkspaceProxy RBACResource = "workspace_proxy"
|
||||
ResourceWorkspaceExecution RBACResource = "workspace_execution"
|
||||
ResourceWorkspaceApplicationConnect RBACResource = "application_connect"
|
||||
ResourceAuditLog RBACResource = "audit_log"
|
||||
ResourceTemplate RBACResource = "template"
|
||||
ResourceGroup RBACResource = "group"
|
||||
ResourceFile RBACResource = "file"
|
||||
ResourceProvisionerDaemon RBACResource = "provisioner_daemon"
|
||||
ResourceOrganization RBACResource = "organization"
|
||||
ResourceRoleAssignment RBACResource = "assign_role"
|
||||
ResourceOrgRoleAssignment RBACResource = "assign_org_role"
|
||||
ResourceAPIKey RBACResource = "api_key"
|
||||
ResourceUser RBACResource = "user"
|
||||
ResourceUserData RBACResource = "user_data"
|
||||
ResourceOrganizationMember RBACResource = "organization_member"
|
||||
ResourceLicense RBACResource = "license"
|
||||
ResourceDeploymentValues RBACResource = "deployment_config"
|
||||
ResourceDeploymentStats RBACResource = "deployment_stats"
|
||||
ResourceReplicas RBACResource = "replicas"
|
||||
ResourceDebugInfo RBACResource = "debug_info"
|
||||
ResourceSystem RBACResource = "system"
|
||||
)
|
||||
|
||||
func (r RBACResource) String() string {
|
||||
return string(r)
|
||||
}
|
|
@ -25,7 +25,7 @@ curl -X POST http://coder-server:8080/api/v2/authcheck \
|
|||
"organization_id": "string",
|
||||
"owner_id": "string",
|
||||
"resource_id": "string",
|
||||
"resource_type": "string"
|
||||
"resource_type": "workspace"
|
||||
}
|
||||
},
|
||||
"property2": {
|
||||
|
@ -34,7 +34,7 @@ curl -X POST http://coder-server:8080/api/v2/authcheck \
|
|||
"organization_id": "string",
|
||||
"owner_id": "string",
|
||||
"resource_id": "string",
|
||||
"resource_type": "string"
|
||||
"resource_type": "workspace"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -188,6 +188,7 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \
|
|||
"stun_addresses": ["string"]
|
||||
}
|
||||
},
|
||||
"disable_owner_workspace_exec": true,
|
||||
"disable_password_auth": true,
|
||||
"disable_path_apps": true,
|
||||
"disable_session_expiry_refresh": true,
|
||||
|
|
|
@ -1042,7 +1042,7 @@
|
|||
"organization_id": "string",
|
||||
"owner_id": "string",
|
||||
"resource_id": "string",
|
||||
"resource_type": "string"
|
||||
"resource_type": "workspace"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -1072,7 +1072,7 @@ AuthorizationCheck is used to check if the currently authenticated user (or the
|
|||
"organization_id": "string",
|
||||
"owner_id": "string",
|
||||
"resource_id": "string",
|
||||
"resource_type": "string"
|
||||
"resource_type": "workspace"
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -1080,12 +1080,12 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ----------------- | ------ | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `organization_id` | string | false | | Organization ID (optional) adds the set constraint to all resources owned by a given organization. |
|
||||
| `owner_id` | string | false | | Owner ID (optional) adds the set constraint to all resources owned by a given user. |
|
||||
| `resource_id` | string | false | | Resource ID (optional) reduces the set to a singular resource. This assigns a resource ID to the resource type, eg: a single workspace. The rbac library will not fetch the resource from the database, so if you are using this option, you should also set the owner ID and organization ID if possible. Be as specific as possible using all the fields relevant. |
|
||||
| `resource_type` | string | false | | Resource type is the name of the resource. `./coderd/rbac/object.go` has the list of valid resource types. |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ----------------- | ---------------------------------------------- | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `organization_id` | string | false | | Organization ID (optional) adds the set constraint to all resources owned by a given organization. |
|
||||
| `owner_id` | string | false | | Owner ID (optional) adds the set constraint to all resources owned by a given user. |
|
||||
| `resource_id` | string | false | | Resource ID (optional) reduces the set to a singular resource. This assigns a resource ID to the resource type, eg: a single workspace. The rbac library will not fetch the resource from the database, so if you are using this option, you should also set the owner ID and organization ID if possible. Be as specific as possible using all the fields relevant. |
|
||||
| `resource_type` | [codersdk.RBACResource](#codersdkrbacresource) | false | | Resource type is the name of the resource. `./coderd/rbac/object.go` has the list of valid resource types. |
|
||||
|
||||
## codersdk.AuthorizationRequest
|
||||
|
||||
|
@ -1098,7 +1098,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
"organization_id": "string",
|
||||
"owner_id": "string",
|
||||
"resource_id": "string",
|
||||
"resource_type": "string"
|
||||
"resource_type": "workspace"
|
||||
}
|
||||
},
|
||||
"property2": {
|
||||
|
@ -1107,7 +1107,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
"organization_id": "string",
|
||||
"owner_id": "string",
|
||||
"resource_id": "string",
|
||||
"resource_type": "string"
|
||||
"resource_type": "workspace"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1817,6 +1817,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a
|
|||
"stun_addresses": ["string"]
|
||||
}
|
||||
},
|
||||
"disable_owner_workspace_exec": true,
|
||||
"disable_password_auth": true,
|
||||
"disable_path_apps": true,
|
||||
"disable_session_expiry_refresh": true,
|
||||
|
@ -2159,6 +2160,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a
|
|||
"stun_addresses": ["string"]
|
||||
}
|
||||
},
|
||||
"disable_owner_workspace_exec": true,
|
||||
"disable_password_auth": true,
|
||||
"disable_path_apps": true,
|
||||
"disable_session_expiry_refresh": true,
|
||||
|
@ -2347,6 +2349,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a
|
|||
| `config_ssh` | [codersdk.SSHConfig](#codersdksshconfig) | false | | |
|
||||
| `dangerous` | [codersdk.DangerousConfig](#codersdkdangerousconfig) | false | | |
|
||||
| `derp` | [codersdk.DERP](#codersdkderp) | false | | |
|
||||
| `disable_owner_workspace_exec` | boolean | false | | |
|
||||
| `disable_password_auth` | boolean | false | | |
|
||||
| `disable_path_apps` | boolean | false | | |
|
||||
| `disable_session_expiry_refresh` | boolean | false | | |
|
||||
|
@ -3359,6 +3362,41 @@ Parameter represents a set value for the scope.
|
|||
| ---------- | ------ | -------- | ------------ | ----------- |
|
||||
| `deadline` | string | true | | |
|
||||
|
||||
## codersdk.RBACResource
|
||||
|
||||
```json
|
||||
"workspace"
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
| Value |
|
||||
| --------------------- |
|
||||
| `workspace` |
|
||||
| `workspace_proxy` |
|
||||
| `workspace_execution` |
|
||||
| `application_connect` |
|
||||
| `audit_log` |
|
||||
| `template` |
|
||||
| `group` |
|
||||
| `file` |
|
||||
| `provisioner_daemon` |
|
||||
| `organization` |
|
||||
| `assign_role` |
|
||||
| `assign_org_role` |
|
||||
| `api_key` |
|
||||
| `user` |
|
||||
| `user_data` |
|
||||
| `organization_member` |
|
||||
| `license` |
|
||||
| `deployment_config` |
|
||||
| `deployment_stats` |
|
||||
| `replicas` |
|
||||
| `debug_info` |
|
||||
| `system` |
|
||||
|
||||
## codersdk.RateLimitConfig
|
||||
|
||||
```json
|
||||
|
|
|
@ -173,6 +173,16 @@ An HTTP URL that is accessible by other replicas to relay DERP traffic. Required
|
|||
|
||||
Addresses for STUN servers to establish P2P connections. Set empty to disable P2P connections.
|
||||
|
||||
### --disable-owner-workspace-access
|
||||
|
||||
| | |
|
||||
| ----------- | -------------------------------------------------- |
|
||||
| Type | <code>bool</code> |
|
||||
| Environment | <code>$CODER_DISABLE_OWNER_WORKSPACE_ACCESS</code> |
|
||||
| YAML | <code>disableOwnerWorkspaceAccess</code> |
|
||||
|
||||
Remove the permission for the 'owner' role to have workspace execution on all workspaces. This prevents the 'owner' from ssh, apps, and terminal access based on the 'owner' role. They still have their user permissions to access their own workspaces.
|
||||
|
||||
### --disable-password-auth
|
||||
|
||||
| | |
|
||||
|
|
|
@ -58,7 +58,7 @@ func TestCheckACLPermissions(t *testing.T) {
|
|||
params := map[string]codersdk.AuthorizationCheck{
|
||||
updateSpecificTemplate: {
|
||||
Object: codersdk.AuthorizationObject{
|
||||
ResourceType: rbac.ResourceTemplate.Type,
|
||||
ResourceType: codersdk.ResourceTemplate,
|
||||
ResourceID: template.ID.String(),
|
||||
},
|
||||
Action: "write",
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"go/types"
|
||||
"html/template"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
//go:embed object.gotmpl
|
||||
var objectGoTpl string
|
||||
|
||||
type TplState struct {
|
||||
ResourceNames []string
|
||||
}
|
||||
|
||||
// main will generate a file that lists all rbac objects.
|
||||
// This is to provide an "AllResources" function that is always
|
||||
// in sync.
|
||||
func main() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
path := "."
|
||||
if len(os.Args) > 1 {
|
||||
path = os.Args[1]
|
||||
}
|
||||
|
||||
cfg := &packages.Config{
|
||||
Mode: packages.NeedTypes | packages.NeedName | packages.NeedTypesInfo | packages.NeedDeps,
|
||||
Tests: false,
|
||||
Context: ctx,
|
||||
}
|
||||
|
||||
pkgs, err := packages.Load(cfg, path)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load package: %s", err.Error())
|
||||
}
|
||||
|
||||
if len(pkgs) != 1 {
|
||||
log.Fatalf("Expected 1 package, got %d", len(pkgs))
|
||||
}
|
||||
|
||||
rbacPkg := pkgs[0]
|
||||
if rbacPkg.Name != "rbac" {
|
||||
log.Fatalf("Expected rbac package, got %q", rbacPkg.Name)
|
||||
}
|
||||
|
||||
tpl, err := template.New("object.gotmpl").Parse(objectGoTpl)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse templates: %s", err.Error())
|
||||
}
|
||||
|
||||
var out bytes.Buffer
|
||||
err = tpl.Execute(&out, TplState{
|
||||
ResourceNames: allResources(rbacPkg),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Execute template: %s", err.Error())
|
||||
}
|
||||
|
||||
formatted, err := format.Source(out.Bytes())
|
||||
if err != nil {
|
||||
log.Fatalf("Format template: %s", err.Error())
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprint(os.Stdout, string(formatted))
|
||||
}
|
||||
|
||||
func allResources(pkg *packages.Package) []string {
|
||||
var resources []string
|
||||
names := pkg.Types.Scope().Names()
|
||||
for _, name := range names {
|
||||
obj, ok := pkg.Types.Scope().Lookup(name).(*types.Var)
|
||||
if ok && obj.Type().String() == "github.com/coder/coder/coderd/rbac.Object" {
|
||||
resources = append(resources, obj.Name())
|
||||
}
|
||||
}
|
||||
sort.Strings(resources)
|
||||
return resources
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// Code generated by rbacgen/main.go. DO NOT EDIT.
|
||||
package rbac
|
||||
|
||||
func AllResources() []Object {
|
||||
return []Object{
|
||||
{{- range .ResourceNames }}
|
||||
{{ . }},
|
||||
{{- end }}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ export interface AuthorizationCheck {
|
|||
|
||||
// From codersdk/authorization.go
|
||||
export interface AuthorizationObject {
|
||||
readonly resource_type: string
|
||||
readonly resource_type: RBACResource
|
||||
readonly owner_id?: string
|
||||
readonly organization_id?: string
|
||||
readonly resource_id?: string
|
||||
|
@ -379,6 +379,7 @@ export interface DeploymentValues {
|
|||
readonly git_auth?: any
|
||||
readonly config_ssh?: SSHConfig
|
||||
readonly wgtunnel_host?: string
|
||||
readonly disable_owner_workspace_exec?: boolean
|
||||
// This is likely an enum in an external package ("github.com/coder/coder/cli/clibase.YAMLConfigPath")
|
||||
readonly config?: string
|
||||
readonly write_config?: boolean
|
||||
|
@ -1421,6 +1422,55 @@ export const ProvisionerStorageMethods: ProvisionerStorageMethod[] = ["file"]
|
|||
export type ProvisionerType = "echo" | "terraform"
|
||||
export const ProvisionerTypes: ProvisionerType[] = ["echo", "terraform"]
|
||||
|
||||
// From codersdk/rbacresources.go
|
||||
export type RBACResource =
|
||||
| "api_key"
|
||||
| "application_connect"
|
||||
| "assign_org_role"
|
||||
| "assign_role"
|
||||
| "audit_log"
|
||||
| "debug_info"
|
||||
| "deployment_config"
|
||||
| "deployment_stats"
|
||||
| "file"
|
||||
| "group"
|
||||
| "license"
|
||||
| "organization"
|
||||
| "organization_member"
|
||||
| "provisioner_daemon"
|
||||
| "replicas"
|
||||
| "system"
|
||||
| "template"
|
||||
| "user"
|
||||
| "user_data"
|
||||
| "workspace"
|
||||
| "workspace_execution"
|
||||
| "workspace_proxy"
|
||||
export const RBACResources: RBACResource[] = [
|
||||
"api_key",
|
||||
"application_connect",
|
||||
"assign_org_role",
|
||||
"assign_role",
|
||||
"audit_log",
|
||||
"debug_info",
|
||||
"deployment_config",
|
||||
"deployment_stats",
|
||||
"file",
|
||||
"group",
|
||||
"license",
|
||||
"organization",
|
||||
"organization_member",
|
||||
"provisioner_daemon",
|
||||
"replicas",
|
||||
"system",
|
||||
"template",
|
||||
"user",
|
||||
"user_data",
|
||||
"workspace",
|
||||
"workspace_execution",
|
||||
"workspace_proxy",
|
||||
]
|
||||
|
||||
// From codersdk/audit.go
|
||||
export type ResourceType =
|
||||
| "api_key"
|
||||
|
|
|
@ -21,6 +21,11 @@ export default {
|
|||
usage: "something",
|
||||
value: "1234",
|
||||
},
|
||||
{
|
||||
name: "Disable Owner Workspace Execution",
|
||||
usage: "something",
|
||||
value: false,
|
||||
},
|
||||
{
|
||||
name: "TLS Version",
|
||||
usage: "something",
|
||||
|
@ -52,6 +57,10 @@ NoTLS.args = {
|
|||
name: "SSH Keygen Algorithm",
|
||||
value: "1234",
|
||||
} as DeploymentOption,
|
||||
{
|
||||
name: "Disable Owner Workspace Execution",
|
||||
value: false,
|
||||
} as DeploymentOption,
|
||||
{
|
||||
name: "Secure Auth Cookie",
|
||||
value: "1234",
|
||||
|
|
|
@ -36,6 +36,7 @@ export const SecuritySettingsPageView = ({
|
|||
options,
|
||||
"SSH Keygen Algorithm",
|
||||
"Secure Auth Cookie",
|
||||
"Disable Owner Workspace Execution",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -59,7 +59,7 @@ export const permissionsToCheck = {
|
|||
},
|
||||
[checks.viewDeploymentValues]: {
|
||||
object: {
|
||||
resource_type: "deployment_flags",
|
||||
resource_type: "deployment_config",
|
||||
},
|
||||
action: "read",
|
||||
},
|
||||
|
@ -71,7 +71,7 @@ export const permissionsToCheck = {
|
|||
},
|
||||
[checks.viewUpdateCheck]: {
|
||||
object: {
|
||||
resource_type: "update_check",
|
||||
resource_type: "deployment_config",
|
||||
},
|
||||
action: "read",
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue