TestWatchChatGitAuthz is the regression test for CODAGT-184. The git-watcher handler opens a bidirectional websocket into the workspace agent and streams repository diffs; before the fix it only enforced chat:read, so a chat owner who lost workspace SSH / application-connect access (e.g. by being de
(t *testing.T)
| 10555 | // disconnected-agent 400) are covered by the mock-based TestWatchChatGit |
| 10556 | // in coderd/workspaceagents_internal_test.go. |
| 10557 | func TestWatchChatGitAuthz(t *testing.T) { |
| 10558 | t.Parallel() |
| 10559 | |
| 10560 | ctx := testutil.Context(t, testutil.WaitLong) |
| 10561 | |
| 10562 | // adminClient = first user (site: owner). Creates the chat below |
| 10563 | // and is demoted after the chat is bound. |
| 10564 | adminClient, db := newChatClientWithDatabase(t) |
| 10565 | firstUser := coderdtest.CreateFirstUser(t, adminClient.Client) |
| 10566 | _ = createChatModelConfig(t, adminClient) |
| 10567 | |
| 10568 | // A second owner is needed to run UpdateUserRoles on the first |
| 10569 | // user, since the server refuses self-demotion. |
| 10570 | secondAdminClient, _ := coderdtest.CreateAnotherUser(t, adminClient.Client, firstUser.OrganizationID, rbac.RoleOwner()) |
| 10571 | |
| 10572 | // The workspace owner is a distinct user so that stripping |
| 10573 | // adminClient's site roles fully removes its workspace |
| 10574 | // SSH/ApplicationConnect. If the workspace were owned by |
| 10575 | // adminClient, the user would retain SSH via the org-member role |
| 10576 | // regardless of site-role demotion. |
| 10577 | _, workspaceOwner := coderdtest.CreateAnotherUser(t, adminClient.Client, firstUser.OrganizationID) |
| 10578 | |
| 10579 | workspaceBuild := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{ |
| 10580 | OrganizationID: firstUser.OrganizationID, |
| 10581 | OwnerID: workspaceOwner.ID, |
| 10582 | }).WithAgent().Do() |
| 10583 | |
| 10584 | chat, err := adminClient.CreateChat(ctx, codersdk.CreateChatRequest{ |
| 10585 | OrganizationID: firstUser.OrganizationID, |
| 10586 | Content: []codersdk.ChatInputPart{ |
| 10587 | {Type: codersdk.ChatInputPartTypeText, Text: "codagt-184"}, |
| 10588 | }, |
| 10589 | }) |
| 10590 | require.NoError(t, err) |
| 10591 | |
| 10592 | // Bind the chat to the workspace while adminClient still has |
| 10593 | // site-wide workspace:ssh via the owner role. |
| 10594 | err = adminClient.UpdateChat(ctx, chat.ID, codersdk.UpdateChatRequest{ |
| 10595 | WorkspaceID: &workspaceBuild.Workspace.ID, |
| 10596 | }) |
| 10597 | require.NoError(t, err) |
| 10598 | |
| 10599 | // Demote adminClient via the second owner. template-admin grants |
| 10600 | // workspace:read (site) but not workspace:ssh or |
| 10601 | // workspace:application_connect; agents-access preserves |
| 10602 | // chat:create|read|update on chats the user owns, so the |
| 10603 | // demoted user still passes ExtractChatParam for their own chat. |
| 10604 | _, err = secondAdminClient.UpdateUserRoles(ctx, firstUser.UserID.String(), codersdk.UpdateRoles{ |
| 10605 | Roles: []string{rbac.RoleTemplateAdmin().String()}, |
| 10606 | }) |
| 10607 | require.NoError(t, err) |
| 10608 | |
| 10609 | _, err = secondAdminClient.UpdateOrganizationMemberRoles(ctx, firstUser.OrganizationID, firstUser.UserID.String(), codersdk.UpdateRoles{ |
| 10610 | Roles: []string{rbac.RoleAgentsAccess()}, |
| 10611 | }) |
| 10612 | require.NoError(t, err) |
| 10613 | |
| 10614 | res, err := adminClient.Request( |
nothing calls this directly
no test coverage detected