From adb7521066398ccf2f26be75ce9a3ac13b71604d Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Wed, 24 Sep 2025 18:40:36 +0200 Subject: [PATCH] feat: generate RBAC scope name constants (#19896) # Generate RBAC scope name constants This PR adds a new generated file `coderd/rbac/scopes_constants_gen.go` that contains typed constants for all RBAC scope names in the format `Scope`. For example, `ScopeWorkspaceRead` for the scope "workspace:read". These constants make it easier to reference specific scopes in code without using string literals, improving type safety and making refactoring easier. The PR: - Adds a new template file `scripts/typegen/scopenames.gotmpl` - Updates the typegen script to support generating scope name constants - Updates the Makefile to include the new generated file in build targets --- Makefile | 11 + coderd/rbac/scopes.go | 13 + coderd/rbac/scopes_constants_gen.go | 448 ++++++++++++++++++++++++++++ scripts/typegen/main.go | 37 +++ scripts/typegen/scopenames.gotmpl | 36 +++ 5 files changed, 545 insertions(+) create mode 100644 coderd/rbac/scopes_constants_gen.go create mode 100644 scripts/typegen/scopenames.gotmpl diff --git a/Makefile b/Makefile index 65e5b41ad5..66c65b5d76 100644 --- a/Makefile +++ b/Makefile @@ -646,6 +646,7 @@ GEN_FILES := \ $(SITE_GEN_FILES) \ coderd/rbac/object_gen.go \ codersdk/rbacresources_gen.go \ + coderd/rbac/scopes_constants_gen.go \ docs/admin/integrations/prometheus.md \ docs/reference/cli/index.md \ docs/admin/security/audit-logs.md \ @@ -695,6 +696,7 @@ gen/mark-fresh: site/src/api/typesGenerated.ts \ coderd/rbac/object_gen.go \ codersdk/rbacresources_gen.go \ + coderd/rbac/scopes_constants_gen.go \ site/src/api/rbacresourcesGenerated.ts \ site/src/api/countriesGenerated.ts \ docs/admin/integrations/prometheus.md \ @@ -842,6 +844,15 @@ coderd/rbac/object_gen.go: scripts/typegen/rbacobject.gotmpl scripts/typegen/mai rmdir -v "$$tempdir" touch "$@" +coderd/rbac/scopes_constants_gen.go: scripts/typegen/scopenames.gotmpl scripts/typegen/main.go coderd/rbac/policy/policy.go + # Generate typed low-level ScopeName constants from RBACPermissions + # Write to a temp file first to avoid truncating the package during build + # since the generator imports the rbac package. + tempfile=$(shell mktemp /tmp/scopes_constants_gen.XXXXXX) + go run ./scripts/typegen/main.go rbac scopenames > "$$tempfile" + mv -v "$$tempfile" coderd/rbac/scopes_constants_gen.go + touch "$@" + codersdk/rbacresources_gen.go: scripts/typegen/codersdk.gotmpl scripts/typegen/main.go coderd/rbac/object.go coderd/rbac/policy/policy.go # Do no overwrite codersdk/rbacresources_gen.go directly, as it would make the file empty, breaking # the `codersdk` package and any parallel build targets. diff --git a/coderd/rbac/scopes.go b/coderd/rbac/scopes.go index 86cba9e1c7..4b66d08170 100644 --- a/coderd/rbac/scopes.go +++ b/coderd/rbac/scopes.go @@ -2,6 +2,7 @@ package rbac import ( "fmt" + "slices" "strings" "github.com/google/uuid" @@ -107,6 +108,18 @@ var builtinScopes = map[ScopeName]Scope{ }, } +// BuiltinScopeNames returns the list of built-in high-level scope names +// defined in this package (e.g., "all", "application_connect"). The result +// is sorted for deterministic ordering in code generation and tests. +func BuiltinScopeNames() []ScopeName { + names := make([]ScopeName, 0, len(builtinScopes)) + for name := range builtinScopes { + names = append(names, name) + } + slices.Sort(names) + return names +} + type ExpandableScope interface { Expand() (Scope, error) // Name is for logging and tracing purposes, we want to know the human diff --git a/coderd/rbac/scopes_constants_gen.go b/coderd/rbac/scopes_constants_gen.go new file mode 100644 index 0000000000..586ae1c583 --- /dev/null +++ b/coderd/rbac/scopes_constants_gen.go @@ -0,0 +1,448 @@ +// Code generated by: go run ./scripts/typegen rbac scopenames; DO NOT EDIT. +package rbac + +// ScopeName constants generated from policy.RBACPermissions. +// These represent low-level ":" scope names. +// Built-in non-low-level scopes like "all" and "application_connect" remain +// declared in code, not here, to avoid duplication. + +const ( + ScopeAibridgeInterceptionCreate ScopeName = "aibridge_interception:create" + ScopeAibridgeInterceptionRead ScopeName = "aibridge_interception:read" + ScopeAibridgeInterceptionUpdate ScopeName = "aibridge_interception:update" + ScopeApiKeyCreate ScopeName = "api_key:create" + ScopeApiKeyDelete ScopeName = "api_key:delete" + ScopeApiKeyRead ScopeName = "api_key:read" + ScopeApiKeyUpdate ScopeName = "api_key:update" + ScopeAssignOrgRoleAssign ScopeName = "assign_org_role:assign" + ScopeAssignOrgRoleCreate ScopeName = "assign_org_role:create" + ScopeAssignOrgRoleDelete ScopeName = "assign_org_role:delete" + ScopeAssignOrgRoleRead ScopeName = "assign_org_role:read" + ScopeAssignOrgRoleUnassign ScopeName = "assign_org_role:unassign" + ScopeAssignOrgRoleUpdate ScopeName = "assign_org_role:update" + ScopeAssignRoleAssign ScopeName = "assign_role:assign" + ScopeAssignRoleRead ScopeName = "assign_role:read" + ScopeAssignRoleUnassign ScopeName = "assign_role:unassign" + ScopeAuditLogCreate ScopeName = "audit_log:create" + ScopeAuditLogRead ScopeName = "audit_log:read" + ScopeConnectionLogRead ScopeName = "connection_log:read" + ScopeConnectionLogUpdate ScopeName = "connection_log:update" + ScopeCryptoKeyCreate ScopeName = "crypto_key:create" + ScopeCryptoKeyDelete ScopeName = "crypto_key:delete" + ScopeCryptoKeyRead ScopeName = "crypto_key:read" + ScopeCryptoKeyUpdate ScopeName = "crypto_key:update" + ScopeDebugInfoRead ScopeName = "debug_info:read" + ScopeDeploymentConfigRead ScopeName = "deployment_config:read" + ScopeDeploymentConfigUpdate ScopeName = "deployment_config:update" + ScopeDeploymentStatsRead ScopeName = "deployment_stats:read" + ScopeFileCreate ScopeName = "file:create" + ScopeFileRead ScopeName = "file:read" + ScopeGroupCreate ScopeName = "group:create" + ScopeGroupDelete ScopeName = "group:delete" + ScopeGroupRead ScopeName = "group:read" + ScopeGroupUpdate ScopeName = "group:update" + ScopeGroupMemberRead ScopeName = "group_member:read" + ScopeIdpsyncSettingsRead ScopeName = "idpsync_settings:read" + ScopeIdpsyncSettingsUpdate ScopeName = "idpsync_settings:update" + ScopeInboxNotificationCreate ScopeName = "inbox_notification:create" + ScopeInboxNotificationRead ScopeName = "inbox_notification:read" + ScopeInboxNotificationUpdate ScopeName = "inbox_notification:update" + ScopeLicenseCreate ScopeName = "license:create" + ScopeLicenseDelete ScopeName = "license:delete" + ScopeLicenseRead ScopeName = "license:read" + ScopeNotificationMessageCreate ScopeName = "notification_message:create" + ScopeNotificationMessageDelete ScopeName = "notification_message:delete" + ScopeNotificationMessageRead ScopeName = "notification_message:read" + ScopeNotificationMessageUpdate ScopeName = "notification_message:update" + ScopeNotificationPreferenceRead ScopeName = "notification_preference:read" + ScopeNotificationPreferenceUpdate ScopeName = "notification_preference:update" + ScopeNotificationTemplateRead ScopeName = "notification_template:read" + ScopeNotificationTemplateUpdate ScopeName = "notification_template:update" + ScopeOauth2AppCreate ScopeName = "oauth2_app:create" + ScopeOauth2AppDelete ScopeName = "oauth2_app:delete" + ScopeOauth2AppRead ScopeName = "oauth2_app:read" + ScopeOauth2AppUpdate ScopeName = "oauth2_app:update" + ScopeOauth2AppCodeTokenCreate ScopeName = "oauth2_app_code_token:create" + ScopeOauth2AppCodeTokenDelete ScopeName = "oauth2_app_code_token:delete" + ScopeOauth2AppCodeTokenRead ScopeName = "oauth2_app_code_token:read" + ScopeOauth2AppSecretCreate ScopeName = "oauth2_app_secret:create" + ScopeOauth2AppSecretDelete ScopeName = "oauth2_app_secret:delete" + ScopeOauth2AppSecretRead ScopeName = "oauth2_app_secret:read" + ScopeOauth2AppSecretUpdate ScopeName = "oauth2_app_secret:update" + ScopeOrganizationCreate ScopeName = "organization:create" + ScopeOrganizationDelete ScopeName = "organization:delete" + ScopeOrganizationRead ScopeName = "organization:read" + ScopeOrganizationUpdate ScopeName = "organization:update" + ScopeOrganizationMemberCreate ScopeName = "organization_member:create" + ScopeOrganizationMemberDelete ScopeName = "organization_member:delete" + ScopeOrganizationMemberRead ScopeName = "organization_member:read" + ScopeOrganizationMemberUpdate ScopeName = "organization_member:update" + ScopePrebuiltWorkspaceDelete ScopeName = "prebuilt_workspace:delete" + ScopePrebuiltWorkspaceUpdate ScopeName = "prebuilt_workspace:update" + ScopeProvisionerDaemonCreate ScopeName = "provisioner_daemon:create" + ScopeProvisionerDaemonDelete ScopeName = "provisioner_daemon:delete" + ScopeProvisionerDaemonRead ScopeName = "provisioner_daemon:read" + ScopeProvisionerDaemonUpdate ScopeName = "provisioner_daemon:update" + ScopeProvisionerJobsCreate ScopeName = "provisioner_jobs:create" + ScopeProvisionerJobsRead ScopeName = "provisioner_jobs:read" + ScopeProvisionerJobsUpdate ScopeName = "provisioner_jobs:update" + ScopeReplicasRead ScopeName = "replicas:read" + ScopeSystemCreate ScopeName = "system:create" + ScopeSystemDelete ScopeName = "system:delete" + ScopeSystemRead ScopeName = "system:read" + ScopeSystemUpdate ScopeName = "system:update" + ScopeTailnetCoordinatorCreate ScopeName = "tailnet_coordinator:create" + ScopeTailnetCoordinatorDelete ScopeName = "tailnet_coordinator:delete" + ScopeTailnetCoordinatorRead ScopeName = "tailnet_coordinator:read" + ScopeTailnetCoordinatorUpdate ScopeName = "tailnet_coordinator:update" + ScopeTemplateCreate ScopeName = "template:create" + ScopeTemplateDelete ScopeName = "template:delete" + ScopeTemplateRead ScopeName = "template:read" + ScopeTemplateUpdate ScopeName = "template:update" + ScopeTemplateUse ScopeName = "template:use" + ScopeTemplateViewInsights ScopeName = "template:view_insights" + ScopeUsageEventCreate ScopeName = "usage_event:create" + ScopeUsageEventRead ScopeName = "usage_event:read" + ScopeUsageEventUpdate ScopeName = "usage_event:update" + ScopeUserCreate ScopeName = "user:create" + ScopeUserDelete ScopeName = "user:delete" + ScopeUserRead ScopeName = "user:read" + ScopeUserReadPersonal ScopeName = "user:read_personal" + ScopeUserUpdate ScopeName = "user:update" + ScopeUserUpdatePersonal ScopeName = "user:update_personal" + ScopeUserSecretCreate ScopeName = "user_secret:create" + ScopeUserSecretDelete ScopeName = "user_secret:delete" + ScopeUserSecretRead ScopeName = "user_secret:read" + ScopeUserSecretUpdate ScopeName = "user_secret:update" + ScopeWebpushSubscriptionCreate ScopeName = "webpush_subscription:create" + ScopeWebpushSubscriptionDelete ScopeName = "webpush_subscription:delete" + ScopeWebpushSubscriptionRead ScopeName = "webpush_subscription:read" + ScopeWorkspaceApplicationConnect ScopeName = "workspace:application_connect" + ScopeWorkspaceCreate ScopeName = "workspace:create" + ScopeWorkspaceCreateAgent ScopeName = "workspace:create_agent" + ScopeWorkspaceDelete ScopeName = "workspace:delete" + ScopeWorkspaceDeleteAgent ScopeName = "workspace:delete_agent" + ScopeWorkspaceRead ScopeName = "workspace:read" + ScopeWorkspaceSsh ScopeName = "workspace:ssh" + ScopeWorkspaceStart ScopeName = "workspace:start" + ScopeWorkspaceStop ScopeName = "workspace:stop" + ScopeWorkspaceUpdate ScopeName = "workspace:update" + ScopeWorkspaceAgentDevcontainersCreate ScopeName = "workspace_agent_devcontainers:create" + ScopeWorkspaceAgentResourceMonitorCreate ScopeName = "workspace_agent_resource_monitor:create" + ScopeWorkspaceAgentResourceMonitorRead ScopeName = "workspace_agent_resource_monitor:read" + ScopeWorkspaceAgentResourceMonitorUpdate ScopeName = "workspace_agent_resource_monitor:update" + ScopeWorkspaceDormantApplicationConnect ScopeName = "workspace_dormant:application_connect" + ScopeWorkspaceDormantCreate ScopeName = "workspace_dormant:create" + ScopeWorkspaceDormantCreateAgent ScopeName = "workspace_dormant:create_agent" + ScopeWorkspaceDormantDelete ScopeName = "workspace_dormant:delete" + ScopeWorkspaceDormantDeleteAgent ScopeName = "workspace_dormant:delete_agent" + ScopeWorkspaceDormantRead ScopeName = "workspace_dormant:read" + ScopeWorkspaceDormantSsh ScopeName = "workspace_dormant:ssh" + ScopeWorkspaceDormantStart ScopeName = "workspace_dormant:start" + ScopeWorkspaceDormantStop ScopeName = "workspace_dormant:stop" + ScopeWorkspaceDormantUpdate ScopeName = "workspace_dormant:update" + ScopeWorkspaceProxyCreate ScopeName = "workspace_proxy:create" + ScopeWorkspaceProxyDelete ScopeName = "workspace_proxy:delete" + ScopeWorkspaceProxyRead ScopeName = "workspace_proxy:read" + ScopeWorkspaceProxyUpdate ScopeName = "workspace_proxy:update" +) + +// Valid reports whether the ScopeName matches one of the known scope values. +// This includes both builtin scope names and generated low-level scopes. +// Builtins are sourced from rbac.BuiltinScopeNames() at generation time to +// ensure changes in rbac/scopes.go remain in sync here. +func (e ScopeName) Valid() bool { + switch e { + case ScopeName("all"), + ScopeName("application_connect"), + ScopeName("no_user_data"), + ScopeAibridgeInterceptionCreate, + ScopeAibridgeInterceptionRead, + ScopeAibridgeInterceptionUpdate, + ScopeApiKeyCreate, + ScopeApiKeyDelete, + ScopeApiKeyRead, + ScopeApiKeyUpdate, + ScopeAssignOrgRoleAssign, + ScopeAssignOrgRoleCreate, + ScopeAssignOrgRoleDelete, + ScopeAssignOrgRoleRead, + ScopeAssignOrgRoleUnassign, + ScopeAssignOrgRoleUpdate, + ScopeAssignRoleAssign, + ScopeAssignRoleRead, + ScopeAssignRoleUnassign, + ScopeAuditLogCreate, + ScopeAuditLogRead, + ScopeConnectionLogRead, + ScopeConnectionLogUpdate, + ScopeCryptoKeyCreate, + ScopeCryptoKeyDelete, + ScopeCryptoKeyRead, + ScopeCryptoKeyUpdate, + ScopeDebugInfoRead, + ScopeDeploymentConfigRead, + ScopeDeploymentConfigUpdate, + ScopeDeploymentStatsRead, + ScopeFileCreate, + ScopeFileRead, + ScopeGroupCreate, + ScopeGroupDelete, + ScopeGroupRead, + ScopeGroupUpdate, + ScopeGroupMemberRead, + ScopeIdpsyncSettingsRead, + ScopeIdpsyncSettingsUpdate, + ScopeInboxNotificationCreate, + ScopeInboxNotificationRead, + ScopeInboxNotificationUpdate, + ScopeLicenseCreate, + ScopeLicenseDelete, + ScopeLicenseRead, + ScopeNotificationMessageCreate, + ScopeNotificationMessageDelete, + ScopeNotificationMessageRead, + ScopeNotificationMessageUpdate, + ScopeNotificationPreferenceRead, + ScopeNotificationPreferenceUpdate, + ScopeNotificationTemplateRead, + ScopeNotificationTemplateUpdate, + ScopeOauth2AppCreate, + ScopeOauth2AppDelete, + ScopeOauth2AppRead, + ScopeOauth2AppUpdate, + ScopeOauth2AppCodeTokenCreate, + ScopeOauth2AppCodeTokenDelete, + ScopeOauth2AppCodeTokenRead, + ScopeOauth2AppSecretCreate, + ScopeOauth2AppSecretDelete, + ScopeOauth2AppSecretRead, + ScopeOauth2AppSecretUpdate, + ScopeOrganizationCreate, + ScopeOrganizationDelete, + ScopeOrganizationRead, + ScopeOrganizationUpdate, + ScopeOrganizationMemberCreate, + ScopeOrganizationMemberDelete, + ScopeOrganizationMemberRead, + ScopeOrganizationMemberUpdate, + ScopePrebuiltWorkspaceDelete, + ScopePrebuiltWorkspaceUpdate, + ScopeProvisionerDaemonCreate, + ScopeProvisionerDaemonDelete, + ScopeProvisionerDaemonRead, + ScopeProvisionerDaemonUpdate, + ScopeProvisionerJobsCreate, + ScopeProvisionerJobsRead, + ScopeProvisionerJobsUpdate, + ScopeReplicasRead, + ScopeSystemCreate, + ScopeSystemDelete, + ScopeSystemRead, + ScopeSystemUpdate, + ScopeTailnetCoordinatorCreate, + ScopeTailnetCoordinatorDelete, + ScopeTailnetCoordinatorRead, + ScopeTailnetCoordinatorUpdate, + ScopeTemplateCreate, + ScopeTemplateDelete, + ScopeTemplateRead, + ScopeTemplateUpdate, + ScopeTemplateUse, + ScopeTemplateViewInsights, + ScopeUsageEventCreate, + ScopeUsageEventRead, + ScopeUsageEventUpdate, + ScopeUserCreate, + ScopeUserDelete, + ScopeUserRead, + ScopeUserReadPersonal, + ScopeUserUpdate, + ScopeUserUpdatePersonal, + ScopeUserSecretCreate, + ScopeUserSecretDelete, + ScopeUserSecretRead, + ScopeUserSecretUpdate, + ScopeWebpushSubscriptionCreate, + ScopeWebpushSubscriptionDelete, + ScopeWebpushSubscriptionRead, + ScopeWorkspaceApplicationConnect, + ScopeWorkspaceCreate, + ScopeWorkspaceCreateAgent, + ScopeWorkspaceDelete, + ScopeWorkspaceDeleteAgent, + ScopeWorkspaceRead, + ScopeWorkspaceSsh, + ScopeWorkspaceStart, + ScopeWorkspaceStop, + ScopeWorkspaceUpdate, + ScopeWorkspaceAgentDevcontainersCreate, + ScopeWorkspaceAgentResourceMonitorCreate, + ScopeWorkspaceAgentResourceMonitorRead, + ScopeWorkspaceAgentResourceMonitorUpdate, + ScopeWorkspaceDormantApplicationConnect, + ScopeWorkspaceDormantCreate, + ScopeWorkspaceDormantCreateAgent, + ScopeWorkspaceDormantDelete, + ScopeWorkspaceDormantDeleteAgent, + ScopeWorkspaceDormantRead, + ScopeWorkspaceDormantSsh, + ScopeWorkspaceDormantStart, + ScopeWorkspaceDormantStop, + ScopeWorkspaceDormantUpdate, + ScopeWorkspaceProxyCreate, + ScopeWorkspaceProxyDelete, + ScopeWorkspaceProxyRead, + ScopeWorkspaceProxyUpdate: + return true + } + return false +} + +// AllScopeNameValues returns a slice containing all known scope values, +// including builtin and generated low-level scopes. +func AllScopeNameValues() []ScopeName { + return []ScopeName{ + ScopeName("all"), + ScopeName("application_connect"), + ScopeName("no_user_data"), + ScopeAibridgeInterceptionCreate, + ScopeAibridgeInterceptionRead, + ScopeAibridgeInterceptionUpdate, + ScopeApiKeyCreate, + ScopeApiKeyDelete, + ScopeApiKeyRead, + ScopeApiKeyUpdate, + ScopeAssignOrgRoleAssign, + ScopeAssignOrgRoleCreate, + ScopeAssignOrgRoleDelete, + ScopeAssignOrgRoleRead, + ScopeAssignOrgRoleUnassign, + ScopeAssignOrgRoleUpdate, + ScopeAssignRoleAssign, + ScopeAssignRoleRead, + ScopeAssignRoleUnassign, + ScopeAuditLogCreate, + ScopeAuditLogRead, + ScopeConnectionLogRead, + ScopeConnectionLogUpdate, + ScopeCryptoKeyCreate, + ScopeCryptoKeyDelete, + ScopeCryptoKeyRead, + ScopeCryptoKeyUpdate, + ScopeDebugInfoRead, + ScopeDeploymentConfigRead, + ScopeDeploymentConfigUpdate, + ScopeDeploymentStatsRead, + ScopeFileCreate, + ScopeFileRead, + ScopeGroupCreate, + ScopeGroupDelete, + ScopeGroupRead, + ScopeGroupUpdate, + ScopeGroupMemberRead, + ScopeIdpsyncSettingsRead, + ScopeIdpsyncSettingsUpdate, + ScopeInboxNotificationCreate, + ScopeInboxNotificationRead, + ScopeInboxNotificationUpdate, + ScopeLicenseCreate, + ScopeLicenseDelete, + ScopeLicenseRead, + ScopeNotificationMessageCreate, + ScopeNotificationMessageDelete, + ScopeNotificationMessageRead, + ScopeNotificationMessageUpdate, + ScopeNotificationPreferenceRead, + ScopeNotificationPreferenceUpdate, + ScopeNotificationTemplateRead, + ScopeNotificationTemplateUpdate, + ScopeOauth2AppCreate, + ScopeOauth2AppDelete, + ScopeOauth2AppRead, + ScopeOauth2AppUpdate, + ScopeOauth2AppCodeTokenCreate, + ScopeOauth2AppCodeTokenDelete, + ScopeOauth2AppCodeTokenRead, + ScopeOauth2AppSecretCreate, + ScopeOauth2AppSecretDelete, + ScopeOauth2AppSecretRead, + ScopeOauth2AppSecretUpdate, + ScopeOrganizationCreate, + ScopeOrganizationDelete, + ScopeOrganizationRead, + ScopeOrganizationUpdate, + ScopeOrganizationMemberCreate, + ScopeOrganizationMemberDelete, + ScopeOrganizationMemberRead, + ScopeOrganizationMemberUpdate, + ScopePrebuiltWorkspaceDelete, + ScopePrebuiltWorkspaceUpdate, + ScopeProvisionerDaemonCreate, + ScopeProvisionerDaemonDelete, + ScopeProvisionerDaemonRead, + ScopeProvisionerDaemonUpdate, + ScopeProvisionerJobsCreate, + ScopeProvisionerJobsRead, + ScopeProvisionerJobsUpdate, + ScopeReplicasRead, + ScopeSystemCreate, + ScopeSystemDelete, + ScopeSystemRead, + ScopeSystemUpdate, + ScopeTailnetCoordinatorCreate, + ScopeTailnetCoordinatorDelete, + ScopeTailnetCoordinatorRead, + ScopeTailnetCoordinatorUpdate, + ScopeTemplateCreate, + ScopeTemplateDelete, + ScopeTemplateRead, + ScopeTemplateUpdate, + ScopeTemplateUse, + ScopeTemplateViewInsights, + ScopeUsageEventCreate, + ScopeUsageEventRead, + ScopeUsageEventUpdate, + ScopeUserCreate, + ScopeUserDelete, + ScopeUserRead, + ScopeUserReadPersonal, + ScopeUserUpdate, + ScopeUserUpdatePersonal, + ScopeUserSecretCreate, + ScopeUserSecretDelete, + ScopeUserSecretRead, + ScopeUserSecretUpdate, + ScopeWebpushSubscriptionCreate, + ScopeWebpushSubscriptionDelete, + ScopeWebpushSubscriptionRead, + ScopeWorkspaceApplicationConnect, + ScopeWorkspaceCreate, + ScopeWorkspaceCreateAgent, + ScopeWorkspaceDelete, + ScopeWorkspaceDeleteAgent, + ScopeWorkspaceRead, + ScopeWorkspaceSsh, + ScopeWorkspaceStart, + ScopeWorkspaceStop, + ScopeWorkspaceUpdate, + ScopeWorkspaceAgentDevcontainersCreate, + ScopeWorkspaceAgentResourceMonitorCreate, + ScopeWorkspaceAgentResourceMonitorRead, + ScopeWorkspaceAgentResourceMonitorUpdate, + ScopeWorkspaceDormantApplicationConnect, + ScopeWorkspaceDormantCreate, + ScopeWorkspaceDormantCreateAgent, + ScopeWorkspaceDormantDelete, + ScopeWorkspaceDormantDeleteAgent, + ScopeWorkspaceDormantRead, + ScopeWorkspaceDormantSsh, + ScopeWorkspaceDormantStart, + ScopeWorkspaceDormantStop, + ScopeWorkspaceDormantUpdate, + ScopeWorkspaceProxyCreate, + ScopeWorkspaceProxyDelete, + ScopeWorkspaceProxyRead, + ScopeWorkspaceProxyUpdate, + } +} diff --git a/scripts/typegen/main.go b/scripts/typegen/main.go index acbaa9c2c3..51af0b3d18 100644 --- a/scripts/typegen/main.go +++ b/scripts/typegen/main.go @@ -18,6 +18,7 @@ import ( "golang.org/x/xerrors" + "github.com/coder/coder/v2/coderd/rbac" "github.com/coder/coder/v2/coderd/rbac/policy" "github.com/coder/coder/v2/codersdk" ) @@ -31,6 +32,9 @@ var codersdkTemplate string //go:embed typescript.tstmpl var typescriptTemplate string +//go:embed scopenames.gotmpl +var scopenamesTemplate string + //go:embed countries.tstmpl var countriesTemplate string @@ -96,6 +100,8 @@ func generateRBAC(tmpl string) ([]byte, error) { // No typescript formatting return src, nil } + case "scopenames": + source = scopenamesTemplate default: return nil, xerrors.Errorf("%q is not a valid RBAC template target", tmpl) } @@ -225,6 +231,37 @@ func generateRbacObjects(templateSource string) ([]byte, error) { "actionsList": func() []ActionDetails { return actionList }, + "actionsOf": func(d Definition) []string { + // Extract and sort action string keys for deterministic output. + list := make([]string, 0, len(d.Actions)) + for a := range d.Actions { + list = append(list, string(a)) + } + slices.Sort(list) + return list + }, + "allCaseList": func(defs []Definition) string { + // Build a multi-line comma-separated list of all scope constants (including builtins) + // suitable for use in a `case ...:` clause, without a trailing comma. + var names []string + // Builtins first, sourced dynamically from the rbac package to avoid drift. + for _, n := range rbac.BuiltinScopeNames() { + // Use typed string literals to avoid relying on constant identifiers. + names = append(names, fmt.Sprintf("ScopeName(%q)", string(n))) + } + for _, d := range defs { + res := pascalCaseName[string](d.Type) + acts := make([]string, 0, len(d.Actions)) + for a := range d.Actions { + acts = append(acts, string(a)) + } + slices.Sort(acts) + for _, a := range acts { + names = append(names, "Scope"+res+pascalCaseName[string](a)) + } + } + return strings.Join(names, ",\n\t\t") + }, "actionEnum": func(action policy.Action) string { x++ v, ok := actionMap[string(action)] diff --git a/scripts/typegen/scopenames.gotmpl b/scripts/typegen/scopenames.gotmpl new file mode 100644 index 0000000000..e5bab6deb1 --- /dev/null +++ b/scripts/typegen/scopenames.gotmpl @@ -0,0 +1,36 @@ +// Code generated by: go run ./scripts/typegen rbac scopenames; DO NOT EDIT. +package rbac + +// ScopeName constants generated from policy.RBACPermissions. +// These represent low-level ":" scope names. +// Built-in non-low-level scopes like "all" and "application_connect" remain +// declared in code, not here, to avoid duplication. + +const ( +{{- range $def := . }} + {{- $Res := pascalCaseName $def.Type }} + {{- range $act := actionsOf $def }} + Scope{{$Res}}{{ pascalCaseName $act }} ScopeName = "{{ $def.Type }}:{{ $act }}" + {{- end }} +{{- end }} +) + +// Valid reports whether the ScopeName matches one of the known scope values. +// This includes both builtin scope names and generated low-level scopes. +// Builtins are sourced from rbac.BuiltinScopeNames() at generation time to +// ensure changes in rbac/scopes.go remain in sync here. +func (e ScopeName) Valid() bool { + switch e { + case {{ allCaseList . }}: + return true + } + return false +} + +// AllScopeNameValues returns a slice containing all known scope values, +// including builtin and generated low-level scopes. +func AllScopeNameValues() []ScopeName { + return []ScopeName{ + {{ allCaseList . }}, + } +}