Helper function to parse URL safely in tests TestMCPHTTP_E2E_WorkspaceSSHAuthz verifies that users who can read a workspace but lack ActionSSH are denied when calling workspace tools through the MCP HTTP endpoint.
(t *testing.T)
| 1402 | // a workspace but lack ActionSSH are denied when calling workspace |
| 1403 | // tools through the MCP HTTP endpoint. |
| 1404 | func TestMCPHTTP_E2E_WorkspaceSSHAuthz(t *testing.T) { |
| 1405 | t.Parallel() |
| 1406 | |
| 1407 | coderClient, closer, api := coderdtest.NewWithAPI(t, nil) |
| 1408 | defer closer.Close() |
| 1409 | |
| 1410 | admin := coderdtest.CreateFirstUser(t, coderClient) |
| 1411 | |
| 1412 | // Create a workspace owned by the admin. |
| 1413 | r := dbfake.WorkspaceBuild(t, api.Database, database.WorkspaceTable{ |
| 1414 | Name: "authz-test-ws", |
| 1415 | OrganizationID: admin.OrganizationID, |
| 1416 | OwnerID: admin.UserID, |
| 1417 | }).WithAgent().Do() |
| 1418 | |
| 1419 | fs := afero.NewMemMapFs() |
| 1420 | require.NoError(t, fs.MkdirAll("/tmp", 0o755)) |
| 1421 | require.NoError(t, afero.WriteFile(fs, "/tmp/secret.txt", []byte("secret-content"), 0o644)) |
| 1422 | |
| 1423 | _ = agenttest.New(t, coderClient.URL, r.AgentToken, func(opts *agent.Options) { |
| 1424 | opts.Filesystem = fs |
| 1425 | }) |
| 1426 | coderdtest.NewWorkspaceAgentWaiter(t, coderClient, r.Workspace.ID).Wait() |
| 1427 | |
| 1428 | // Create a second user with template-admin role. This role grants |
| 1429 | // ActionRead on workspaces but not ActionSSH. |
| 1430 | tmplAdminClient, _ := coderdtest.CreateAnotherUser( |
| 1431 | t, coderClient, admin.OrganizationID, rbac.RoleTemplateAdmin(), |
| 1432 | ) |
| 1433 | |
| 1434 | // Connect with the template-admin user. |
| 1435 | mcpURL := api.AccessURL.String() + mcpserver.MCPEndpoint |
| 1436 | mcpClient, err := mcpclient.NewStreamableHttpClient(mcpURL, |
| 1437 | transport.WithHTTPHeaders(map[string]string{ |
| 1438 | "Authorization": "Bearer " + tmplAdminClient.SessionToken(), |
| 1439 | })) |
| 1440 | require.NoError(t, err) |
| 1441 | defer func() { |
| 1442 | _ = mcpClient.Close() |
| 1443 | }() |
| 1444 | |
| 1445 | ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) |
| 1446 | defer cancel() |
| 1447 | |
| 1448 | require.NoError(t, mcpClient.Start(ctx)) |
| 1449 | _, err = mcpClient.Initialize(ctx, mcp.InitializeRequest{ |
| 1450 | Params: mcp.InitializeParams{ |
| 1451 | ProtocolVersion: mcp.LATEST_PROTOCOL_VERSION, |
| 1452 | ClientInfo: mcp.Implementation{ |
| 1453 | Name: "test-client-authz", |
| 1454 | Version: "1.0.0", |
| 1455 | }, |
| 1456 | }, |
| 1457 | }) |
| 1458 | require.NoError(t, err) |
| 1459 | |
| 1460 | // Calling a workspace tool that requires an agent connection |
| 1461 | // should fail because the template-admin user lacks ActionSSH. |
nothing calls this directly
no test coverage detected