From e3840e7d4e48a5516afda1f3013036d1c7de3943 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 23 Jan 2025 18:57:17 +0000 Subject: [PATCH] fix: fetch custom roles from workspace agent context (#16237) --- coderd/httpmw/workspaceagent.go | 31 +++-------- enterprise/coderd/gitsshkey_test.go | 81 +++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 24 deletions(-) create mode 100644 enterprise/coderd/gitsshkey_test.go diff --git a/coderd/httpmw/workspaceagent.go b/coderd/httpmw/workspaceagent.go index b27af7d0093a0..241fa385681e6 100644 --- a/coderd/httpmw/workspaceagent.go +++ b/coderd/httpmw/workspaceagent.go @@ -109,37 +109,20 @@ func ExtractWorkspaceAgentAndLatestBuild(opts ExtractWorkspaceAgentAndLatestBuil return } - //nolint:gocritic // System needs to be able to get owner roles. - roles, err := opts.DB.GetAuthorizationUserRoles(dbauthz.AsSystemRestricted(ctx), row.WorkspaceTable.OwnerID) + subject, _, err := UserRBACSubject(ctx, opts.DB, row.WorkspaceTable.OwnerID, rbac.WorkspaceAgentScope(rbac.WorkspaceAgentScopeParams{ + WorkspaceID: row.WorkspaceTable.ID, + OwnerID: row.WorkspaceTable.OwnerID, + TemplateID: row.WorkspaceTable.TemplateID, + VersionID: row.WorkspaceBuild.TemplateVersionID, + })) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Internal error checking workspace agent authorization.", + Message: "Internal error with workspace agent authorization context.", Detail: err.Error(), }) return } - roleNames, err := roles.RoleNames() - if err != nil { - httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Internal server error", - Detail: err.Error(), - }) - return - } - - subject := rbac.Subject{ - ID: row.WorkspaceTable.OwnerID.String(), - Roles: rbac.RoleIdentifiers(roleNames), - Groups: roles.Groups, - Scope: rbac.WorkspaceAgentScope(rbac.WorkspaceAgentScopeParams{ - WorkspaceID: row.WorkspaceTable.ID, - OwnerID: row.WorkspaceTable.OwnerID, - TemplateID: row.WorkspaceTable.TemplateID, - VersionID: row.WorkspaceBuild.TemplateVersionID, - }), - }.WithCachedASTValue() - ctx = context.WithValue(ctx, workspaceAgentContextKey{}, row.WorkspaceAgent) ctx = context.WithValue(ctx, latestBuildContextKey{}, row.WorkspaceBuild) // Also set the dbauthz actor for the request. diff --git a/enterprise/coderd/gitsshkey_test.go b/enterprise/coderd/gitsshkey_test.go new file mode 100644 index 0000000000000..a4978ac8fdad3 --- /dev/null +++ b/enterprise/coderd/gitsshkey_test.go @@ -0,0 +1,81 @@ +package coderd_test + +import ( + "context" + "testing" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" + + "github.com/coder/coder/v2/coderd/coderdtest" + "github.com/coder/coder/v2/coderd/rbac" + "github.com/coder/coder/v2/codersdk" + "github.com/coder/coder/v2/codersdk/agentsdk" + "github.com/coder/coder/v2/enterprise/coderd/coderdenttest" + "github.com/coder/coder/v2/enterprise/coderd/license" + "github.com/coder/coder/v2/provisioner/echo" + "github.com/coder/coder/v2/testutil" +) + +// TestAgentGitSSHKeyCustomRoles tests that the agent can fetch its git ssh key when +// the user has a custom role in a second workspace. +func TestAgentGitSSHKeyCustomRoles(t *testing.T) { + t.Parallel() + + owner, _ := coderdenttest.New(t, &coderdenttest.Options{ + Options: &coderdtest.Options{ + IncludeProvisionerDaemon: true, + }, + LicenseOptions: &coderdenttest.LicenseOptions{ + Features: license.Features{ + codersdk.FeatureCustomRoles: 1, + codersdk.FeatureMultipleOrganizations: 1, + codersdk.FeatureExternalProvisionerDaemons: 1, + }, + }, + }) + + // When custom roles exist in a second organization + org := coderdenttest.CreateOrganization(t, owner, coderdenttest.CreateOrganizationOptions{ + IncludeProvisionerDaemon: true, + }) + + ctx := testutil.Context(t, testutil.WaitShort) + //nolint:gocritic // required to make orgs + newRole, err := owner.CreateOrganizationRole(ctx, codersdk.Role{ + Name: "custom", + OrganizationID: org.ID.String(), + DisplayName: "", + SitePermissions: nil, + OrganizationPermissions: codersdk.CreatePermissions(map[codersdk.RBACResource][]codersdk.RBACAction{ + codersdk.ResourceTemplate: {codersdk.ActionRead, codersdk.ActionCreate, codersdk.ActionUpdate}, + }), + UserPermissions: nil, + }) + require.NoError(t, err) + + // Create the new user + client, _ := coderdtest.CreateAnotherUser(t, owner, org.ID, rbac.RoleIdentifier{Name: newRole.Name, OrganizationID: org.ID}) + + // Create the workspace + agent + authToken := uuid.NewString() + version := coderdtest.CreateTemplateVersion(t, client, org.ID, &echo.Responses{ + Parse: echo.ParseComplete, + ProvisionPlan: echo.PlanComplete, + ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + }) + project := coderdtest.CreateTemplate(t, client, org.ID, version.ID) + coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) + workspace := coderdtest.CreateWorkspace(t, client, project.ID) + coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID) + + agentClient := agentsdk.New(client.URL) + agentClient.SetSessionToken(authToken) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + agentKey, err := agentClient.GitSSHKey(ctx) + require.NoError(t, err) + require.NotEmpty(t, agentKey.PrivateKey) +}