diff --git a/coderd/dynamicparameters/render.go b/coderd/dynamicparameters/render.go index fd050b4062e5f..3392f9c3abdc3 100644 --- a/coderd/dynamicparameters/render.go +++ b/coderd/dynamicparameters/render.go @@ -243,24 +243,30 @@ func (r *dynamicRenderer) getWorkspaceOwnerData(ctx context.Context, ownerID uui return nil // already fetched } - // You only need to be able to read the organization member to get the owner - // data. Only the terraform files can therefore leak more information than the - // caller should have access to. All this info should be public assuming you can - // read the user though. - mem, err := database.ExpectOne(r.db.OrganizationMembers(ctx, database.OrganizationMembersParams{ - OrganizationID: r.data.templateVersion.OrganizationID, - UserID: ownerID, - IncludeSystem: true, - })) + user, err := r.db.GetUserByID(ctx, ownerID) if err != nil { - return err - } + // If the user failed to read, we also try to read the user from their + // organization member. You only need to be able to read the organization member + // to get the owner data. + // + // Only the terraform files can therefore leak more information than the + // caller should have access to. All this info should be public assuming you can + // read the user though. + mem, err := database.ExpectOne(r.db.OrganizationMembers(ctx, database.OrganizationMembersParams{ + OrganizationID: r.data.templateVersion.OrganizationID, + UserID: ownerID, + IncludeSystem: true, + })) + if err != nil { + return xerrors.Errorf("fetch user: %w", err) + } - // User data is required for the form. Org member is checked above - // nolint:gocritic - user, err := r.db.GetUserByID(dbauthz.AsProvisionerd(ctx), mem.OrganizationMember.UserID) - if err != nil { - return xerrors.Errorf("fetch user: %w", err) + // Org member fetched, so use the provisioner context to fetch the user. + //nolint:gocritic // Has the correct permissions, and matches the provisioning flow. + user, err = r.db.GetUserByID(dbauthz.AsProvisionerd(ctx), mem.OrganizationMember.UserID) + if err != nil { + return xerrors.Errorf("fetch user: %w", err) + } } // nolint:gocritic // This is kind of the wrong query to use here, but it @@ -314,10 +320,10 @@ func (r *dynamicRenderer) getWorkspaceOwnerData(ctx context.Context, ownerID uui } r.currentOwner = &previewtypes.WorkspaceOwner{ - ID: mem.OrganizationMember.UserID.String(), - Name: mem.Username, - FullName: mem.Name, - Email: mem.Email, + ID: user.ID.String(), + Name: user.Username, + FullName: user.Name, + Email: user.Email, LoginType: string(user.LoginType), RBACRoles: ownerRoles, SSHPublicKey: key.PublicKey, diff --git a/enterprise/coderd/workspaces_test.go b/enterprise/coderd/workspaces_test.go index ef9d1b977ea00..228b11f485a96 100644 --- a/enterprise/coderd/workspaces_test.go +++ b/enterprise/coderd/workspaces_test.go @@ -287,11 +287,9 @@ func TestCreateUserWorkspace(t *testing.T) { OrganizationID: first.OrganizationID, }) - version := coderdtest.CreateTemplateVersion(t, admin, first.OrganizationID, nil) - coderdtest.AwaitTemplateVersionJobCompleted(t, admin, version.ID) - template := coderdtest.CreateTemplate(t, admin, first.OrganizationID, version.ID) + template, _ := coderdtest.DynamicParameterTemplate(t, admin, first.OrganizationID, coderdtest.DynamicParameterTemplateParams{}) - ctx = testutil.Context(t, testutil.WaitLong*1000) // Reset the context to avoid timeouts. + ctx = testutil.Context(t, testutil.WaitLong) wrk, err := creator.CreateUserWorkspace(ctx, adminID.ID.String(), codersdk.CreateWorkspaceRequest{ TemplateID: template.ID, @@ -306,6 +304,66 @@ func TestCreateUserWorkspace(t *testing.T) { require.NoError(t, err) }) + t.Run("ForANonOrgMember", func(t *testing.T) { + t.Parallel() + + owner, first := coderdenttest.New(t, &coderdenttest.Options{ + Options: &coderdtest.Options{ + IncludeProvisionerDaemon: true, + }, + LicenseOptions: &coderdenttest.LicenseOptions{ + Features: license.Features{ + codersdk.FeatureCustomRoles: 1, + codersdk.FeatureTemplateRBAC: 1, + codersdk.FeatureMultipleOrganizations: 1, + }, + }, + }) + ctx := testutil.Context(t, testutil.WaitShort) + //nolint:gocritic // using owner to setup roles + r, err := owner.CreateOrganizationRole(ctx, codersdk.Role{ + Name: "creator", + OrganizationID: first.OrganizationID.String(), + DisplayName: "Creator", + OrganizationPermissions: codersdk.CreatePermissions(map[codersdk.RBACResource][]codersdk.RBACAction{ + codersdk.ResourceWorkspace: {codersdk.ActionCreate, codersdk.ActionWorkspaceStart, codersdk.ActionUpdate, codersdk.ActionRead}, + codersdk.ResourceOrganizationMember: {codersdk.ActionRead}, + }), + }) + require.NoError(t, err) + + // user to make the workspace for, **note** the user is not a member of the first org. + // This is strange, but technically valid. The creator can create a workspace for + // this user in this org, even though the user cannot access the workspace. + secondOrg := coderdenttest.CreateOrganization(t, owner, coderdenttest.CreateOrganizationOptions{}) + _, forUser := coderdtest.CreateAnotherUser(t, owner, secondOrg.ID) + + // try the test action with this user & custom role + creator, _ := coderdtest.CreateAnotherUser(t, owner, first.OrganizationID, rbac.RoleMember(), + rbac.RoleTemplateAdmin(), // Need site wide access to make workspace for non-org + rbac.RoleIdentifier{ + Name: r.Name, + OrganizationID: first.OrganizationID, + }, + ) + + template, _ := coderdtest.DynamicParameterTemplate(t, creator, first.OrganizationID, coderdtest.DynamicParameterTemplateParams{}) + + ctx = testutil.Context(t, testutil.WaitLong) + + wrk, err := creator.CreateUserWorkspace(ctx, forUser.ID.String(), codersdk.CreateWorkspaceRequest{ + TemplateID: template.ID, + Name: "workspace", + }) + require.NoError(t, err) + coderdtest.AwaitWorkspaceBuildJobCompleted(t, creator, wrk.LatestBuild.ID) + + _, err = creator.WorkspaceByOwnerAndName(ctx, forUser.Username, wrk.Name, codersdk.WorkspaceOptions{ + IncludeDeleted: false, + }) + require.NoError(t, err) + }) + // Asserting some authz calls when creating a workspace. t.Run("AuthzStory", func(t *testing.T) { t.Parallel() pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy