mirror of
https://github.com/coder/coder.git
synced 2026-10-16 14:48:56 +08:00
## Stack Part of [CODAGT-1341](https://linear.app/codercom/issue/CODAGT-1341/support-organization-level-skills-in-coder-agents). This stack adds organization skills to Coder Agents: admins publish a skill once per organization, share it with groups or members, and chats in that organization load it next to personal and workspace skills. It replaces the 21-PR draft stack #30584 to #30604 with five PRs, bottom to top: 1. **#30609** data model: one migration, queries, RBAC and dbauthz (this PR) 2. #30610 organization skill API routes and the scope-neutral SDK 3. #30611 sharing ACL routes 4. #30612 settings UI: shared skills table, admin page, member section 5. #30613 chats: chatd loading, `/` menu, docs ## What changes This PR adds the data model and authorization for organization skills. Personal skills keep working unchanged: `git diff origin/main -- codersdk site` shows only generated RBAC and API key scope additions (`apikey_scopes_gen.go`, `rbacresources_gen.go`, `rbacresourcesGenerated.ts`, the RBAC and scope unions in `typesGenerated.ts`) and `codersdk.ResourceTypeOrganizationSkill` with its friendly string. No personal route, SDK type or site component changes, and `coderd/userskills_test.go` changes only by mechanical constant renames. **One migration, `000621_skills`** (please review it closely): - renames `user_skills` to `skills` with its constraints, index and triggers, and recreates the cap function and `delete_deleted_user_resources`, whose PL/pgSQL bodies name the table; - adds `organization_id` and `project_id` (`ON DELETE CASCADE`), `enabled` (default true), and `group_acl` / `user_acl` (`jsonb`, default `{}`), with CHECKs for exactly one owner, ACLs that are JSON objects, and ACLs that stay empty unless an organization owns the row, plus partial unique name indexes per owner kind; - replaces the cap trigger with `enforce_skills_per_owner_limit()`: 100 rows per owner, disabled rows included, locking the owner row `FOR NO KEY UPDATE` so unrelated foreign key checks are not blocked; - adds a `BEFORE DELETE` trigger on `organization_members` that removes a departing member from `user_acl` on that organization's skills, so rejoining doesn't restore a direct grant; - adds the `organization_skill` `resource_type` and `api_key_scope` enum values last; nothing in the migration uses them. The down migration undoes the three old migrations in reverse order: it drops organization and project rows and restores `user_skills`, its names, the cap function and `user_id NOT NULL`. Authorization: a new RBAC resource `organization_skill` (create, read, update, delete, share). Owner and org admin get it; site and org auditors get read; members read only through the skill's ACL, which applies only to members of its organization. `Skill.RBACObject()` returns the organization object with both ACLs; project rows stay denied to everyone. New sqlc queries cover organization insert, get, list (SQL-filtered through `GetAuthorizedOrganizationSkillMetadata` and a `regosql` converter), update and delete, with dbauthz wrappers and the audit table entries. No routes yet. ## Commits The first commit reproduces the reviewed code from the old stack; each later commit is a labeled trim or fix. - `baa3150e3b7` feat: add the organization skills data model. Reproduction: every non-site file this commit changes equals the reviewed old `authz` head `1e16b3004c4`, except that the old stack's migrations 000615, 000616 and 000617 are merged into one migration (then `000615_skills`, now `000621_skills`) with one fixture (`testdata/fixtures/000621_skills.up.sql`) and one migration test (now `testMigration000621Skills`). At that commit, `dump.sql` was byte-identical to the old head's. - `1b84e83463a` test(coderd/database): keep only the SQL paths of the org skill matrix. `TestOrganizationSkillAuthorization` becomes `TestOrganizationSkillQueries` instead of being deleted, because coverage showed it is this PR's only run of the `GetAuthorizedOrganizationSkillMetadata` row scan (80% to 65% without it) and of the organization update and delete queries (100% to 0%). Swapping `group_acl` and `user_acl` in the converter makes `GrantedMember` fail. The removed cases are decided by `TestRolePermissions`, `TestSkillRBACObjectOrganizationACL` and the dbauthz method suite. - `e2931b412e9` test(coderd/database): fold the skill cap cases into existing coverage. The user case is covered by `TestUserSkillLimit` and `TestUserSkillLimitConcurrentCreates`; the organization case folds into `ConcurrentOrganizationInsertsHonorCap`, which now also checks that disabled rows count and another organization is unaffected (mutation checked); the project case stays. - `be62aea9021` test(coderd/rbac): drop `TestOrganizationSkillReadScope`, covered by `TestExternalScopeNames` and the generated scope file. - `0c275ba37de` merges main at `454a2fcc697`, which took 000615 to 000618: it renumbers the migration to 000619 and resolves `dump.sql`, `check_constraint.go` and the generated API docs. `8f2b4b87ee2` merges main at `ad85646164e`, which took 000619: it renumbers the migration to 000620. - `5248d56b64f` merges main at `e7b76872098`, which took 000620 (`000620_audit_chat_project_instructions_resource_type`): it renumbers the migration, fixture and migration test to `000621_skills` (step `Migration000621Skills`, version 621), and resolves `enterprise/audit/table.go` (keeping main's `AuditableChatProjectInstructions` entries and the `Skill` entry), `dump.sql` and `models.go` (the `resource_type` enum gets `chat_project_instructions`, then `organization_skill`) and the regenerated `schemas.md`. The up and down SQL are unchanged by every renumbering. ## Size Production +549/-67, tests +728/-33, generated +881/-314; total +2158/-414. This is above the 500-line reviewer-friendly limit on purpose: Mike asked for 4 to 5 PRs instead of the 21 small ones this stack replaces. ## Replaces #30584 (rename `user_skills` to `skills`), #30585 (skill owners), #30586 (organization skill authorization). ## Validation - Migration test: up keeps the existing personal row and its ID; down restores the `user_skills` names, cap function and `NOT NULL`, and drops organization rows; soft-deleting a user removes their skills after up, after down, and after re-up. - `go test` passed on `be62aea9021`, before the main merges, for `coderd` (skill tests), `coderd/database`, `dbauthz`, `migrations`, `gentest`, `coderd/rbac/...`, `coderd/x/chatd`, `coderd/x/skills`, `enterprise/audit` and `enterprise/coderd`; `make lint/check-scopes` passed; the four personal skills Vitest files pass unchanged. - On `5248d56b64f`: build, vet, `TestMigrate`, `TestMigrationChain`, the `migrations`, `database`, `dbauthz`, `gentest`, `rbac` and `enterprise/audit` packages pass; `make gen` run twice is clean, and `make pre-commit` passes. - Remote UAT round 7 through Coder Agents passed on the whole stack; see #30613's Validation for scope and findings. > Xum opened this PR on behalf of @ibetitsmike.
98 lines
14 KiB
Go
Generated
98 lines
14 KiB
Go
Generated
// Code generated by scripts/dbgen/main.go. DO NOT EDIT.
|
|
package database
|
|
|
|
// CheckConstraint represents a named check constraint on a table.
|
|
type CheckConstraint string
|
|
|
|
// CheckConstraint enums.
|
|
const (
|
|
CheckAIGatewayKeysHashedSecretCheck CheckConstraint = "ai_gateway_keys_hashed_secret_check" // ai_gateway_keys
|
|
CheckAIGatewayKeysNameCheck CheckConstraint = "ai_gateway_keys_name_check" // ai_gateway_keys
|
|
CheckAIGatewayKeysSecretPrefixCheck CheckConstraint = "ai_gateway_keys_secret_prefix_check" // ai_gateway_keys
|
|
CheckAIModelPricesCacheReadPriceCheck CheckConstraint = "ai_model_prices_cache_read_price_check" // ai_model_prices
|
|
CheckAIModelPricesCacheWritePriceCheck CheckConstraint = "ai_model_prices_cache_write_price_check" // ai_model_prices
|
|
CheckAIModelPricesInputPriceCheck CheckConstraint = "ai_model_prices_input_price_check" // ai_model_prices
|
|
CheckAIModelPricesOutputPriceCheck CheckConstraint = "ai_model_prices_output_price_check" // ai_model_prices
|
|
CheckAIProvidersNameCheck CheckConstraint = "ai_providers_name_check" // ai_providers
|
|
CheckAIUserDailySpendSpendMicrosCheck CheckConstraint = "ai_user_daily_spend_spend_micros_check" // ai_user_daily_spend
|
|
CheckAibridgeTokenUsagesCacheReadPriceMicrosCheck CheckConstraint = "aibridge_token_usages_cache_read_price_micros_check" // aibridge_token_usages
|
|
CheckAibridgeTokenUsagesCacheWritePriceMicrosCheck CheckConstraint = "aibridge_token_usages_cache_write_price_micros_check" // aibridge_token_usages
|
|
CheckAibridgeTokenUsagesCostMicrosCheck CheckConstraint = "aibridge_token_usages_cost_micros_check" // aibridge_token_usages
|
|
CheckAibridgeTokenUsagesInputPriceMicrosCheck CheckConstraint = "aibridge_token_usages_input_price_micros_check" // aibridge_token_usages
|
|
CheckAibridgeTokenUsagesOutputPriceMicrosCheck CheckConstraint = "aibridge_token_usages_output_price_micros_check" // aibridge_token_usages
|
|
CheckAPIKeysAllowListNotEmpty CheckConstraint = "api_keys_allow_list_not_empty" // api_keys
|
|
CheckBoundaryLogsSequenceNumberCheck CheckConstraint = "boundary_logs_sequence_number_check" // boundary_logs
|
|
CheckChatModelConfigsAIProviderRequiredWhenActive CheckConstraint = "chat_model_configs_ai_provider_required_when_active" // chat_model_configs
|
|
CheckChatModelConfigsCompressionThresholdCheck CheckConstraint = "chat_model_configs_compression_threshold_check" // chat_model_configs
|
|
CheckChatModelConfigsContextLimitCheck CheckConstraint = "chat_model_configs_context_limit_check" // chat_model_configs
|
|
CheckChatModelConfigsGroupAclIsObject CheckConstraint = "chat_model_configs_group_acl_is_object" // chat_model_configs
|
|
CheckChatModelConfigsUserAclIsObject CheckConstraint = "chat_model_configs_user_acl_is_object" // chat_model_configs
|
|
CheckChatOrganizationModelOverridesContextCheck CheckConstraint = "chat_organization_model_overrides_context_check" // chat_organization_model_overrides
|
|
CheckChatProjectInstructionsNotBlank CheckConstraint = "chat_project_instructions_not_blank" // chat_project_instructions
|
|
CheckChatProjectMemoriesBodyLength CheckConstraint = "chat_project_memories_body_length" // chat_project_memories
|
|
CheckChatProjectMemoriesDescriptionLength CheckConstraint = "chat_project_memories_description_length" // chat_project_memories
|
|
CheckChatProjectMemoriesNameFormat CheckConstraint = "chat_project_memories_name_format" // chat_project_memories
|
|
CheckChatProjectsDescriptionLength CheckConstraint = "chat_projects_description_length" // chat_projects
|
|
CheckChatProjectsIconLength CheckConstraint = "chat_projects_icon_length" // chat_projects
|
|
CheckChatProjectsNameLength CheckConstraint = "chat_projects_name_length" // chat_projects
|
|
CheckChatProjectsNameNotBlank CheckConstraint = "chat_projects_name_not_blank" // chat_projects
|
|
CheckChatUsageLimitConfigDefaultLimitMicrosCheck CheckConstraint = "chat_usage_limit_config_default_limit_micros_check" // chat_usage_limit_config
|
|
CheckChatUsageLimitConfigPeriodCheck CheckConstraint = "chat_usage_limit_config_period_check" // chat_usage_limit_config
|
|
CheckChatUsageLimitConfigSingletonCheck CheckConstraint = "chat_usage_limit_config_singleton_check" // chat_usage_limit_config
|
|
CheckChatUserModelOverridesContextCheck CheckConstraint = "chat_user_model_overrides_context_check" // chat_user_model_overrides
|
|
CheckChatUserModelOverridesModeCheck CheckConstraint = "chat_user_model_overrides_mode_check" // chat_user_model_overrides
|
|
CheckChatUserModelOverridesModelRequiresConfigCheck CheckConstraint = "chat_user_model_overrides_model_requires_config_check" // chat_user_model_overrides
|
|
CheckChatAclOnlyOnRootChats CheckConstraint = "chat_acl_only_on_root_chats" // chats
|
|
CheckChatGroupAclNotNullJsonb CheckConstraint = "chat_group_acl_not_null_jsonb" // chats
|
|
CheckChatUserAclNotNullJsonb CheckConstraint = "chat_user_acl_not_null_jsonb" // chats
|
|
CheckChatsPinOrderArchivedCheck CheckConstraint = "chats_pin_order_archived_check" // chats
|
|
CheckChatsPinOrderParentCheck CheckConstraint = "chats_pin_order_parent_check" // chats
|
|
CheckOneTimePasscodeSet CheckConstraint = "one_time_passcode_set" // users
|
|
CheckUsersChatSpendLimitMicrosCheck CheckConstraint = "users_chat_spend_limit_micros_check" // users
|
|
CheckUsersEmailNotEmpty CheckConstraint = "users_email_not_empty" // users
|
|
CheckUsersServiceAccountLoginType CheckConstraint = "users_service_account_login_type" // users
|
|
CheckUsersUsernameMinLength CheckConstraint = "users_username_min_length" // users
|
|
CheckConnectionLogsClientSessionIDCheck CheckConstraint = "connection_logs_client_session_id_check" // connection_logs
|
|
CheckOrganizationIDNotZero CheckConstraint = "organization_id_not_zero" // custom_roles
|
|
CheckGroupAIBudgetsSpendLimitMicrosCheck CheckConstraint = "group_ai_budgets_spend_limit_micros_check" // group_ai_budgets
|
|
CheckGroupsChatSpendLimitMicrosCheck CheckConstraint = "groups_chat_spend_limit_micros_check" // groups
|
|
CheckMcpServerConfigsAuthTypeCheck CheckConstraint = "mcp_server_configs_auth_type_check" // mcp_server_configs
|
|
CheckMcpServerConfigsAvailabilityCheck CheckConstraint = "mcp_server_configs_availability_check" // mcp_server_configs
|
|
CheckMcpServerConfigsGroupAclIsObject CheckConstraint = "mcp_server_configs_group_acl_is_object" // mcp_server_configs
|
|
CheckMcpServerConfigsTransportCheck CheckConstraint = "mcp_server_configs_transport_check" // mcp_server_configs
|
|
CheckMcpServerConfigsUserAclIsObject CheckConstraint = "mcp_server_configs_user_acl_is_object" // mcp_server_configs
|
|
CheckOauth2ProviderAppCodesScopeNotEmpty CheckConstraint = "oauth2_provider_app_codes_scope_not_empty" // oauth2_provider_app_codes
|
|
CheckOauth2ProviderAppTokensScopeNotEmpty CheckConstraint = "oauth2_provider_app_tokens_scope_not_empty" // oauth2_provider_app_tokens
|
|
CheckOauth2ProviderAppsClientTypeCheck CheckConstraint = "oauth2_provider_apps_client_type_check" // oauth2_provider_apps
|
|
CheckMaxProvisionerLogsLength CheckConstraint = "max_provisioner_logs_length" // provisioner_jobs
|
|
CheckNatsPortValidTcp CheckConstraint = "nats_port_valid_tcp" // replicas
|
|
CheckSkillsAclOnlyOnOrganizationSkills CheckConstraint = "skills_acl_only_on_organization_skills" // skills
|
|
CheckSkillsContentSize CheckConstraint = "skills_content_size" // skills
|
|
CheckSkillsDescriptionSize CheckConstraint = "skills_description_size" // skills
|
|
CheckSkillsGroupAclIsObject CheckConstraint = "skills_group_acl_is_object" // skills
|
|
CheckSkillsNameFormat CheckConstraint = "skills_name_format" // skills
|
|
CheckSkillsNameSize CheckConstraint = "skills_name_size" // skills
|
|
CheckSkillsSingleOwner CheckConstraint = "skills_single_owner" // skills
|
|
CheckSkillsUserAclIsObject CheckConstraint = "skills_user_acl_is_object" // skills
|
|
CheckTelemetryLockEventTypeConstraint CheckConstraint = "telemetry_lock_event_type_constraint" // telemetry_locks
|
|
CheckValidationMonotonicOrder CheckConstraint = "validation_monotonic_order" // template_version_parameters
|
|
CheckUsageEventTypeCheck CheckConstraint = "usage_event_type_check" // usage_events
|
|
CheckUsageEventsAgentRuntimeHourAligned CheckConstraint = "usage_events_agent_runtime_hour_aligned" // usage_events
|
|
CheckUserAIBudgetOverridesSpendLimitMicrosCheck CheckConstraint = "user_ai_budget_overrides_spend_limit_micros_check" // user_ai_budget_overrides
|
|
CheckUserAIProviderKeysAPIKeyCheck CheckConstraint = "user_ai_provider_keys_api_key_check" // user_ai_provider_keys
|
|
CheckUserSecretsEnabledRequiresTarget CheckConstraint = "user_secrets_enabled_requires_target" // user_secrets
|
|
CheckWorkspaceAgentScriptsDependenciesIsArray CheckConstraint = "workspace_agent_scripts_dependencies_is_array" // workspace_agent_scripts
|
|
CheckMaxLogsLength CheckConstraint = "max_logs_length" // workspace_agents
|
|
CheckSubsystemsNotNone CheckConstraint = "subsystems_not_none" // workspace_agents
|
|
CheckWorkspaceBuildOrchestrationsAttemptCountCheck CheckConstraint = "workspace_build_orchestrations_attempt_count_check" // workspace_build_orchestrations
|
|
CheckWorkspaceBuildOrchestrationsChildLogLevelCheck CheckConstraint = "workspace_build_orchestrations_child_log_level_check" // workspace_build_orchestrations
|
|
CheckWorkspaceBuildOrchestrationsChildParametersCheck CheckConstraint = "workspace_build_orchestrations_child_parameters_check" // workspace_build_orchestrations
|
|
CheckWorkspaceBuildOrchestrationsChildPresetVersionCheck CheckConstraint = "workspace_build_orchestrations_child_preset_version_check" // workspace_build_orchestrations
|
|
CheckWorkspaceBuildOrchestrationsCompletedChildCheck CheckConstraint = "workspace_build_orchestrations_completed_child_check" // workspace_build_orchestrations
|
|
CheckWorkspaceBuildOrchestrationsNextRetryAfterCheck CheckConstraint = "workspace_build_orchestrations_next_retry_after_check" // workspace_build_orchestrations
|
|
CheckWorkspaceBuildOrchestrationsStatusCheck CheckConstraint = "workspace_build_orchestrations_status_check" // workspace_build_orchestrations
|
|
CheckWorkspaceBuildsDeadlineBelowMaxDeadline CheckConstraint = "workspace_builds_deadline_below_max_deadline" // workspace_builds
|
|
CheckGroupAclIsObject CheckConstraint = "group_acl_is_object" // workspaces
|
|
CheckUserAclIsObject CheckConstraint = "user_acl_is_object" // workspaces
|
|
)
|