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

Commit760b65c

Browse files
author
Rafael Rodriguez
committed
Merge branch 'main' into rafrdz/tooltip-support
2 parentscae0ec7 +e12b621 commit760b65c

17 files changed

+1215
-319
lines changed

‎cli/root.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ func (r *RootCmd) CoreSubcommands() []*serpent.Command {
9797
r.portForward(),
9898
r.publickey(),
9999
r.resetPassword(),
100+
r.sharing(),
100101
r.state(),
101102
r.templates(),
102103
r.tokens(),

‎cli/sharing.go‎

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
package cli
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
7+
"golang.org/x/xerrors"
8+
9+
"github.com/coder/coder/v2/cli/cliui"
10+
"github.com/coder/coder/v2/codersdk"
11+
"github.com/coder/serpent"
12+
)
13+
14+
constdefaultGroupDisplay="-"
15+
16+
typeworkspaceShareRowstruct {
17+
Userstring`table:"user"`
18+
Groupstring`table:"group,default_sort"`
19+
Role codersdk.WorkspaceRole`table:"role"`
20+
}
21+
22+
func (r*RootCmd)sharing()*serpent.Command {
23+
orgContext:=NewOrganizationContext()
24+
25+
cmd:=&serpent.Command{
26+
Use:"sharing [subcommand]",
27+
Short:"Commands for managing shared workspaces",
28+
Aliases: []string{"share"},
29+
Handler:func(inv*serpent.Invocation)error {
30+
returninv.Command.HelpHandler(inv)
31+
},
32+
Children: []*serpent.Command{r.shareWorkspace(orgContext)},
33+
Hidden:true,
34+
}
35+
36+
orgContext.AttachOptions(cmd)
37+
returncmd
38+
}
39+
40+
func (r*RootCmd)shareWorkspace(orgContext*OrganizationContext)*serpent.Command {
41+
var (
42+
// Username regex taken from codersdk/name.go
43+
nameRoleRegex=regexp.MustCompile(`(^[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*)+(?::([A-Za-z0-9-]+))?`)
44+
client=new(codersdk.Client)
45+
users []string
46+
groups []string
47+
formatter=cliui.NewOutputFormatter(
48+
cliui.TableFormat(
49+
[]workspaceShareRow{}, []string{"User","Group","Role"}),
50+
cliui.JSONFormat(),
51+
)
52+
)
53+
54+
cmd:=&serpent.Command{
55+
Use:"add <workspace> --user <user>:<role> --group <group>:<role>",
56+
Aliases: []string{"share"},
57+
Short:"Share a workspace with a user or group.",
58+
Options: serpent.OptionSet{
59+
{
60+
Name:"user",
61+
Description:"A comma separated list of users to share the workspace with.",
62+
Flag:"user",
63+
Value:serpent.StringArrayOf(&users),
64+
}, {
65+
Name:"group",
66+
Description:"A comma separated list of groups to share the workspace with.",
67+
Flag:"group",
68+
Value:serpent.StringArrayOf(&groups),
69+
},
70+
},
71+
Middleware:serpent.Chain(
72+
r.InitClient(client),
73+
serpent.RequireNArgs(1),
74+
),
75+
Handler:func(inv*serpent.Invocation)error {
76+
iflen(users)==0&&len(groups)==0 {
77+
returnxerrors.New("at least one user or group must be provided")
78+
}
79+
80+
workspace,err:=namedWorkspace(inv.Context(),client,inv.Args[0])
81+
iferr!=nil {
82+
returnxerrors.Errorf("could not fetch the workspace %s: %w",inv.Args[0],err)
83+
}
84+
85+
org,err:=orgContext.Selected(inv,client)
86+
iferr!=nil {
87+
returnerr
88+
}
89+
90+
userRoles:=make(map[string]codersdk.WorkspaceRole,len(users))
91+
iflen(users)>0 {
92+
orgMembers,err:=client.OrganizationMembers(inv.Context(),org.ID)
93+
iferr!=nil {
94+
returnerr
95+
}
96+
97+
for_,user:=rangeusers {
98+
userAndRole:=nameRoleRegex.FindStringSubmatch(user)
99+
ifuserAndRole==nil {
100+
returnxerrors.Errorf("invalid user format %q: must match pattern 'username:role'",user)
101+
}
102+
103+
username:=userAndRole[1]
104+
role:=userAndRole[2]
105+
ifrole=="" {
106+
role=string(codersdk.WorkspaceRoleUse)
107+
}
108+
109+
userID:=""
110+
for_,member:=rangeorgMembers {
111+
ifmember.Username==username {
112+
userID=member.UserID.String()
113+
break
114+
}
115+
}
116+
ifuserID=="" {
117+
returnxerrors.Errorf("could not find user %s in the organization %s",username,org.Name)
118+
}
119+
120+
workspaceRole,err:=stringToWorkspaceRole(role)
121+
iferr!=nil {
122+
returnerr
123+
}
124+
125+
userRoles[userID]=workspaceRole
126+
}
127+
}
128+
129+
groupRoles:=make(map[string]codersdk.WorkspaceRole)
130+
iflen(groups)>0 {
131+
orgGroups,err:=client.Groups(inv.Context(), codersdk.GroupArguments{
132+
Organization:org.ID.String(),
133+
})
134+
iferr!=nil {
135+
returnerr
136+
}
137+
138+
for_,group:=rangegroups {
139+
groupAndRole:=nameRoleRegex.FindStringSubmatch(group)
140+
ifgroupAndRole==nil {
141+
returnxerrors.Errorf("invalid group format %q: must match pattern 'group:role'",group)
142+
}
143+
groupName:=groupAndRole[1]
144+
role:=groupAndRole[2]
145+
ifrole=="" {
146+
role=string(codersdk.WorkspaceRoleUse)
147+
}
148+
149+
varorgGroup*codersdk.Group
150+
for_,group:=rangeorgGroups {
151+
ifgroup.Name==groupName {
152+
orgGroup=&group
153+
break
154+
}
155+
}
156+
157+
iforgGroup==nil {
158+
returnxerrors.Errorf("could not find group named %s belonging to the organization %s",groupName,org.Name)
159+
}
160+
161+
workspaceRole,err:=stringToWorkspaceRole(role)
162+
iferr!=nil {
163+
returnerr
164+
}
165+
166+
groupRoles[orgGroup.ID.String()]=workspaceRole
167+
}
168+
}
169+
170+
err=client.UpdateWorkspaceACL(inv.Context(),workspace.ID, codersdk.UpdateWorkspaceACL{
171+
UserRoles:userRoles,
172+
GroupRoles:groupRoles,
173+
})
174+
iferr!=nil {
175+
returnerr
176+
}
177+
178+
workspaceACL,err:=client.WorkspaceACL(inv.Context(),workspace.ID)
179+
iferr!=nil {
180+
returnxerrors.Errorf("could not fetch current workspace ACL after sharing %w",err)
181+
}
182+
183+
outputRows:=make([]workspaceShareRow,0)
184+
for_,user:=rangeworkspaceACL.Users {
185+
ifuser.Role==codersdk.WorkspaceRoleDeleted {
186+
continue
187+
}
188+
189+
outputRows=append(outputRows,workspaceShareRow{
190+
User:user.Username,
191+
Group:defaultGroupDisplay,
192+
Role:user.Role,
193+
})
194+
}
195+
for_,group:=rangeworkspaceACL.Groups {
196+
ifgroup.Role==codersdk.WorkspaceRoleDeleted {
197+
continue
198+
}
199+
200+
for_,user:=rangegroup.Members {
201+
outputRows=append(outputRows,workspaceShareRow{
202+
User:user.Username,
203+
Group:group.Name,
204+
Role:group.Role,
205+
})
206+
}
207+
}
208+
out,err:=formatter.Format(inv.Context(),outputRows)
209+
iferr!=nil {
210+
returnerr
211+
}
212+
213+
_,err=fmt.Fprintln(inv.Stdout,out)
214+
returnerr
215+
},
216+
}
217+
218+
returncmd
219+
}
220+
221+
funcstringToWorkspaceRole(rolestring) (codersdk.WorkspaceRole,error) {
222+
switchrole {
223+
casestring(codersdk.WorkspaceRoleUse):
224+
returncodersdk.WorkspaceRoleUse,nil
225+
casestring(codersdk.WorkspaceRoleAdmin):
226+
returncodersdk.WorkspaceRoleAdmin,nil
227+
default:
228+
return"",xerrors.Errorf("invalid role %q: expected %q or %q",
229+
role,codersdk.WorkspaceRoleAdmin,codersdk.WorkspaceRoleUse)
230+
}
231+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp