Skip to content

Commit 2c6d037

Browse files
committed
- allow group members to read basic Group info
- allow group members to see they are part of the group, but not see that information about other members - add a GetGroupMembersCountByGroupID SQL query, which allows group members to see members count without revealing other information about the members - expose a reducedGroupsByUserAndOrganization API endpoint
1 parent 4c7132f commit 2c6d037

File tree

17 files changed

+471
-3
lines changed

17 files changed

+471
-3
lines changed

coderd/apidoc/docs.go

Lines changed: 69 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: 65 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/db2sdk/db2sdk.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,17 @@ func Group(group database.Group, members []database.User) codersdk.Group {
192192
}
193193
}
194194

195+
func ReducedGroup(group database.Group, memberCount int) codersdk.ReducedGroup {
196+
return codersdk.ReducedGroup{
197+
ID: group.ID,
198+
Name: group.Name,
199+
DisplayName: group.DisplayName,
200+
OrganizationID: group.OrganizationID,
201+
AvatarURL: group.AvatarURL,
202+
MemberCount: memberCount,
203+
}
204+
}
205+
195206
func TemplateInsightsParameters(parameterRows []database.GetTemplateParameterInsightsRow) ([]codersdk.TemplateParameterUsage, error) {
196207
// Use a stable sort, similarly to how we would sort in the query, note that
197208
// we don't sort in the query because order varies depending on the table

coderd/database/dbauthz/dbauthz.go

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,10 +1397,55 @@ func (q *querier) GetGroupMembers(ctx context.Context) ([]database.GroupMember,
13971397
}
13981398

13991399
func (q *querier) GetGroupMembersByGroupID(ctx context.Context, id uuid.UUID) ([]database.User, error) {
1400-
if _, err := q.GetGroupByID(ctx, id); err != nil { // AuthZ check
1400+
group, err := q.GetGroupByID(ctx, id)
1401+
if err != nil { // AuthZ check
14011402
return nil, err
14021403
}
1403-
return q.db.GetGroupMembersByGroupID(ctx, id)
1404+
// The UserWithGroupAndOrgID type is used to do the authz check. It ensures
1405+
// that group members can see themselves. Unless they have Group read permissions,
1406+
// they cannot see other members.
1407+
fetch := func(ctx context.Context, _ any) ([]database.UserWithGroupAndOrgID, error) {
1408+
users, err := q.db.GetGroupMembersByGroupID(ctx, id)
1409+
if err != nil {
1410+
return nil, err
1411+
}
1412+
groupMembers := make([]database.UserWithGroupAndOrgID, len(users))
1413+
for i, user := range users {
1414+
groupMembers[i] = database.UserWithGroupAndOrgID{
1415+
User: user,
1416+
GroupID: group.ID,
1417+
OrganizationID: group.OrganizationID,
1418+
}
1419+
}
1420+
return groupMembers, nil
1421+
}
1422+
groupMembers, err := fetchWithPostFilter(q.auth, policy.ActionRead, fetch)(ctx, nil)
1423+
if err != nil {
1424+
return nil, err
1425+
}
1426+
users := make([]database.User, len(groupMembers))
1427+
for i, groupMember := range groupMembers {
1428+
users[i] = groupMember.User
1429+
}
1430+
return users, nil
1431+
}
1432+
1433+
func (q *querier) GetGroupMembersCountByGroupID(ctx context.Context, groupID uuid.UUID) (int64, error) {
1434+
group, err := q.GetGroupByID(ctx, groupID)
1435+
if err != nil {
1436+
return 0, err
1437+
}
1438+
memberCount, err := q.db.GetGroupMembersCountByGroupID(ctx, groupID)
1439+
if err != nil {
1440+
return 0, err
1441+
}
1442+
if err := q.authorizeContext(ctx, policy.ActionRead, database.GroupMembersCountRBACHelper{
1443+
GroupID: groupID,
1444+
OrganizationID: group.OrganizationID,
1445+
}); err != nil {
1446+
return 0, err
1447+
}
1448+
return memberCount, nil
14041449
}
14051450

14061451
func (q *querier) GetGroups(ctx context.Context) ([]database.Group, error) {

coderd/database/dbmem/dbmem.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2524,6 +2524,14 @@ func (q *FakeQuerier) GetGroupMembersByGroupID(_ context.Context, id uuid.UUID)
25242524
return users, nil
25252525
}
25262526

2527+
func (q *FakeQuerier) GetGroupMembersCountByGroupID(ctx context.Context, groupID uuid.UUID) (int64, error) {
2528+
users, err := q.GetGroupMembersByGroupID(ctx, groupID)
2529+
if err != nil {
2530+
return 0, err
2531+
}
2532+
return int64(len(users)), nil
2533+
}
2534+
25272535
func (q *FakeQuerier) GetGroups(_ context.Context) ([]database.Group, error) {
25282536
q.mutex.RLock()
25292537
defer q.mutex.RUnlock()

coderd/database/dbmetrics/dbmetrics.go

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

coderd/database/dbmock/dbmock.go

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

coderd/database/modelmethods.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
"github.com/coder/coder/v2/coderd/database/dbtime"
1414
"github.com/coder/coder/v2/coderd/rbac"
15+
"github.com/coder/coder/v2/coderd/rbac/policy"
1516
)
1617

1718
type WorkspaceStatus string
@@ -173,7 +174,44 @@ func (v TemplateVersion) RBACObjectNoTemplate() rbac.Object {
173174

174175
func (g Group) RBACObject() rbac.Object {
175176
return rbac.ResourceGroup.WithID(g.ID).
176-
InOrg(g.OrganizationID)
177+
InOrg(g.OrganizationID).
178+
// Group members can read the group.
179+
WithGroupACL(map[string][]policy.Action{
180+
g.ID.String(): {
181+
policy.ActionRead,
182+
},
183+
})
184+
}
185+
186+
type UserWithGroupAndOrgID struct {
187+
User User
188+
GroupID uuid.UUID
189+
OrganizationID uuid.UUID
190+
}
191+
192+
func (gm UserWithGroupAndOrgID) RBACObject() rbac.Object {
193+
return rbac.ResourceGroup.WithID(gm.GroupID).InOrg(gm.OrganizationID).
194+
// Group member can see they are in the group.
195+
WithACLUserList(map[string][]policy.Action{
196+
gm.User.ID.String(): {
197+
policy.ActionRead,
198+
},
199+
})
200+
}
201+
202+
type GroupMembersCountRBACHelper struct {
203+
GroupID uuid.UUID
204+
OrganizationID uuid.UUID
205+
}
206+
207+
func (r GroupMembersCountRBACHelper) RBACObject() rbac.Object {
208+
return rbac.ResourceGroup.WithID(r.GroupID).InOrg(r.OrganizationID).
209+
// Group members can read the member count.
210+
WithGroupACL(map[string][]policy.Action{
211+
r.GroupID.String(): {
212+
policy.ActionRead,
213+
},
214+
})
177215
}
178216

179217
func (w GetWorkspaceByAgentIDRow) RBACObject() rbac.Object {

coderd/database/querier.go

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

coderd/database/queries.sql.go

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

0 commit comments

Comments
 (0)