mirror of
https://github.com/coder/coder.git
synced 2026-06-03 21:18:24 +00:00
37a8e61ea2
* Removed the shared-workspaces experiment and cleaned up related middleware * Added beta tagging to the UI for shared workspaces
388 lines
13 KiB
Go
388 lines
13 KiB
Go
package cli_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/coder/coder/v2/cli/clitest"
|
|
"github.com/coder/coder/v2/coderd/coderdtest"
|
|
"github.com/coder/coder/v2/coderd/database"
|
|
"github.com/coder/coder/v2/coderd/database/dbfake"
|
|
"github.com/coder/coder/v2/coderd/rbac"
|
|
"github.com/coder/coder/v2/codersdk"
|
|
"github.com/coder/coder/v2/enterprise/coderd/coderdenttest"
|
|
"github.com/coder/coder/v2/enterprise/coderd/license"
|
|
"github.com/coder/coder/v2/testutil"
|
|
)
|
|
|
|
func TestSharingShare(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("ShareWithGroups_Simple", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var (
|
|
client, db, orgOwner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
|
|
LicenseOptions: &coderdenttest.LicenseOptions{
|
|
Features: license.Features{
|
|
codersdk.FeatureTemplateRBAC: 1,
|
|
},
|
|
},
|
|
})
|
|
workspaceOwnerClient, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID))
|
|
workspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
|
|
OwnerID: workspaceOwner.ID,
|
|
OrganizationID: orgOwner.OrganizationID,
|
|
}).Do().Workspace
|
|
_, orgMember = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID)
|
|
)
|
|
|
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
|
|
|
group, err := createGroupWithMembers(ctx, client, orgOwner.OrganizationID, "new-group", []uuid.UUID{orgMember.ID})
|
|
require.NoError(t, err)
|
|
|
|
inv, root := clitest.New(t, "sharing", "share", workspace.Name, "--group", group.Name)
|
|
clitest.SetupConfig(t, workspaceOwnerClient, root)
|
|
|
|
out := new(bytes.Buffer)
|
|
inv.Stdout = out
|
|
err = inv.WithContext(ctx).Run()
|
|
require.NoError(t, err)
|
|
|
|
acl, err := workspaceOwnerClient.WorkspaceACL(inv.Context(), workspace.ID)
|
|
require.NoError(t, err)
|
|
assert.Len(t, acl.Groups, 1)
|
|
assert.Equal(t, acl.Groups[0].Group.ID, group.ID)
|
|
assert.Equal(t, acl.Groups[0].Role, codersdk.WorkspaceRoleUse)
|
|
|
|
found := false
|
|
for _, line := range strings.Split(out.String(), "\n") {
|
|
found = strings.Contains(line, group.Name) && strings.Contains(line, string(codersdk.WorkspaceRoleUse))
|
|
if found {
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, found, "Expected to find group name %s and role %s in output: %s", group.Name, codersdk.WorkspaceRoleUse, out.String())
|
|
})
|
|
|
|
t.Run("ShareWithGroups_Multiple", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var (
|
|
client, db, orgOwner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
|
|
LicenseOptions: &coderdenttest.LicenseOptions{
|
|
Features: license.Features{
|
|
codersdk.FeatureTemplateRBAC: 1,
|
|
},
|
|
},
|
|
})
|
|
|
|
workspaceOwnerClient, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID))
|
|
workspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
|
|
OwnerID: workspaceOwner.ID,
|
|
OrganizationID: orgOwner.OrganizationID,
|
|
}).Do().Workspace
|
|
|
|
_, wibbleMember = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID)
|
|
_, wobbleMember = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID)
|
|
)
|
|
|
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
|
|
|
wibbleGroup, err := createGroupWithMembers(ctx, client, orgOwner.OrganizationID, "wibble", []uuid.UUID{wibbleMember.ID})
|
|
require.NoError(t, err)
|
|
|
|
wobbleGroup, err := createGroupWithMembers(ctx, client, orgOwner.OrganizationID, "wobble", []uuid.UUID{wobbleMember.ID})
|
|
require.NoError(t, err)
|
|
|
|
inv, root := clitest.New(t, "sharing", "share", workspace.Name,
|
|
fmt.Sprintf("--group=%s,%s", wibbleGroup.Name, wobbleGroup.Name))
|
|
clitest.SetupConfig(t, workspaceOwnerClient, root)
|
|
|
|
out := new(bytes.Buffer)
|
|
inv.Stdout = out
|
|
err = inv.WithContext(ctx).Run()
|
|
require.NoError(t, err)
|
|
|
|
acl, err := workspaceOwnerClient.WorkspaceACL(inv.Context(), workspace.ID)
|
|
require.NoError(t, err)
|
|
assert.Len(t, acl.Groups, 2)
|
|
|
|
type workspaceGroup []codersdk.WorkspaceGroup
|
|
assert.NotEqual(t, -1, slices.IndexFunc(workspaceGroup(acl.Groups), func(g codersdk.WorkspaceGroup) bool {
|
|
return g.Group.ID == wibbleGroup.ID
|
|
}))
|
|
assert.NotEqual(t, -1, slices.IndexFunc(workspaceGroup(acl.Groups), func(g codersdk.WorkspaceGroup) bool {
|
|
return g.Group.ID == wobbleGroup.ID
|
|
}))
|
|
|
|
t.Run("ShareWithGroups_Role", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var (
|
|
client, db, orgOwner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
|
|
LicenseOptions: &coderdenttest.LicenseOptions{
|
|
Features: license.Features{
|
|
codersdk.FeatureTemplateRBAC: 1,
|
|
},
|
|
},
|
|
})
|
|
workspaceOwnerClient, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID))
|
|
workspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
|
|
OwnerID: workspaceOwner.ID,
|
|
OrganizationID: orgOwner.OrganizationID,
|
|
}).Do().Workspace
|
|
_, orgMember = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID)
|
|
)
|
|
|
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
|
|
|
group, err := createGroupWithMembers(ctx, client, orgOwner.OrganizationID, "new-group", []uuid.UUID{orgMember.ID})
|
|
require.NoError(t, err)
|
|
|
|
inv, root := clitest.New(t, "sharing", "share", workspace.Name, "--group", fmt.Sprintf("%s:admin", group.Name))
|
|
clitest.SetupConfig(t, workspaceOwnerClient, root)
|
|
|
|
out := new(bytes.Buffer)
|
|
inv.Stdout = out
|
|
err = inv.WithContext(ctx).Run()
|
|
require.NoError(t, err)
|
|
|
|
acl, err := workspaceOwnerClient.WorkspaceACL(inv.Context(), workspace.ID)
|
|
require.NoError(t, err)
|
|
assert.Len(t, acl.Groups, 1)
|
|
assert.Equal(t, acl.Groups[0].Group.ID, group.ID)
|
|
assert.Equal(t, acl.Groups[0].Role, codersdk.WorkspaceRoleAdmin)
|
|
|
|
found := false
|
|
for _, line := range strings.Split(out.String(), "\n") {
|
|
found = strings.Contains(line, group.Name) && strings.Contains(line, string(codersdk.WorkspaceRoleAdmin))
|
|
if found {
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, found, "Expected to find group name %s and role %s in output: %s", group.Name, codersdk.WorkspaceRoleAdmin, out.String())
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestSharingStatus(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("ListSharedUsers", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var (
|
|
client, db, orgOwner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
|
|
LicenseOptions: &coderdenttest.LicenseOptions{
|
|
Features: license.Features{
|
|
codersdk.FeatureTemplateRBAC: 1,
|
|
},
|
|
},
|
|
})
|
|
workspaceOwnerClient, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID))
|
|
workspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
|
|
OwnerID: workspaceOwner.ID,
|
|
OrganizationID: orgOwner.OrganizationID,
|
|
}).Do().Workspace
|
|
_, orgMember = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID)
|
|
ctx = testutil.Context(t, testutil.WaitMedium)
|
|
)
|
|
|
|
group, err := createGroupWithMembers(ctx, client, orgOwner.OrganizationID, "new-group", []uuid.UUID{orgMember.ID})
|
|
require.NoError(t, err)
|
|
|
|
err = client.UpdateWorkspaceACL(ctx, workspace.ID, codersdk.UpdateWorkspaceACL{
|
|
GroupRoles: map[string]codersdk.WorkspaceRole{
|
|
group.ID.String(): codersdk.WorkspaceRoleUse,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
inv, root := clitest.New(t, "sharing", "status", workspace.Name)
|
|
clitest.SetupConfig(t, workspaceOwnerClient, root)
|
|
|
|
out := new(bytes.Buffer)
|
|
inv.Stdout = out
|
|
err = inv.WithContext(ctx).Run()
|
|
require.NoError(t, err)
|
|
|
|
found := false
|
|
for _, line := range strings.Split(out.String(), "\n") {
|
|
if strings.Contains(line, orgMember.Username) && strings.Contains(line, string(codersdk.WorkspaceRoleUse)) && strings.Contains(line, group.Name) {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, found, "expected to find username %s with role %s in the output: %s", orgMember.Username, codersdk.WorkspaceRoleUse, out.String())
|
|
})
|
|
}
|
|
|
|
func TestSharingRemove(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("RemoveSharedGroup_Single", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var (
|
|
client, db, orgOwner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
|
|
LicenseOptions: &coderdenttest.LicenseOptions{
|
|
Features: license.Features{
|
|
codersdk.FeatureTemplateRBAC: 1,
|
|
},
|
|
},
|
|
})
|
|
workspaceOwnerClient, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID))
|
|
workspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
|
|
OwnerID: workspaceOwner.ID,
|
|
OrganizationID: orgOwner.OrganizationID,
|
|
}).Do().Workspace
|
|
_, groupUser1 = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID)
|
|
_, groupUser2 = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID)
|
|
)
|
|
|
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
|
|
|
group1, err := createGroupWithMembers(ctx, client, orgOwner.OrganizationID, "group-1", []uuid.UUID{groupUser1.ID, groupUser2.ID})
|
|
require.NoError(t, err)
|
|
|
|
group2, err := createGroupWithMembers(ctx, client, orgOwner.OrganizationID, "group-2", []uuid.UUID{groupUser1.ID, groupUser2.ID})
|
|
require.NoError(t, err)
|
|
|
|
// Share the workspace with a user to later remove
|
|
err = client.UpdateWorkspaceACL(ctx, workspace.ID, codersdk.UpdateWorkspaceACL{
|
|
GroupRoles: map[string]codersdk.WorkspaceRole{
|
|
group1.ID.String(): codersdk.WorkspaceRoleUse,
|
|
group2.ID.String(): codersdk.WorkspaceRoleUse,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
inv, root := clitest.New(t,
|
|
"sharing",
|
|
"remove",
|
|
workspace.Name,
|
|
"--group", group1.Name,
|
|
)
|
|
clitest.SetupConfig(t, workspaceOwnerClient, root)
|
|
|
|
err = inv.WithContext(ctx).Run()
|
|
require.NoError(t, err)
|
|
|
|
acl, err := workspaceOwnerClient.WorkspaceACL(inv.Context(), workspace.ID)
|
|
require.NoError(t, err)
|
|
|
|
removedGroup1 := true
|
|
removedGroup2 := true
|
|
for _, group := range acl.Groups {
|
|
if group.ID == group1.ID {
|
|
removedGroup1 = false
|
|
continue
|
|
}
|
|
|
|
if group.ID == group2.ID {
|
|
removedGroup2 = false
|
|
continue
|
|
}
|
|
}
|
|
assert.True(t, removedGroup1)
|
|
assert.False(t, removedGroup2)
|
|
})
|
|
|
|
t.Run("RemoveSharedGroup_Multiple", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var (
|
|
client, db, orgOwner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
|
|
LicenseOptions: &coderdenttest.LicenseOptions{
|
|
Features: license.Features{
|
|
codersdk.FeatureTemplateRBAC: 1,
|
|
},
|
|
},
|
|
})
|
|
workspaceOwnerClient, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID))
|
|
workspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
|
|
OwnerID: workspaceOwner.ID,
|
|
OrganizationID: orgOwner.OrganizationID,
|
|
}).Do().Workspace
|
|
_, groupUser1 = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID)
|
|
_, groupUser2 = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID)
|
|
)
|
|
|
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
|
|
|
group1, err := createGroupWithMembers(ctx, client, orgOwner.OrganizationID, "group-1", []uuid.UUID{groupUser1.ID, groupUser2.ID})
|
|
require.NoError(t, err)
|
|
|
|
group2, err := createGroupWithMembers(ctx, client, orgOwner.OrganizationID, "group-2", []uuid.UUID{groupUser1.ID, groupUser2.ID})
|
|
require.NoError(t, err)
|
|
|
|
// Share the workspace with a user to later remove
|
|
err = client.UpdateWorkspaceACL(ctx, workspace.ID, codersdk.UpdateWorkspaceACL{
|
|
GroupRoles: map[string]codersdk.WorkspaceRole{
|
|
group1.ID.String(): codersdk.WorkspaceRoleUse,
|
|
group2.ID.String(): codersdk.WorkspaceRoleUse,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
inv, root := clitest.New(t,
|
|
"sharing",
|
|
"remove",
|
|
workspace.Name,
|
|
fmt.Sprintf("--group=%s,%s", group1.Name, group2.Name),
|
|
)
|
|
clitest.SetupConfig(t, workspaceOwnerClient, root)
|
|
|
|
err = inv.WithContext(ctx).Run()
|
|
require.NoError(t, err)
|
|
|
|
acl, err := workspaceOwnerClient.WorkspaceACL(inv.Context(), workspace.ID)
|
|
require.NoError(t, err)
|
|
|
|
removedGroup1 := true
|
|
removedGroup2 := true
|
|
for _, group := range acl.Groups {
|
|
if group.ID == group1.ID {
|
|
removedGroup1 = false
|
|
continue
|
|
}
|
|
|
|
if group.ID == group2.ID {
|
|
removedGroup2 = false
|
|
continue
|
|
}
|
|
}
|
|
assert.True(t, removedGroup1)
|
|
assert.True(t, removedGroup2)
|
|
})
|
|
}
|
|
|
|
func createGroupWithMembers(ctx context.Context, client *codersdk.Client, orgID uuid.UUID, name string, memberIDs []uuid.UUID) (codersdk.Group, error) {
|
|
group, err := client.CreateGroup(ctx, orgID, codersdk.CreateGroupRequest{
|
|
Name: name,
|
|
DisplayName: name,
|
|
})
|
|
if err != nil {
|
|
return codersdk.Group{}, err
|
|
}
|
|
|
|
ids := make([]string, len(memberIDs))
|
|
for i, id := range memberIDs {
|
|
ids[i] = id.String()
|
|
}
|
|
|
|
return client.PatchGroup(ctx, group.ID, codersdk.PatchGroupRequest{
|
|
AddUsers: ids,
|
|
})
|
|
}
|