@@ -2372,13 +2372,13 @@ func (api *API) workspaceACL(rw http.ResponseWriter, r *http.Request) {
23722372 return
23732373 }
23742374
2375- // This is largely based on the template ACL implementation, and is far from
2376- // ideal. Usually, when we use the System context it's because we need to
2377- // run some query that won't actually be exposed to the user. That is not
2378- // the case here. This data goes directly to an unauthorized user. We are
2379- // just straight up breaking security promises.
2380- //
2381- // TODO: This needs to be fixed before GA. Currently in beta .
2375+ // Callers are authorized to read this workspace, not necessarily the
2376+ // users and groups on its ACL. We deliberately use the System context to
2377+ // look up that data, but only return minimal identity information that is
2378+ // safe to expose to anyone who can read the ACL: MinimalUser for ACL users
2379+ // (no email or other PII) and group identity plus a member count for ACL
2380+ // groups (no member roster). This mirrors the chat ACL and template
2381+ // available-ACL endpoints .
23822382
23832383 // Fetch all of the users and their organization memberships
23842384 userIDs := make ([]uuid.UUID , 0 , len (workspaceACL .Users ))
@@ -2390,7 +2390,8 @@ func (api *API) workspaceACL(rw http.ResponseWriter, r *http.Request) {
23902390 }
23912391 userIDs = append (userIDs , id )
23922392 }
2393- // For context see https://github.com/coder/coder/pull/19375
2393+ // ACL users are returned as MinimalUser, which contains no PII, so it is
2394+ // safe to fetch them under the System context.
23942395 // nolint:gocritic
23952396 dbUsers , err := api .Database .GetUsersByIDs (dbauthz .AsSystemRestricted (ctx ), userIDs )
23962397 if err != nil && ! xerrors .Is (err , sql .ErrNoRows ) {
@@ -2422,7 +2423,8 @@ func (api *API) workspaceACL(rw http.ResponseWriter, r *http.Request) {
24222423 // before making the DB call.
24232424 dbGroups := make ([]database.GetGroupsRow , 0 )
24242425 if len (groupIDs ) > 0 {
2425- // For context see https://github.com/coder/coder/pull/19375
2426+ // Group identity must be visible to anyone who can read the ACL so
2427+ // that owners and shared users can see and manage entries.
24262428 // nolint:gocritic
24272429 dbGroups , err = api .Database .GetGroups (dbauthz .AsSystemRestricted (ctx ), database.GetGroupsParams {GroupIds : groupIDs })
24282430 if err != nil && ! xerrors .Is (err , sql .ErrNoRows ) {
@@ -2431,26 +2433,30 @@ func (api *API) workspaceACL(rw http.ResponseWriter, r *http.Request) {
24312433 }
24322434 }
24332435
2436+ // Fetch member counts for all groups in a single query to avoid an N+1
2437+ // lookup. We intentionally do not populate the per-group member rosters:
2438+ // callers authorized to read the ACL are not necessarily authorized to
2439+ // read group membership, and the roster includes member PII. Only the
2440+ // total member count is returned (see Group.TotalMemberCount).
2441+ // nolint:gocritic
2442+ countRows , err := api .Database .GetGroupMembersCountByGroupIDs (dbauthz .AsSystemRestricted (ctx ), database.GetGroupMembersCountByGroupIDsParams {
2443+ GroupIds : groupIDs ,
2444+ IncludeSystem : false ,
2445+ })
2446+ if err != nil && ! xerrors .Is (err , sql .ErrNoRows ) {
2447+ httpapi .InternalServerError (rw , err )
2448+ return
2449+ }
2450+ countByGroup := make (map [uuid.UUID ]int64 , len (countRows ))
2451+ for _ , row := range countRows {
2452+ countByGroup [row .GroupID ] = row .MemberCount
2453+ }
2454+
24342455 groups := make ([]codersdk.WorkspaceGroup , 0 , len (dbGroups ))
24352456 for _ , it := range dbGroups {
2436- var members []database.GroupMember
2437- // For context see https://github.com/coder/coder/pull/19375
2438- // nolint:gocritic
2439- members , err = api .Database .GetGroupMembersByGroupID (dbauthz .AsSystemRestricted (ctx ), database.GetGroupMembersByGroupIDParams {
2440- GroupID : it .Group .ID ,
2441- IncludeSystem : false ,
2442- })
2443- if err != nil {
2444- httpapi .InternalServerError (rw , err )
2445- return
2446- }
24472457 groups = append (groups , codersdk.WorkspaceGroup {
2448- Group : db2sdk .Group (database.GetGroupsRow {
2449- Group : it .Group ,
2450- OrganizationName : it .OrganizationName ,
2451- OrganizationDisplayName : it .OrganizationDisplayName ,
2452- }, members , len (members )),
2453- Role : convertToWorkspaceRole (workspaceACL .Groups [it .Group .ID .String ()].Permissions ),
2458+ Group : db2sdk .Group (it , nil , int (countByGroup [it .Group .ID ])),
2459+ Role : convertToWorkspaceRole (workspaceACL .Groups [it .Group .ID .String ()].Permissions ),
24542460 })
24552461 }
24562462
0 commit comments