Skip to content

Commit 7ce87a7

Browse files
committed
make groups a privledged endpoint
1 parent 4545d1a commit 7ce87a7

File tree

11 files changed

+412
-66
lines changed

11 files changed

+412
-66
lines changed

coderd/apidoc/docs.go

Lines changed: 55 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

Lines changed: 51 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/dbauthz/dbauthz.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2614,24 +2614,24 @@ func (q *querier) GetAuthorizedTemplates(ctx context.Context, arg database.GetTe
26142614
}
26152615

26162616
func (q *querier) GetTemplateGroupRoles(ctx context.Context, id uuid.UUID) ([]database.TemplateGroup, error) {
2617-
// An actor is authorized to read template group roles if they are authorized to read the template.
2617+
// An actor is authorized to read template group roles if they are authorized to update the template.
26182618
template, err := q.db.GetTemplateByID(ctx, id)
26192619
if err != nil {
26202620
return nil, err
26212621
}
2622-
if err := q.authorizeContext(ctx, rbac.ActionRead, template); err != nil {
2622+
if err := q.authorizeContext(ctx, rbac.ActionUpdate, template); err != nil {
26232623
return nil, err
26242624
}
26252625
return q.db.GetTemplateGroupRoles(ctx, id)
26262626
}
26272627

26282628
func (q *querier) GetTemplateUserRoles(ctx context.Context, id uuid.UUID) ([]database.TemplateUser, error) {
2629-
// An actor is authorized to query template user roles if they are authorized to read the template.
2629+
// An actor is authorized to query template user roles if they are authorized to update the template.
26302630
template, err := q.db.GetTemplateByID(ctx, id)
26312631
if err != nil {
26322632
return nil, err
26332633
}
2634-
if err := q.authorizeContext(ctx, rbac.ActionRead, template); err != nil {
2634+
if err := q.authorizeContext(ctx, rbac.ActionUpdate, template); err != nil {
26352635
return nil, err
26362636
}
26372637
return q.db.GetTemplateUserRoles(ctx, id)

coderd/rbac/roles.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
168168
ResourceTemplate.Type: {ActionRead},
169169
ResourceAuditLog.Type: {ActionRead},
170170
ResourceUser.Type: {ActionRead},
171+
ResourceGroup.Type: {ActionRead},
172+
// Org roles are not really used yet, so grant the perm at the site level.
173+
ResourceOrganizationMember.Type: {ActionRead},
171174
}),
172175
Org: map[string][]Permission{},
173176
User: []Permission{},
@@ -186,6 +189,9 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
186189
// Needs to read all organizations since
187190
ResourceOrganization.Type: {ActionRead},
188191
ResourceUser.Type: {ActionRead},
192+
ResourceGroup.Type: {ActionRead},
193+
// Org roles are not really used yet, so grant the perm at the site level.
194+
ResourceOrganizationMember.Type: {ActionRead},
189195
}),
190196
Org: map[string][]Permission{},
191197
User: []Permission{},
@@ -200,6 +206,9 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
200206
// Full perms to manage org members
201207
ResourceOrganizationMember.Type: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
202208
ResourceGroup.Type: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
209+
210+
// Org roles are not really used yet, so grant the perm at the site level.
211+
ResourceOrganizationMember.Type: {ActionRead},
203212
}),
204213
Org: map[string][]Permission{},
205214
User: []Permission{},
@@ -255,11 +264,6 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
255264
Site: []Permission{},
256265
Org: map[string][]Permission{
257266
organizationID: {
258-
{
259-
// All org members can read the other members in their org.
260-
ResourceType: ResourceOrganizationMember.Type,
261-
Action: ActionRead,
262-
},
263267
{
264268
// All org members can read the organization
265269
ResourceType: ResourceOrganization.Type,
@@ -270,10 +274,6 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
270274
ResourceType: ResourceOrgRoleAssignment.Type,
271275
Action: ActionRead,
272276
},
273-
{
274-
ResourceType: ResourceGroup.Type,
275-
Action: ActionRead,
276-
},
277277
},
278278
},
279279
User: []Permission{},

coderd/users.go

Lines changed: 41 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,40 @@ func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) {
182182
// @Success 200 {object} codersdk.GetUsersResponse
183183
// @Router /users [get]
184184
func (api *API) users(rw http.ResponseWriter, r *http.Request) {
185+
ctx := r.Context()
186+
users, userCount, ok := api.GetUsers(rw, r)
187+
if !ok {
188+
return
189+
}
190+
191+
userIDs := make([]uuid.UUID, 0, len(users))
192+
for _, user := range users {
193+
userIDs = append(userIDs, user.ID)
194+
}
195+
organizationIDsByMemberIDsRows, err := api.Database.GetOrganizationIDsByMemberIDs(ctx, userIDs)
196+
if xerrors.Is(err, sql.ErrNoRows) {
197+
err = nil
198+
}
199+
if err != nil {
200+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
201+
Message: "Internal error fetching user's organizations.",
202+
Detail: err.Error(),
203+
})
204+
return
205+
}
206+
organizationIDsByUserID := map[uuid.UUID][]uuid.UUID{}
207+
for _, organizationIDsByMemberIDsRow := range organizationIDsByMemberIDsRows {
208+
organizationIDsByUserID[organizationIDsByMemberIDsRow.UserID] = organizationIDsByMemberIDsRow.OrganizationIDs
209+
}
210+
211+
render.Status(r, http.StatusOK)
212+
render.JSON(rw, r, codersdk.GetUsersResponse{
213+
Users: convertUsers(users, organizationIDsByUserID),
214+
Count: int(userCount),
215+
})
216+
}
217+
218+
func (api *API) GetUsers(rw http.ResponseWriter, r *http.Request) ([]database.User, int64, bool) {
185219
ctx := r.Context()
186220
query := r.URL.Query().Get("q")
187221
params, errs := searchquery.Users(query)
@@ -190,12 +224,12 @@ func (api *API) users(rw http.ResponseWriter, r *http.Request) {
190224
Message: "Invalid user search query.",
191225
Validations: errs,
192226
})
193-
return
227+
return nil, -1, false
194228
}
195229

196230
paginationParams, ok := parsePagination(rw, r)
197231
if !ok {
198-
return
232+
return nil, -1, false
199233
}
200234

201235
userRows, err := api.Database.GetUsers(ctx, database.GetUsersParams{
@@ -213,52 +247,17 @@ func (api *API) users(rw http.ResponseWriter, r *http.Request) {
213247
Message: "Internal error fetching users.",
214248
Detail: err.Error(),
215249
})
216-
return
250+
return nil, -1, false
217251
}
252+
218253
// GetUsers does not return ErrNoRows because it uses a window function to get the count.
219254
// So we need to check if the userRows is empty and return an empty array if so.
220255
if len(userRows) == 0 {
221-
httpapi.Write(ctx, rw, http.StatusOK, codersdk.GetUsersResponse{
222-
Users: []codersdk.User{},
223-
Count: 0,
224-
})
225-
return
226-
}
227-
228-
users, err := AuthorizeFilter(api.HTTPAuth, r, rbac.ActionRead, database.ConvertUserRows(userRows))
229-
if err != nil {
230-
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
231-
Message: "Internal error fetching users.",
232-
Detail: err.Error(),
233-
})
234-
return
235-
}
236-
237-
userIDs := make([]uuid.UUID, 0, len(users))
238-
for _, user := range users {
239-
userIDs = append(userIDs, user.ID)
240-
}
241-
organizationIDsByMemberIDsRows, err := api.Database.GetOrganizationIDsByMemberIDs(ctx, userIDs)
242-
if xerrors.Is(err, sql.ErrNoRows) {
243-
err = nil
244-
}
245-
if err != nil {
246-
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
247-
Message: "Internal error fetching user's organizations.",
248-
Detail: err.Error(),
249-
})
250-
return
251-
}
252-
organizationIDsByUserID := map[uuid.UUID][]uuid.UUID{}
253-
for _, organizationIDsByMemberIDsRow := range organizationIDsByMemberIDsRows {
254-
organizationIDsByUserID[organizationIDsByMemberIDsRow.UserID] = organizationIDsByMemberIDsRow.OrganizationIDs
256+
return []database.User{}, -1, true
255257
}
256258

257-
render.Status(r, http.StatusOK)
258-
render.JSON(rw, r, codersdk.GetUsersResponse{
259-
Users: convertUsers(users, organizationIDsByUserID),
260-
Count: int(userRows[0].Count),
261-
})
259+
users := database.ConvertUserRows(userRows)
260+
return users, userRows[0].Count, true
262261
}
263262

264263
// Creates a new user.

codersdk/templates.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,13 @@ type UpdateTemplateACL struct {
167167
GroupPerms map[string]TemplateRole `json:"group_perms,omitempty" example:"<user_id>>:admin,8bd26b20-f3e8-48be-a903-46bb920cf671:use"`
168168
}
169169

170+
// ACLAvailable is a list of users and groups that can be added to a template
171+
// ACL.
172+
type ACLAvailable struct {
173+
Users []MinimalUser `json:"users"`
174+
Groups []Group `json:"groups"`
175+
}
176+
170177
type UpdateTemplateMeta struct {
171178
Name string `json:"name,omitempty" validate:"omitempty,template_name"`
172179
DisplayName string `json:"display_name,omitempty" validate:"omitempty,template_display_name"`

0 commit comments

Comments
 (0)