Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit79106bc

Browse files
feat: add organization scope to shared ports
Adds 'organization' level to shared ports that allows sharing with allauthenticated users in the template's organization.- Add organization level to AppSharingLevel enum and constants- Update database schema with migration to add organization value- Implement organization-level access control logic in workspaceapps- Add validation functions for organization level- Add comprehensive tests for organization level port sharing- Update enterprise port sharing authorization logicCloses#17247Co-authored-by: kylecarbs <7122116+kylecarbs@users.noreply.github.com>
1 parent8661d1a commit79106bc

16 files changed

+920
-1699
lines changed

‎coderd/database/dump.sql

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-- Remove 'organization' from the app_sharing_level enum
2+
-- Note: PostgreSQL doesn't support removing enum values directly.
3+
-- This would require recreating the enum type and updating all references.
4+
-- For safety, we'll leave this as a no-op since removing enum values
5+
-- can be dangerous in production.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- Add 'organization' to the app_sharing_level enum
2+
ALTERTYPE app_sharing_level ADD VALUE'organization';

‎coderd/database/models.go

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/querier.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/queries.sql.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/workspaceagentportshare_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,26 @@ func TestPostWorkspaceAgentPortShare(t *testing.T) {
116116
require.EqualValues(t,codersdk.WorkspaceAgentPortShareLevelAuthenticated,list.Shares[0].ShareLevel)
117117
require.EqualValues(t,codersdk.WorkspaceAgentPortShareProtocolHTTP,list.Shares[0].Protocol)
118118

119+
// update to organization level
120+
ps,err=client.UpsertWorkspaceAgentPortShare(ctx,r.Workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
121+
AgentName:agents[0].Name,
122+
Port:8080,
123+
ShareLevel:codersdk.WorkspaceAgentPortShareLevelOrganization,
124+
Protocol:codersdk.WorkspaceAgentPortShareProtocolHTTP,
125+
})
126+
require.NoError(t,err)
127+
require.EqualValues(t,codersdk.WorkspaceAgentPortShareLevelOrganization,ps.ShareLevel)
128+
require.EqualValues(t,codersdk.WorkspaceAgentPortShareProtocolHTTP,ps.Protocol)
129+
130+
// list
131+
list,err=client.GetWorkspaceAgentPortShares(ctx,r.Workspace.ID)
132+
require.NoError(t,err)
133+
require.Len(t,list.Shares,1)
134+
require.EqualValues(t,agents[0].Name,list.Shares[0].AgentName)
135+
require.EqualValues(t,8080,list.Shares[0].Port)
136+
require.EqualValues(t,codersdk.WorkspaceAgentPortShareLevelOrganization,list.Shares[0].ShareLevel)
137+
require.EqualValues(t,codersdk.WorkspaceAgentPortShareProtocolHTTP,list.Shares[0].Protocol)
138+
119139
// list 2 ordered by port
120140
ps,err=client.UpsertWorkspaceAgentPortShare(ctx,r.Workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
121141
AgentName:agents[0].Name,

‎coderd/workspaceapps/apptest/apptest.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1403,6 +1403,7 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
14031403
proxyTestAppNameFake:codersdk.WorkspaceAppSharingLevelOwner,
14041404
proxyTestAppNameOwner:codersdk.WorkspaceAppSharingLevelOwner,
14051405
proxyTestAppNameAuthenticated:codersdk.WorkspaceAppSharingLevelAuthenticated,
1406+
proxyTestAppNameOrganization:codersdk.WorkspaceAppSharingLevelOrganization,
14061407
proxyTestAppNamePublic:codersdk.WorkspaceAppSharingLevelPublic,
14071408
}
14081409
for_,app:=rangeagnt.Apps {
@@ -1562,6 +1563,23 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
15621563
verifyAccess(t,appDetails,isPathApp,user.Username,workspace.Name,agnt.Name,proxyTestAppNameAuthenticated,clientWithNoAuth,false,true)
15631564
})
15641565

1566+
t.Run("LevelOrganization",func(t*testing.T) {
1567+
t.Parallel()
1568+
1569+
// Site owner should be able to access all workspaces if
1570+
// enabled.
1571+
verifyAccess(t,appDetails,isPathApp,user.Username,workspace.Name,agnt.Name,proxyTestAppNameOrganization,ownerClient,siteOwnerCanAccessShared,false)
1572+
1573+
// Owner should be able to access their own workspace.
1574+
verifyAccess(t,appDetails,isPathApp,user.Username,workspace.Name,agnt.Name,proxyTestAppNameOrganization,client,true,false)
1575+
1576+
// Users in different organizations should NOT be able to access the workspace.
1577+
verifyAccess(t,appDetails,isPathApp,user.Username,workspace.Name,agnt.Name,proxyTestAppNameOrganization,clientInOtherOrg,false,false)
1578+
1579+
// Unauthenticated user should not have any access.
1580+
verifyAccess(t,appDetails,isPathApp,user.Username,workspace.Name,agnt.Name,proxyTestAppNameOrganization,clientWithNoAuth,false,true)
1581+
})
1582+
15651583
t.Run("LevelPublic",func(t*testing.T) {
15661584
t.Parallel()
15671585

‎coderd/workspaceapps/apptest/setup.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const (
3535
proxyTestAppNameFake="test-app-fake"
3636
proxyTestAppNameOwner="test-app-owner"
3737
proxyTestAppNameAuthenticated="test-app-authenticated"
38+
proxyTestAppNameOrganization="test-app-organization"
3839
proxyTestAppNamePublic="test-app-public"
3940
proxyTestAppQuery="query=true"
4041
proxyTestAppBody="hello world from apps test"
@@ -354,6 +355,13 @@ func createWorkspaceWithApps(t *testing.T, client *codersdk.Client, orgID uuid.U
354355
Url:appURL,
355356
Subdomain:true,
356357
},
358+
{
359+
Slug:proxyTestAppNameOrganization,
360+
DisplayName:proxyTestAppNameOrganization,
361+
SharingLevel:proto.AppSharingLevel_ORGANIZATION,
362+
Url:appURL,
363+
Subdomain:true,
364+
},
357365
{
358366
Slug:proxyTestAppNamePublic,
359367
DisplayName:proxyTestAppNamePublic,

‎coderd/workspaceapps/db.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,32 @@ func (p *DBTokenProvider) authorizeRequest(ctx context.Context, roles *rbac.Subj
354354
iferr==nil {
355355
returntrue, []string{},nil
356356
}
357+
casedatabase.AppSharingLevelOrganization:
358+
// Check if the user is in the same organization as the workspace.
359+
// This allows all authenticated users in the template's organization to access the app.
360+
userID,err:=uuid.Parse(roles.ID)
361+
iferr!=nil {
362+
// Invalid user ID, deny access
363+
break
364+
}
365+
userOrgs,err:=p.Database.GetOrganizationsByUserID(ctx, database.GetOrganizationsByUserIDParams{
366+
UserID:userID,
367+
Deleted: sql.NullBool{Valid:true,Bool:false},
368+
})
369+
iferr!=nil {
370+
// Error getting user organizations, deny access
371+
break
372+
}
373+
for_,org:=rangeuserOrgs {
374+
iforg.ID==dbReq.Workspace.OrganizationID {
375+
// User is in the same organization, check scopes
376+
err:=p.Authorizer.Authorize(ctx,*roles,rbacAction,rbacResourceOwned)
377+
iferr==nil {
378+
returntrue, []string{},nil
379+
}
380+
break
381+
}
382+
}
357383
casedatabase.AppSharingLevelPublic:
358384
// We don't really care about scopes and stuff if it's public anyways.
359385
// Someone with a restricted-scope API key could just not submit the API

‎codersdk/workspaceagentportshare.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
const (
1313
WorkspaceAgentPortShareLevelOwnerWorkspaceAgentPortShareLevel="owner"
1414
WorkspaceAgentPortShareLevelAuthenticatedWorkspaceAgentPortShareLevel="authenticated"
15+
WorkspaceAgentPortShareLevelOrganizationWorkspaceAgentPortShareLevel="organization"
1516
WorkspaceAgentPortShareLevelPublicWorkspaceAgentPortShareLevel="public"
1617

1718
WorkspaceAgentPortShareProtocolHTTPWorkspaceAgentPortShareProtocol="http"
@@ -24,7 +25,7 @@ type (
2425
UpsertWorkspaceAgentPortShareRequeststruct {
2526
AgentNamestring`json:"agent_name"`
2627
Portint32`json:"port"`
27-
ShareLevelWorkspaceAgentPortShareLevel`json:"share_level" enums:"owner,authenticated,public"`
28+
ShareLevelWorkspaceAgentPortShareLevel`json:"share_level" enums:"owner,authenticated,organization,public"`
2829
ProtocolWorkspaceAgentPortShareProtocol`json:"protocol" enums:"http,https"`
2930
}
3031
WorkspaceAgentPortSharesstruct {
@@ -34,7 +35,7 @@ type (
3435
WorkspaceID uuid.UUID`json:"workspace_id" format:"uuid"`
3536
AgentNamestring`json:"agent_name"`
3637
Portint32`json:"port"`
37-
ShareLevelWorkspaceAgentPortShareLevel`json:"share_level" enums:"owner,authenticated,public"`
38+
ShareLevelWorkspaceAgentPortShareLevel`json:"share_level" enums:"owner,authenticated,organization,public"`
3839
ProtocolWorkspaceAgentPortShareProtocol`json:"protocol" enums:"http,https"`
3940
}
4041
DeleteWorkspaceAgentPortShareRequeststruct {
@@ -46,11 +47,13 @@ type (
4647
func (lWorkspaceAgentPortShareLevel)ValidMaxLevel()bool {
4748
returnl==WorkspaceAgentPortShareLevelOwner||
4849
l==WorkspaceAgentPortShareLevelAuthenticated||
50+
l==WorkspaceAgentPortShareLevelOrganization||
4951
l==WorkspaceAgentPortShareLevelPublic
5052
}
5153

5254
func (lWorkspaceAgentPortShareLevel)ValidPortShareLevel()bool {
5355
returnl==WorkspaceAgentPortShareLevelAuthenticated||
56+
l==WorkspaceAgentPortShareLevelOrganization||
5457
l==WorkspaceAgentPortShareLevelPublic
5558
}
5659

‎codersdk/workspaceapps.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@ type WorkspaceAppSharingLevel string
3535
const (
3636
WorkspaceAppSharingLevelOwnerWorkspaceAppSharingLevel="owner"
3737
WorkspaceAppSharingLevelAuthenticatedWorkspaceAppSharingLevel="authenticated"
38+
WorkspaceAppSharingLevelOrganizationWorkspaceAppSharingLevel="organization"
3839
WorkspaceAppSharingLevelPublicWorkspaceAppSharingLevel="public"
3940
)
4041

4142
varMapWorkspaceAppSharingLevels=map[WorkspaceAppSharingLevel]struct{}{
4243
WorkspaceAppSharingLevelOwner: {},
4344
WorkspaceAppSharingLevelAuthenticated: {},
45+
WorkspaceAppSharingLevelOrganization: {},
4446
WorkspaceAppSharingLevelPublic: {},
4547
}
4648

@@ -79,7 +81,7 @@ type WorkspaceApp struct {
7981
Subdomainbool`json:"subdomain"`
8082
// SubdomainName is the application domain exposed on the `coder server`.
8183
SubdomainNamestring`json:"subdomain_name,omitempty"`
82-
SharingLevelWorkspaceAppSharingLevel`json:"sharing_level" enums:"owner,authenticated,public"`
84+
SharingLevelWorkspaceAppSharingLevel`json:"sharing_level" enums:"owner,authenticated,organization,public"`
8385
// Healthcheck specifies the configuration for checking app health.
8486
HealthcheckHealthcheck`json:"healthcheck,omitempty"`
8587
HealthWorkspaceAppHealth`json:"health"`

‎enterprise/coderd/portsharing/portsharing.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ func (EnterprisePortSharer) AuthorizedLevel(template database.Template, level co
2020
ifmaxLevel!=codersdk.WorkspaceAgentPortShareLevelPublic {
2121
returnxerrors.Errorf("port sharing level not allowed. Max level is '%s'",maxLevel)
2222
}
23+
casecodersdk.WorkspaceAgentPortShareLevelOrganization:
24+
ifmaxLevel==codersdk.WorkspaceAgentPortShareLevelOwner||maxLevel==codersdk.WorkspaceAgentPortShareLevelAuthenticated {
25+
returnxerrors.Errorf("port sharing level not allowed. Max level is '%s'",maxLevel)
26+
}
2327
casecodersdk.WorkspaceAgentPortShareLevelAuthenticated:
2428
ifmaxLevel==codersdk.WorkspaceAgentPortShareLevelOwner {
2529
returnxerrors.Errorf("port sharing level not allowed. Max level is '%s'",maxLevel)

‎enterprise/coderd/workspaceportshare_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,27 @@ func TestWorkspacePortShare(t *testing.T) {
5959
})
6060
require.NoError(t,err)
6161
require.EqualValues(t,codersdk.WorkspaceAgentPortShareLevelPublic,ps.ShareLevel)
62+
63+
// Test organization level - should work when max level is public
64+
ps,err=client.UpsertWorkspaceAgentPortShare(ctx,r.workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
65+
AgentName:r.sdkAgent.Name,
66+
Port:8081,
67+
ShareLevel:codersdk.WorkspaceAgentPortShareLevelOrganization,
68+
Protocol:codersdk.WorkspaceAgentPortShareProtocolHTTP,
69+
})
70+
require.NoError(t,err)
71+
require.EqualValues(t,codersdk.WorkspaceAgentPortShareLevelOrganization,ps.ShareLevel)
72+
73+
// Test organization level should fail when max level is authenticated
74+
varauthLevel codersdk.WorkspaceAgentPortShareLevel=codersdk.WorkspaceAgentPortShareLevelAuthenticated
75+
client.UpdateTemplateMeta(ctx,r.workspace.TemplateID, codersdk.UpdateTemplateMeta{
76+
MaxPortShareLevel:&authLevel,
77+
})
78+
_,err=client.UpsertWorkspaceAgentPortShare(ctx,r.workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
79+
AgentName:r.sdkAgent.Name,
80+
Port:8082,
81+
ShareLevel:codersdk.WorkspaceAgentPortShareLevelOrganization,
82+
Protocol:codersdk.WorkspaceAgentPortShareProtocolHTTP,
83+
})
84+
require.Error(t,err,"Organization level should not be allowed when max level is authenticated")
6285
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp