8000 feat: add organization scope for shared ports by aslilac · Pull Request #18314 · coder/coder · GitHub
[go: up one dir, main page]

Skip to content

feat: add organization scope for shared ports #18314

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Jun 16, 2025
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
a test that actually makes any sense
  • Loading branch information
aslilac committed Jun 13, 2025
commit c10de9e102b2609eb050d61e34249c92b1c05968
150 changes: 45 additions & 105 deletions enterprise/coderd/workspaceportshare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,80 +6,49 @@ import (

"github.com/stretchr/testify/require"

"github.com/google/uuid"

"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/util/ptr"
"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/provisioner/echo"
"github.com/coder/coder/v2/provisionersdk/proto"
"github.com/coder/coder/v2/testutil"
)

func TestWorkspacePortShare(t *testing.T) {
func TestWorkspacePortSharePublic(t *testing.T) {
t.Parallel()

ownerClient, owner := coderdenttest.New(t, &coderdenttest.Options{
Options: &coderdtest.Options{
IncludeProvisionerDaemon: true,
},
Options: &coderdtest.Options{IncludeProvisionerDaemon: true},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureControlSharedPorts: 1,
},
Features: license.Features{codersdk.FeatureControlSharedPorts: 1},
},
})
client, _ := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID, rbac.RoleTemplateAdmin())
client, user := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID, rbac.RoleTemplateAdmin())
r := setupWorkspaceAgent(t, client, codersdk.CreateFirstUserResponse{
UserID: user.ID,
OrganizationID: owner.OrganizationID,
}, 0)
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
defer cancel()

// Create a template and workspace
authToken := uuid.NewString()
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, &echo.Responses{
Parse: echo.ParseComplete,
ProvisionApply: []*proto.Response{{
Type: &proto.Response_Apply{
Apply: &proto.ApplyComplete{
Resources: []*proto.Resource{{
Name: "example",
Type: "aws_instance",
Agents: []*proto.Agent{{
Id: uuid.NewString(),
Name: "test-agent",
Auth: &proto.Agent_Token{
Token: authToken,
},
}},
}},
},
},
}},
})
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, template.ID)
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)

// try to update port share with template max port share level owner
_, err := client.UpsertWorkspaceAgentPortShare(ctx, workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
AgentName: "test-agent",
// Try to update port share with template max port share level owner.
_, err := client.UpsertWorkspaceAgentPortShare(ctx, r.workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
AgentName: r.sdkAgent.Name,
Port: 8080,
ShareLevel: codersdk.WorkspaceAgentPortShareLevelPublic,
Protocol: codersdk.WorkspaceAgentPortShareProtocolHTTP,
})
require.Error(t, err, "Port sharing level not allowed")

// update the template max port share level to public
var level codersdk.WorkspaceAgentPortShareLevel = codersdk.WorkspaceAgentPortShareLevelPublic
client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{
MaxPortShareLevel: &level,
// Update the template max port share level to public
client.UpdateTemplateMeta(ctx, r.workspace.TemplateID, codersdk.UpdateTemplateMeta{
MaxPortShareLevel: ptr.Ref(codersdk.WorkspaceAgentPortShareLevelPublic),
})

// OK
ps, err := client.UpsertWorkspaceAgentPortShare(ctx, workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
AgentName: "test-agent",
ps, err := client.UpsertWorkspaceAgentPortShare(ctx, r.workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
AgentName: r.sdkAgent.Name,
Port: 8080,
ShareLevel: codersdk.WorkspaceAgentPortShareLevelPublic,
Protocol: codersdk.WorkspaceAgentPortShareProtocolHTTP,
Expand All @@ -92,78 +61,49 @@ func TestWorkspacePortShareOrganization(t *testing.T) {
t.Parallel()

ownerClient, owner := coderdenttest.New(t, &coderdenttest.Options{
Options: &coderdtest.Options{
IncludeProvisionerDaemon: true,
},
Options: &coderdtest.Options{IncludeProvisionerDaemon: true},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureControlSharedPorts: 1,
codersdk.FeatureMultipleOrganizations: 1,
},
Features: license.Features{codersdk.FeatureControlSharedPorts: 1},
},
})

// Create a user in the same organization
sameOrgClient, _ := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID)
client, user := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID, rbac.RoleTemplateAdmin())
r := setupWorkspaceAgent(t, client, codersdk.CreateFirstUserResponse{
UserID: user.ID,
OrganizationID: owner.OrganizationID,
}, 0)
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
defer cancel()

// Create another organization and a user in it
otherOrg := coderdenttest.CreateOrganization(t, ownerClient, coderdenttest.CreateOrganizationOptions{})
_, _ = coderdtest.CreateAnotherUser(t, ownerClient, otherOrg.ID)
// Try to update port share with template max port share level owner
_, err := client.UpsertWorkspaceAgentPortShare(ctx, r.workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
AgentName: r.sdkAgent.Name,
Port: 8080,
ShareLevel: codersdk.WorkspaceAgentPortShareLevelOrganization,
Protocol: codersdk.WorkspaceAgentPortShareProtocolHTTP,
})
require.Error(t, err, "Port sharing level not allowed")

// Create workspace in the first organization
authToken := uuid.NewString()
version := coderdtest.CreateTemplateVersion(t, sameOrgClient, owner.OrganizationID, &echo.Responses{
Parse: echo.ParseComplete,
ProvisionApply: []*proto.Response{{
Type: &proto.Response_Apply{
Apply: &proto.ApplyComplete{
Resources: []*proto.Resource{{
Name: "example",
Type: "aws_instance",
Agents: []*proto.Agent{{
Id: uuid.NewString(),
Name: "test-agent",
Auth: &proto.Agent_Token{
Token: authToken,
},
}},
}},
},
},
}},
// Update the template max port share level to organization
client.UpdateTemplateMeta(ctx, r.workspace.TemplateID, codersdk.UpdateTemplateMeta{
MaxPortShareLevel: ptr.Ref(codersdk.WorkspaceAgentPortShareLevelOrganization),
})
template := coderdtest.CreateTemplate(t, sameOrgClient, owner.OrganizationID, version.ID)
coderdtest.AwaitTemplateVersionJobCompleted(t, sameOrgClient, version.ID)
workspace := coderdtest.CreateWorkspace(t, sameOrgClient, template.ID)
coderdtest.AwaitWorkspaceBuildJobCompleted(t, sameOrgClient, workspace.LatestBuild.ID)

// Update the template max port share level to public to allow all sharing levels
var level codersdk.WorkspaceAgentPortShareLevel = codersdk.WorkspaceAgentPortShareLevelPublic
_, err := sameOrgClient.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{
MaxPortShareLevel: &level,
// Try to share a port publicly with template max port share level organization
_, err = client.UpsertWorkspaceAgentPortShare(ctx, r.workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
AgentName: r.sdkAgent.Name,
Port: 8080,
ShareLevel: codersdk.WorkspaceAgentPortShareLevelPublic,
Protocol: codersdk.WorkspaceAgentPortShareProtocolHTTP,
})
require.NoError(t, err)
require.Error(t, err, "Port sharing level not allowed")

// Share port at organization level
ps, err := sameOrgClient.UpsertWorkspaceAgentPortShare(ctx, workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
AgentName: "test-agent",
// OK
ps, err := client.UpsertWorkspaceAgentPortShare(ctx, r.workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
AgentName: r.sdkAgent.Name,
Port: 8080,
ShareLevel: codersdk.WorkspaceAgentPortShareLevelOrganization,
Protocol: codersdk.WorkspaceAgentPortShareProtocolHTTP,
})
require.NoError(t, err)
require.EqualValues(t, codersdk.WorkspaceAgentPortShareLevelOrganization, ps.ShareLevel)

// Verify user in same organization can see the shared port
shares, err := sameOrgClient.GetWorkspaceAgentPortShares(ctx, workspace.ID)
require.NoError(t, err)
require.Len(t, shares.Shares, 1)
require.EqualValues(t, codersdk.WorkspaceAgentPortShareLevelOrganization, shares.Shares[0].ShareLevel)

// TODO: Once the authorization middleware is fully integrated, add tests to verify:
// - Users in the same organization can access the port
// - Users in different organizations cannot access the port
// - Unauthenticated users cannot access the port
}
0