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

Commit8d5c566

Browse files
authored
feat: addsharing remove command to the CLI (#19767)
Closes[coder/internal#861](coder/internal#861)
1 parentd5a02d5 commit8d5c566

File tree

4 files changed

+509
-106
lines changed

4 files changed

+509
-106
lines changed

‎cli/sharing.go‎

Lines changed: 220 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77

88
"golang.org/x/xerrors"
99

10+
"github.com/google/uuid"
11+
1012
"github.com/coder/coder/v2/cli/cliui"
1113
"github.com/coder/coder/v2/codersdk"
1214
"github.com/coder/serpent"
@@ -15,8 +17,6 @@ import (
1517
constdefaultGroupDisplay="-"
1618

1719
func (r*RootCmd)sharing()*serpent.Command {
18-
orgContext:=NewOrganizationContext()
19-
2020
cmd:=&serpent.Command{
2121
Use:"sharing [subcommand]",
2222
Short:"Commands for managing shared workspaces",
@@ -25,13 +25,13 @@ func (r *RootCmd) sharing() *serpent.Command {
2525
returninv.Command.HelpHandler(inv)
2626
},
2727
Children: []*serpent.Command{
28-
r.shareWorkspace(orgContext),
28+
r.shareWorkspace(),
29+
r.unshareWorkspace(),
2930
r.statusWorkspaceSharing(),
3031
},
3132
Hidden:true,
3233
}
3334

34-
orgContext.AttachOptions(cmd)
3535
returncmd
3636
}
3737

@@ -70,13 +70,14 @@ func (r *RootCmd) statusWorkspaceSharing() *serpent.Command {
7070
returncmd
7171
}
7272

73-
func (r*RootCmd)shareWorkspace(orgContext*OrganizationContext)*serpent.Command {
73+
func (r*RootCmd)shareWorkspace()*serpent.Command {
7474
var (
75+
client=new(codersdk.Client)
76+
users []string
77+
groups []string
78+
7579
// Username regex taken from codersdk/name.go
7680
nameRoleRegex=regexp.MustCompile(`(^[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*)+(?::([A-Za-z0-9-]+))?`)
77-
client=new(codersdk.Client)
78-
users []string
79-
groups []string
8081
)
8182

8283
cmd:=&serpent.Command{
@@ -110,89 +111,130 @@ func (r *RootCmd) shareWorkspace(orgContext *OrganizationContext) *serpent.Comma
110111
returnxerrors.Errorf("could not fetch the workspace %s: %w",inv.Args[0],err)
111112
}
112113

113-
org,err:=orgContext.Selected(inv,client)
114+
userRoleStrings:=make([][2]string,len(users))
115+
forindex,user:=rangeusers {
116+
userAndRole:=nameRoleRegex.FindStringSubmatch(user)
117+
ifuserAndRole==nil {
118+
returnxerrors.Errorf("invalid user format %q: must match pattern 'username:role'",user)
119+
}
120+
121+
userRoleStrings[index]= [2]string{userAndRole[1],userAndRole[2]}
122+
}
123+
124+
groupRoleStrings:=make([][2]string,len(groups))
125+
forindex,group:=rangegroups {
126+
groupAndRole:=nameRoleRegex.FindStringSubmatch(group)
127+
ifgroupAndRole==nil {
128+
returnxerrors.Errorf("invalid group format %q: must match pattern 'group:role'",group)
129+
}
130+
131+
groupRoleStrings[index]= [2]string{groupAndRole[1],groupAndRole[2]}
132+
}
133+
134+
userRoles,groupRoles,err:=fetchUsersAndGroups(inv.Context(),fetchUsersAndGroupsParams{
135+
Client:client,
136+
OrgID:workspace.OrganizationID,
137+
OrgName:workspace.OrganizationName,
138+
Users:userRoleStrings,
139+
Groups:groupRoleStrings,
140+
DefaultRole:codersdk.WorkspaceRoleUse,
141+
})
114142
iferr!=nil {
115143
returnerr
116144
}
117145

118-
userRoles:=make(map[string]codersdk.WorkspaceRole,len(users))
119-
iflen(users)>0 {
120-
orgMembers,err:=client.OrganizationMembers(inv.Context(),org.ID)
121-
iferr!=nil {
122-
returnerr
123-
}
146+
err=client.UpdateWorkspaceACL(inv.Context(),workspace.ID, codersdk.UpdateWorkspaceACL{
147+
UserRoles:userRoles,
148+
GroupRoles:groupRoles,
149+
})
150+
iferr!=nil {
151+
returnerr
152+
}
124153

125-
for_,user:=rangeusers {
126-
userAndRole:=nameRoleRegex.FindStringSubmatch(user)
127-
ifuserAndRole==nil {
128-
returnxerrors.Errorf("invalid user format %q: must match pattern 'username:role'",user)
129-
}
130-
131-
username:=userAndRole[1]
132-
role:=userAndRole[2]
133-
ifrole=="" {
134-
role=string(codersdk.WorkspaceRoleUse)
135-
}
136-
137-
userID:=""
138-
for_,member:=rangeorgMembers {
139-
ifmember.Username==username {
140-
userID=member.UserID.String()
141-
break
142-
}
143-
}
144-
ifuserID=="" {
145-
returnxerrors.Errorf("could not find user %s in the organization %s",username,org.Name)
146-
}
147-
148-
workspaceRole,err:=stringToWorkspaceRole(role)
149-
iferr!=nil {
150-
returnerr
151-
}
152-
153-
userRoles[userID]=workspaceRole
154-
}
154+
acl,err:=client.WorkspaceACL(inv.Context(),workspace.ID)
155+
iferr!=nil {
156+
returnxerrors.Errorf("could not fetch current workspace ACL after sharing %w",err)
157+
}
158+
159+
out,err:=workspaceACLToTable(inv.Context(),&acl)
160+
iferr!=nil {
161+
returnerr
155162
}
156163

157-
groupRoles:=make(map[string]codersdk.WorkspaceRole)
158-
iflen(groups)>0 {
159-
orgGroups,err:=client.Groups(inv.Context(), codersdk.GroupArguments{
160-
Organization:org.ID.String(),
161-
})
162-
iferr!=nil {
163-
returnerr
164+
_,err=fmt.Fprintln(inv.Stdout,out)
165+
returnerr
166+
},
167+
}
168+
169+
returncmd
170+
}
171+
172+
func (r*RootCmd)unshareWorkspace()*serpent.Command {
173+
var (
174+
client=new(codersdk.Client)
175+
users []string
176+
groups []string
177+
)
178+
179+
cmd:=&serpent.Command{
180+
Use:"remove <workspace> --user <user> --group <group>",
181+
Aliases: []string{"unshare"},
182+
Short:"Remove shared access for users or groups from a workspace.",
183+
Options: serpent.OptionSet{
184+
{
185+
Name:"user",
186+
Description:"A comma separated list of users to share the workspace with.",
187+
Flag:"user",
188+
Value:serpent.StringArrayOf(&users),
189+
}, {
190+
Name:"group",
191+
Description:"A comma separated list of groups to share the workspace with.",
192+
Flag:"group",
193+
Value:serpent.StringArrayOf(&groups),
194+
},
195+
},
196+
Middleware:serpent.Chain(
197+
r.InitClient(client),
198+
serpent.RequireNArgs(1),
199+
),
200+
Handler:func(inv*serpent.Invocation)error {
201+
iflen(users)==0&&len(groups)==0 {
202+
returnxerrors.New("at least one user or group must be provided")
203+
}
204+
205+
workspace,err:=namedWorkspace(inv.Context(),client,inv.Args[0])
206+
iferr!=nil {
207+
returnxerrors.Errorf("could not fetch the workspace %s: %w",inv.Args[0],err)
208+
}
209+
210+
userRoleStrings:=make([][2]string,len(users))
211+
forindex,user:=rangeusers {
212+
if!codersdk.UsernameValidRegex.MatchString(user) {
213+
returnxerrors.Errorf("invalid username")
164214
}
165215

166-
for_,group:=rangegroups {
167-
groupAndRole:=nameRoleRegex.FindStringSubmatch(group)
168-
ifgroupAndRole==nil {
169-
returnxerrors.Errorf("invalid group format %q: must match pattern 'group:role'",group)
170-
}
171-
groupName:=groupAndRole[1]
172-
role:=groupAndRole[2]
173-
ifrole=="" {
174-
role=string(codersdk.WorkspaceRoleUse)
175-
}
176-
177-
varorgGroup*codersdk.Group
178-
for_,group:=rangeorgGroups {
179-
ifgroup.Name==groupName {
180-
orgGroup=&group
181-
break
182-
}
183-
}
184-
185-
iforgGroup==nil {
186-
returnxerrors.Errorf("could not find group named %s belonging to the organization %s",groupName,org.Name)
187-
}
188-
189-
workspaceRole,err:=stringToWorkspaceRole(role)
190-
iferr!=nil {
191-
returnerr
192-
}
193-
194-
groupRoles[orgGroup.ID.String()]=workspaceRole
216+
userRoleStrings[index]= [2]string{user,""}
217+
}
218+
219+
groupRoleStrings:=make([][2]string,len(groups))
220+
forindex,group:=rangegroups {
221+
if!codersdk.UsernameValidRegex.MatchString(group) {
222+
returnxerrors.Errorf("invalid group name")
195223
}
224+
225+
groupRoleStrings[index]= [2]string{group,""}
226+
}
227+
228+
userRoles,groupRoles,err:=fetchUsersAndGroups(inv.Context(),fetchUsersAndGroupsParams{
229+
Client:client,
230+
OrgID:workspace.OrganizationID,
231+
OrgName:workspace.OrganizationName,
232+
Users:userRoleStrings,
233+
Groups:groupRoleStrings,
234+
DefaultRole:codersdk.WorkspaceRoleDeleted,
235+
})
236+
iferr!=nil {
237+
returnerr
196238
}
197239

198240
err=client.UpdateWorkspaceACL(inv.Context(),workspace.ID, codersdk.UpdateWorkspaceACL{
@@ -227,9 +269,11 @@ func stringToWorkspaceRole(role string) (codersdk.WorkspaceRole, error) {
227269
returncodersdk.WorkspaceRoleUse,nil
228270
casestring(codersdk.WorkspaceRoleAdmin):
229271
returncodersdk.WorkspaceRoleAdmin,nil
272+
casestring(codersdk.WorkspaceRoleDeleted):
273+
returncodersdk.WorkspaceRoleDeleted,nil
230274
default:
231-
return"",xerrors.Errorf("invalid role %q: expected %qor%q",
232-
role,codersdk.WorkspaceRoleAdmin,codersdk.WorkspaceRoleUse)
275+
return"",xerrors.Errorf("invalid role %q: expected %q, %q,or\"%q\"",
276+
role,codersdk.WorkspaceRoleAdmin,codersdk.WorkspaceRoleUse,codersdk.WorkspaceRoleDeleted)
233277
}
234278
}
235279

@@ -277,3 +321,96 @@ func workspaceACLToTable(ctx context.Context, acl *codersdk.WorkspaceACL) (strin
277321

278322
returnout,nil
279323
}
324+
325+
typefetchUsersAndGroupsParamsstruct {
326+
Client*codersdk.Client
327+
OrgID uuid.UUID
328+
OrgNamestring
329+
Users [][2]string
330+
Groups [][2]string
331+
DefaultRole codersdk.WorkspaceRole
332+
}
333+
334+
funcfetchUsersAndGroups(ctx context.Context,paramsfetchUsersAndGroupsParams) (userRolesmap[string]codersdk.WorkspaceRole,groupRolesmap[string]codersdk.WorkspaceRole,errerror) {
335+
var (
336+
client=params.Client
337+
orgID=params.OrgID
338+
orgName=params.OrgName
339+
users=params.Users
340+
groups=params.Groups
341+
defaultRole=params.DefaultRole
342+
)
343+
344+
userRoles=make(map[string]codersdk.WorkspaceRole,len(users))
345+
iflen(users)>0 {
346+
orgMembers,err:=client.OrganizationMembers(ctx,orgID)
347+
iferr!=nil {
348+
returnnil,nil,err
349+
}
350+
351+
for_,user:=rangeusers {
352+
username:=user[0]
353+
role:=user[1]
354+
ifrole=="" {
355+
role=string(defaultRole)
356+
}
357+
358+
userID:=""
359+
for_,member:=rangeorgMembers {
360+
ifmember.Username==username {
361+
userID=member.UserID.String()
362+
break
363+
}
364+
}
365+
ifuserID=="" {
366+
returnnil,nil,xerrors.Errorf("could not find user %s in the organization %s",username,orgName)
367+
}
368+
369+
workspaceRole,err:=stringToWorkspaceRole(role)
370+
iferr!=nil {
371+
returnnil,nil,err
372+
}
373+
374+
userRoles[userID]=workspaceRole
375+
}
376+
}
377+
378+
groupRoles=make(map[string]codersdk.WorkspaceRole)
379+
iflen(groups)>0 {
380+
orgGroups,err:=client.Groups(ctx, codersdk.GroupArguments{
381+
Organization:orgID.String(),
382+
})
383+
iferr!=nil {
384+
returnnil,nil,err
385+
}
386+
387+
for_,group:=rangegroups {
388+
groupName:=group[0]
389+
role:=group[1]
390+
ifrole=="" {
391+
role=string(defaultRole)
392+
}
393+
394+
varorgGroup*codersdk.Group
395+
for_,og:=rangeorgGroups {
396+
ifog.Name==groupName {
397+
orgGroup=&og
398+
break
399+
}
400+
}
401+
402+
iforgGroup==nil {
403+
returnnil,nil,xerrors.Errorf("could not find group named %s belonging to the organization %s",groupName,orgName)
404+
}
405+
406+
workspaceRole,err:=stringToWorkspaceRole(role)
407+
iferr!=nil {
408+
returnnil,nil,err
409+
}
410+
411+
groupRoles[orgGroup.ID.String()]=workspaceRole
412+
}
413+
}
414+
415+
returnuserRoles,groupRoles,nil
416+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp