Skip to content

Commit 6b10251

Browse files
committed
feat: set groupsync to use default org
1 parent a67362f commit 6b10251

30 files changed

+348
-160
lines changed

coderd/apidoc/docs.go

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

+4-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/coderd.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ type Options struct {
134134
BaseDERPMap *tailcfg.DERPMap
135135
DERPMapUpdateFrequency time.Duration
136136
SwaggerEndpoint bool
137-
SetUserGroups func(ctx context.Context, logger slog.Logger, tx database.Store, userID uuid.UUID, groupNames []string, createMissingGroups bool) error
137+
SetUserGroups func(ctx context.Context, logger slog.Logger, tx database.Store, userID uuid.UUID, orgGroupNames map[uuid.UUID][]string, createMissingGroups bool) error
138138
SetUserSiteRoles func(ctx context.Context, logger slog.Logger, tx database.Store, userID uuid.UUID, roles []string) error
139139
TemplateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore]
140140
UserQuietHoursScheduleStore *atomic.Pointer[schedule.UserQuietHoursScheduleStore]
@@ -301,9 +301,11 @@ func New(options *Options) *API {
301301
options.TracerProvider = trace.NewNoopTracerProvider()
302302
}
303303
if options.SetUserGroups == nil {
304-
options.SetUserGroups = func(ctx context.Context, logger slog.Logger, _ database.Store, userID uuid.UUID, groups []string, createMissingGroups bool) error {
304+
options.SetUserGroups = func(ctx context.Context, logger slog.Logger, _ database.Store, userID uuid.UUID, orgGroupNames map[uuid.UUID][]string, createMissingGroups bool) error {
305305
logger.Warn(ctx, "attempted to assign OIDC groups without enterprise license",
306-
slog.F("user_id", userID), slog.F("groups", groups), slog.F("create_missing_groups", createMissingGroups),
306+
slog.F("user_id", userID),
307+
slog.F("groups", orgGroupNames),
308+
slog.F("create_missing_groups", createMissingGroups),
307309
)
308310
return nil
309311
}

coderd/database/dbauthz/dbauthz.go

+14-10
Original file line numberDiff line numberDiff line change
@@ -793,16 +793,6 @@ func (q *querier) DeleteGroupMemberFromGroup(ctx context.Context, arg database.D
793793
return update(q.log, q.auth, fetch, q.db.DeleteGroupMemberFromGroup)(ctx, arg)
794794
}
795795

796-
func (q *querier) DeleteGroupMembersByOrgAndUser(ctx context.Context, arg database.DeleteGroupMembersByOrgAndUserParams) error {
797-
// This will remove the user from all groups in the org. This counts as updating a group.
798-
// NOTE: instead of fetching all groups in the org with arg.UserID as a member, we instead
799-
// check if the caller has permission to update any group in the org.
800-
fetch := func(ctx context.Context, arg database.DeleteGroupMembersByOrgAndUserParams) (rbac.Objecter, error) {
801-
return rbac.ResourceGroup.InOrg(arg.OrganizationID), nil
802-
}
803-
return update(q.log, q.auth, fetch, q.db.DeleteGroupMembersByOrgAndUser)(ctx, arg)
804-
}
805-
806796
func (q *querier) DeleteLicense(ctx context.Context, id int32) (int32, error) {
807797
err := deleteQ(q.log, q.auth, q.db.GetLicenseByID, func(ctx context.Context, id int32) error {
808798
_, err := q.db.DeleteLicense(ctx, id)
@@ -1016,6 +1006,12 @@ func (q *querier) GetDERPMeshKey(ctx context.Context) (string, error) {
10161006
return q.db.GetDERPMeshKey(ctx)
10171007
}
10181008

1009+
func (q *querier) GetDefaultOrganization(ctx context.Context) (database.Organization, error) {
1010+
return fetch(q.log, q.auth, func(ctx context.Context, _ any) (database.Organization, error) {
1011+
return q.db.GetDefaultOrganization(ctx)
1012+
})(ctx, nil)
1013+
}
1014+
10191015
func (q *querier) GetDefaultProxyConfig(ctx context.Context) (database.GetDefaultProxyConfigRow, error) {
10201016
// No authz checks
10211017
return q.db.GetDefaultProxyConfig(ctx)
@@ -2549,6 +2545,14 @@ func (q *querier) RegisterWorkspaceProxy(ctx context.Context, arg database.Regis
25492545
return updateWithReturn(q.log, q.auth, fetch, q.db.RegisterWorkspaceProxy)(ctx, arg)
25502546
}
25512547

2548+
func (q *querier) RemoveUserFromAllGroups(ctx context.Context, userID uuid.UUID) error {
2549+
// This is a system function to clear user groups in group sync.
2550+
if err := q.authorizeContext(ctx, rbac.ActionUpdate, rbac.ResourceSystem); err != nil {
2551+
return err
2552+
}
2553+
return q.db.RemoveUserFromAllGroups(ctx, userID)
2554+
}
2555+
25522556
func (q *querier) RevokeDBCryptKey(ctx context.Context, activeKeyDigest string) error {
25532557
if err := q.authorizeContext(ctx, rbac.ActionUpdate, rbac.ResourceSystem); err != nil {
25542558
return err

coderd/database/dbauthz/dbauthz_test.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -344,17 +344,14 @@ func (s *MethodTestSuite) TestGroup() {
344344
GroupNames: slice.New(g1.Name, g2.Name),
345345
}).Asserts(rbac.ResourceGroup.InOrg(o.ID), rbac.ActionUpdate).Returns()
346346
}))
347-
s.Run("DeleteGroupMembersByOrgAndUser", s.Subtest(func(db database.Store, check *expects) {
347+
s.Run("RemoveUserFromAllGroups", s.Subtest(func(db database.Store, check *expects) {
348348
o := dbgen.Organization(s.T(), db, database.Organization{})
349349
u1 := dbgen.User(s.T(), db, database.User{})
350350
g1 := dbgen.Group(s.T(), db, database.Group{OrganizationID: o.ID})
351351
g2 := dbgen.Group(s.T(), db, database.Group{OrganizationID: o.ID})
352352
_ = dbgen.GroupMember(s.T(), db, database.GroupMember{GroupID: g1.ID, UserID: u1.ID})
353353
_ = dbgen.GroupMember(s.T(), db, database.GroupMember{GroupID: g2.ID, UserID: u1.ID})
354-
check.Args(database.DeleteGroupMembersByOrgAndUserParams{
355-
OrganizationID: o.ID,
356-
UserID: u1.ID,
357-
}).Asserts(rbac.ResourceGroup.InOrg(o.ID), rbac.ActionUpdate).Returns()
354+
check.Args(u1.ID).Asserts(rbac.ResourceSystem, rbac.ActionUpdate).Returns()
358355
}))
359356
s.Run("UpdateGroupByID", s.Subtest(func(db database.Store, check *expects) {
360357
g := dbgen.Group(s.T(), db, database.Group{})
@@ -570,6 +567,10 @@ func (s *MethodTestSuite) TestOrganization() {
570567
o := dbgen.Organization(s.T(), db, database.Organization{})
571568
check.Args(o.ID).Asserts(o, rbac.ActionRead).Returns(o)
572569
}))
570+
s.Run("GetDefaultOrganization", s.Subtest(func(db database.Store, check *expects) {
571+
o := dbgen.Organization(s.T(), db, database.Organization{})
572+
check.Args().Asserts(o, rbac.ActionRead).Returns(o)
573+
}))
573574
s.Run("GetOrganizationByName", s.Subtest(func(db database.Store, check *expects) {
574575
o := dbgen.Organization(s.T(), db, database.Organization{})
575576
check.Args(o.Name).Asserts(o, rbac.ActionRead).Returns(o)

coderd/database/dbmem/dbmem.go

+29-30
Original file line numberDiff line numberDiff line change
@@ -1135,36 +1135,6 @@ func (q *FakeQuerier) DeleteGroupMemberFromGroup(_ context.Context, arg database
11351135
return nil
11361136
}
11371137

1138-
func (q *FakeQuerier) DeleteGroupMembersByOrgAndUser(_ context.Context, arg database.DeleteGroupMembersByOrgAndUserParams) error {
1139-
q.mutex.Lock()
1140-
defer q.mutex.Unlock()
1141-
1142-
newMembers := q.groupMembers[:0]
1143-
for _, member := range q.groupMembers {
1144-
if member.UserID != arg.UserID {
1145-
// Do not delete the other members
1146-
newMembers = append(newMembers, member)
1147-
} else if member.UserID == arg.UserID {
1148-
// We only want to delete from groups in the organization in the args.
1149-
for _, group := range q.groups {
1150-
// Find the group that the member is apartof.
1151-
if group.ID == member.GroupID {
1152-
// Only add back the member if the organization ID does not match
1153-
// the arg organization ID. Since the arg is saying which
1154-
// org to delete.
1155-
if group.OrganizationID != arg.OrganizationID {
1156-
newMembers = append(newMembers, member)
1157-
}
1158-
break
1159-
}
1160-
}
1161-
}
1162-
}
1163-
q.groupMembers = newMembers
1164-
1165-
return nil
1166-
}
1167-
11681138
func (q *FakeQuerier) DeleteLicense(_ context.Context, id int32) (int32, error) {
11691139
q.mutex.Lock()
11701140
defer q.mutex.Unlock()
@@ -1657,6 +1627,18 @@ func (q *FakeQuerier) GetDERPMeshKey(_ context.Context) (string, error) {
16571627
return q.derpMeshKey, nil
16581628
}
16591629

1630+
func (q *FakeQuerier) GetDefaultOrganization(_ context.Context) (database.Organization, error) {
1631+
q.mutex.RLock()
1632+
defer q.mutex.RUnlock()
1633+
1634+
for _, org := range q.organizations {
1635+
if org.IsDefault {
1636+
return org, nil
1637+
}
1638+
}
1639+
return database.Organization{}, sql.ErrNoRows
1640+
}
1641+
16601642
func (q *FakeQuerier) GetDefaultProxyConfig(_ context.Context) (database.GetDefaultProxyConfigRow, error) {
16611643
return database.GetDefaultProxyConfigRow{
16621644
DisplayName: q.defaultProxyDisplayName,
@@ -5285,6 +5267,7 @@ func (q *FakeQuerier) InsertOrganization(_ context.Context, arg database.InsertO
52855267
Name: arg.Name,
52865268
CreatedAt: arg.CreatedAt,
52875269
UpdatedAt: arg.UpdatedAt,
5270+
IsDefault: len(q.organizations) == 0,
52885271
}
52895272
q.organizations = append(q.organizations, organization)
52905273
return organization, nil
@@ -6083,6 +6066,22 @@ func (q *FakeQuerier) RegisterWorkspaceProxy(_ context.Context, arg database.Reg
60836066
return database.WorkspaceProxy{}, sql.ErrNoRows
60846067
}
60856068

6069+
func (q *FakeQuerier) RemoveUserFromAllGroups(_ context.Context, userID uuid.UUID) error {
6070+
q.mutex.Lock()
6071+
defer q.mutex.Unlock()
6072+
6073+
newMembers := q.groupMembers[:0]
6074+
for _, member := range q.groupMembers {
6075+
if member.UserID == userID {
6076+
continue
6077+
}
6078+
newMembers = append(newMembers, member)
6079+
}
6080+
q.groupMembers = newMembers
6081+
6082+
return nil
6083+
}
6084+
60866085
func (q *FakeQuerier) RevokeDBCryptKey(_ context.Context, activeKeyDigest string) error {
60876086
q.mutex.Lock()
60886087
defer q.mutex.Unlock()

coderd/database/dbmetrics/dbmetrics.go

+14-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/dbmock/dbmock.go

+29-14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/dump.sql

+4-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
DROP INDEX organizations_single_default_org;
2+
ALTER TABLE organizations DROP COLUMN is_default;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
-- This migration is intended to maintain the existing behavior of single org
2+
-- deployments, while allowing for multi-org deployments. By default, this organization
3+
-- will be used when no organization is specified.
4+
ALTER TABLE organizations ADD COLUMN is_default BOOLEAN NOT NULL DEFAULT FALSE;
5+
6+
-- Only 1 org should ever be set to is_default.
7+
create unique index organizations_single_default_org on organizations (is_default)
8+
where is_default = true;
9+
10+
UPDATE
11+
organizations
12+
SET
13+
is_default = true
14+
WHERE
15+
-- The first organization created will be the default.
16+
id = (SELECT id FROM organizations ORDER BY organizations.created_at ASC LIMIT 1 );

coderd/database/models.go

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/querier.go

+2-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)