Skip to content

Commit 12dfd05

Browse files
committed
feat: remove user from groups on org membership delete
Groups inherently provide authz access to certain resources. If a user is removed from an organization, they should be removed from all their groups in said organization.
1 parent 370f0b9 commit 12dfd05

File tree

5 files changed

+99
-1
lines changed

5 files changed

+99
-1
lines changed

coderd/database/dbmem/dbmem.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1922,7 +1922,7 @@ func (q *FakeQuerier) DeleteOrganization(_ context.Context, id uuid.UUID) error
19221922
return sql.ErrNoRows
19231923
}
19241924

1925-
func (q *FakeQuerier) DeleteOrganizationMember(_ context.Context, arg database.DeleteOrganizationMemberParams) error {
1925+
func (q *FakeQuerier) DeleteOrganizationMember(ctx context.Context, arg database.DeleteOrganizationMemberParams) error {
19261926
err := validateDatabaseType(arg)
19271927
if err != nil {
19281928
return err
@@ -1937,6 +1937,16 @@ func (q *FakeQuerier) DeleteOrganizationMember(_ context.Context, arg database.D
19371937
if len(deleted) == 0 {
19381938
return sql.ErrNoRows
19391939
}
1940+
1941+
// Delete group member trigger
1942+
q.groupMembers = slices.DeleteFunc(q.groupMembers, func(member database.GroupMemberTable) bool {
1943+
if member.UserID != arg.UserID {
1944+
return false
1945+
}
1946+
g, _ := q.getGroupByIDNoLock(ctx, member.GroupID)
1947+
return g.OrganizationID == arg.OrganizationID
1948+
})
1949+
19401950
return nil
19411951
}
19421952

coderd/database/dump.sql

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
DROP TRIGGER IF EXISTS trigger_delete_group_members_on_org_member_delete ON organization_members;
2+
DROP FUNCTION IF EXISTS delete_group_members_on_org_member_delete;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
CREATE FUNCTION delete_group_members_on_org_member_delete() RETURNS TRIGGER
2+
LANGUAGE plpgsql
3+
AS $$
4+
DECLARE
5+
BEGIN
6+
-- Remove the user from all groups associated with the same
7+
-- organization as the organization_member being deleted.
8+
DELETE FROM group_members
9+
WHERE
10+
user_id = OLD.user_id
11+
AND group_id IN (
12+
SELECT id
13+
FROM groups
14+
WHERE organization_id = OLD.organization_id
15+
);
16+
RETURN OLD;
17+
END;
18+
$$;
19+
20+
CREATE TRIGGER trigger_delete_group_members_on_org_member_delete
21+
BEFORE DELETE ON organization_members
22+
FOR EACH ROW
23+
EXECUTE PROCEDURE delete_group_members_on_org_member_delete();

enterprise/members_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ func TestEnterpriseMembers(t *testing.T) {
2929
LicenseOptions: &coderdenttest.LicenseOptions{
3030
Features: license.Features{
3131
codersdk.FeatureMultipleOrganizations: 1,
32+
codersdk.FeatureTemplateRBAC: 1,
3233
},
3334
},
3435
})
@@ -39,6 +40,21 @@ func TestEnterpriseMembers(t *testing.T) {
3940
_, user := coderdtest.CreateAnotherUser(t, owner, secondOrg.ID)
4041

4142
ctx := testutil.Context(t, testutil.WaitMedium)
43+
44+
// Groups exist to ensure a user removed from the org loses their
45+
// group access.
46+
g1, err := orgAdminClient.CreateGroup(ctx, secondOrg.ID, codersdk.CreateGroupRequest{
47+
Name: "foo",
48+
DisplayName: "Foo",
49+
})
50+
require.NoError(t, err)
51+
52+
g2, err := orgAdminClient.CreateGroup(ctx, secondOrg.ID, codersdk.CreateGroupRequest{
53+
Name: "bar",
54+
DisplayName: "Bar",
55+
})
56+
require.NoError(t, err)
57+
4258
// Verify the org of 3 members
4359
members, err := orgAdminClient.OrganizationMembers(ctx, secondOrg.ID)
4460
require.NoError(t, err)
@@ -47,6 +63,25 @@ func TestEnterpriseMembers(t *testing.T) {
4763
[]uuid.UUID{first.UserID, user.ID, orgAdmin.ID},
4864
db2sdk.List(members, onlyIDs))
4965

66+
// Add the member to some groups
67+
_, err = orgAdminClient.PatchGroup(ctx, g1.ID, codersdk.PatchGroupRequest{
68+
AddUsers: []string{user.ID.String()},
69+
})
70+
require.NoError(t, err)
71+
72+
_, err = orgAdminClient.PatchGroup(ctx, g2.ID, codersdk.PatchGroupRequest{
73+
AddUsers: []string{user.ID.String()},
74+
})
75+
require.NoError(t, err)
76+
77+
// Verify group membership
78+
userGroups, err := orgAdminClient.Groups(ctx, codersdk.GroupArguments{
79+
HasMember: user.ID.String(),
80+
})
81+
require.NoError(t, err)
82+
// Everyone group + 2 groups
83+
require.Len(t, userGroups, 3)
84+
5085
// Delete a member
5186
err = orgAdminClient.DeleteOrganizationMember(ctx, secondOrg.ID, user.Username)
5287
require.NoError(t, err)
@@ -57,6 +92,13 @@ func TestEnterpriseMembers(t *testing.T) {
5792
require.ElementsMatch(t,
5893
[]uuid.UUID{first.UserID, orgAdmin.ID},
5994
db2sdk.List(members, onlyIDs))
95+
96+
// User should now belong to 0 groups
97+
userGroups, err = orgAdminClient.Groups(ctx, codersdk.GroupArguments{
98+
HasMember: user.ID.String(),
99+
})
100+
require.NoError(t, err)
101+
require.Len(t, userGroups, 0)
60102
})
61103

62104
t.Run("PostUser", func(t *testing.T) {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy