@Summary Get template available acl users/groups @ID get-template-available-acl-usersgroups @Security CoderSessionToken @Produce json @Tags Enterprise @Param template path string true "Template ID" format(uuid) @Success 200 {array} codersdk.ACLAvailable @Router /api/v2/templates/{template}/acl/avail
(rw http.ResponseWriter, r *http.Request)
| 32 | // @Success 200 {array} codersdk.ACLAvailable |
| 33 | // @Router /api/v2/templates/{template}/acl/available [get] |
| 34 | func (api *API) templateAvailablePermissions(rw http.ResponseWriter, r *http.Request) { |
| 35 | var ( |
| 36 | ctx = r.Context() |
| 37 | template = httpmw.TemplateParam(r) |
| 38 | ) |
| 39 | |
| 40 | // Requires update permission on the template to list all avail users/groups |
| 41 | // for assignment. |
| 42 | if !api.Authorize(r, policy.ActionUpdate, template) { |
| 43 | httpapi.ResourceNotFound(rw) |
| 44 | return |
| 45 | } |
| 46 | |
| 47 | // We have to use the system restricted context here because the caller |
| 48 | // might not have permission to read all users. |
| 49 | // nolint:gocritic |
| 50 | users, _, ok := api.AGPL.GetUsers(rw, r.WithContext(dbauthz.AsSystemRestricted(ctx))) |
| 51 | if !ok { |
| 52 | return |
| 53 | } |
| 54 | |
| 55 | // Apply the same q/limit semantics to groups as the users half of this response. |
| 56 | // The query semantics are defined for the users, which is awkward. But we can |
| 57 | // just reuse the search part of the query which is a fuzzy match. |
| 58 | userFilter, verr := searchquery.Users(r.URL.Query().Get("q")) |
| 59 | if len(verr) > 0 { |
| 60 | httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ |
| 61 | Message: "Invalid user search query.", |
| 62 | Validations: verr, |
| 63 | }) |
| 64 | return |
| 65 | } |
| 66 | groupPagination, ok := agpl.ParsePagination(rw, r) |
| 67 | if !ok { |
| 68 | return |
| 69 | } |
| 70 | |
| 71 | // Perm check is the template update check. |
| 72 | // nolint:gocritic |
| 73 | groups, err := api.Database.GetGroups(dbauthz.AsSystemRestricted(ctx), database.GetGroupsParams{ |
| 74 | OrganizationID: template.OrganizationID, |
| 75 | Search: userFilter.Search, |
| 76 | // #nosec G115 - Pagination limits are small and fit in int32 |
| 77 | LimitOpt: int32(groupPagination.Limit), |
| 78 | }) |
| 79 | if err != nil { |
| 80 | httpapi.InternalServerError(rw, err) |
| 81 | return |
| 82 | } |
| 83 | |
| 84 | // Fetch member counts for all groups in a single query to avoid an |
| 85 | // N+1 lookup pattern that was making this endpoint extremely slow on |
| 86 | // deployments with many groups. The per-group member lists are |
| 87 | // intentionally not populated here: callers of this endpoint only |
| 88 | // surface total_member_count (see Group.TotalMemberCount, which is |
| 89 | // already documented as the canonical value). |
| 90 | groupIDs := make([]uuid.UUID, len(groups)) |
| 91 | for i, g := range groups { |
nothing calls this directly
no test coverage detected