feat: add chat-access site-wide role to gate chat creation (#23724)

- Add `chat-access` built-in role granting chat CRUD at User scope
- Exclude `ResourceChat` from member, org member, and org service
account `allPermsExcept` calls
- Allow system, owner, and user-admin to assign the new role
- Migration auto-assigns role to users who have ever created a chat
- Update RBAC test matrix: `memberMe` denied, `chatAccessUser` allowed

**Breaking change**: Members without `chat-access` lose chat creation
ability. Migration covers existing chat creators. Members who have never
created a chat do not get this role automatically applied.

> 🤖 This PR was created by a Coder Agent and reviewed by me.
This commit is contained in:
Cian Johnston
2026-03-31 10:07:21 +01:00
committed by GitHub
parent 348a3bd693
commit 3ce82bb885
18 changed files with 451 additions and 117 deletions
+17 -11
View File
@@ -5925,56 +5925,62 @@ export interface Role {
// From codersdk/rbacroles.go
/**
* Ideally this roles would be generated from the rbac/roles.go package.
* Ideally these roles would be generated from the rbac/roles.go package.
*/
export const RoleAgentsAccess = "agents-access";
// From codersdk/rbacroles.go
/**
* Ideally these roles would be generated from the rbac/roles.go package.
*/
export const RoleAuditor = "auditor";
// From codersdk/rbacroles.go
/**
* Ideally this roles would be generated from the rbac/roles.go package.
* Ideally these roles would be generated from the rbac/roles.go package.
*/
export const RoleMember = "member";
// From codersdk/rbacroles.go
/**
* Ideally this roles would be generated from the rbac/roles.go package.
* Ideally these roles would be generated from the rbac/roles.go package.
*/
export const RoleOrganizationAdmin = "organization-admin";
// From codersdk/rbacroles.go
/**
* Ideally this roles would be generated from the rbac/roles.go package.
* Ideally these roles would be generated from the rbac/roles.go package.
*/
export const RoleOrganizationAuditor = "organization-auditor";
// From codersdk/rbacroles.go
/**
* Ideally this roles would be generated from the rbac/roles.go package.
* Ideally these roles would be generated from the rbac/roles.go package.
*/
export const RoleOrganizationMember = "organization-member";
// From codersdk/rbacroles.go
/**
* Ideally this roles would be generated from the rbac/roles.go package.
* Ideally these roles would be generated from the rbac/roles.go package.
*/
export const RoleOrganizationTemplateAdmin = "organization-template-admin";
// From codersdk/rbacroles.go
/**
* Ideally this roles would be generated from the rbac/roles.go package.
* Ideally these roles would be generated from the rbac/roles.go package.
*/
export const RoleOrganizationUserAdmin = "organization-user-admin";
// From codersdk/rbacroles.go
/**
* Ideally this roles would be generated from the rbac/roles.go package.
* Ideally these roles would be generated from the rbac/roles.go package.
*/
export const RoleOrganizationWorkspaceCreationBan =
"organization-workspace-creation-ban";
// From codersdk/rbacroles.go
/**
* Ideally this roles would be generated from the rbac/roles.go package.
* Ideally these roles would be generated from the rbac/roles.go package.
*/
export const RoleOwner = "owner";
@@ -5993,13 +5999,13 @@ export interface RoleSyncSettings {
// From codersdk/rbacroles.go
/**
* Ideally this roles would be generated from the rbac/roles.go package.
* Ideally these roles would be generated from the rbac/roles.go package.
*/
export const RoleTemplateAdmin = "template-admin";
// From codersdk/rbacroles.go
/**
* Ideally this roles would be generated from the rbac/roles.go package.
* Ideally these roles would be generated from the rbac/roles.go package.
*/
export const RoleUserAdmin = "user-admin";
@@ -29,6 +29,7 @@ const roleDescriptions: Record<string, string> = {
"user-admin": "User admin can manage all users and groups.",
"template-admin": "Template admin can manage all templates and workspaces.",
auditor: "Auditor can access the audit logs.",
"agents-access": "Use Coder Agents allows creating and using AI chats.",
member:
"Everybody is a member. This is a shared and default role for all users.",
};
@@ -177,6 +177,7 @@ const roleNamesByAccessLevel: readonly string[] = [
"organization-template-admin",
"auditor",
"organization-auditor",
"agents-access",
];
function sortRolesByAccessLevel<T extends SlimRole>(