@@ -65,13 +65,17 @@ func (s AGPLIDPSync) SyncGroups(ctx context.Context, db database.Store, user dat
65
65
}
66
66
67
67
// Figure out which organizations the user is a member of.
68
+ // The "Everyone" group is always included, so we can infer organization
69
+ // membership via the groups the user is in.
68
70
userOrgs := make (map [uuid.UUID ][]database.GetGroupsRow )
69
71
for _ , g := range userGroups {
70
72
g := g
71
73
userOrgs [g .Group .OrganizationID ] = append (userOrgs [g .Group .OrganizationID ], g )
72
74
}
73
75
74
76
// For each org, we need to fetch the sync settings
77
+ // This loop also handles any legacy settings for the default
78
+ // organization.
75
79
orgSettings := make (map [uuid.UUID ]GroupSyncSettings )
76
80
for orgID := range userOrgs {
77
81
orgResolver := s .Manager .OrganizationResolver (tx , orgID )
@@ -97,16 +101,23 @@ func (s AGPLIDPSync) SyncGroups(ctx context.Context, db database.Store, user dat
97
101
orgSettings [orgID ] = * settings
98
102
}
99
103
100
- // collect all diffs to do 1 sql update for all orgs
104
+ // groupIDsToAdd & groupIDsToRemove are the final group differences
105
+ // needed to be applied to user. The loop below will iterate over all
106
+ // organizations the user is in, and determine the diffs.
107
+ // The diffs are applied as a batch sql query, rather than each
108
+ // organization having to execute a query.
101
109
groupIDsToAdd := make ([]uuid.UUID , 0 )
102
110
groupIDsToRemove := make ([]uuid.UUID , 0 )
103
111
// For each org, determine which groups the user should land in
104
112
for orgID , settings := range orgSettings {
105
113
if settings .Field == "" {
106
114
// No group sync enabled for this org, so do nothing.
115
+ // The user can remain in their groups for this org.
107
116
continue
108
117
}
109
118
119
+ // expectedGroups is the set of groups the IDP expects the
120
+ // user to be a member of.
110
121
expectedGroups , err := settings .ParseClaims (orgID , params .MergedClaims )
111
122
if err != nil {
112
123
s .Logger .Debug (ctx , "failed to parse claims for groups" ,
@@ -117,7 +128,7 @@ func (s AGPLIDPSync) SyncGroups(ctx context.Context, db database.Store, user dat
117
128
// Unsure where to raise this error on the UI or database.
118
129
continue
119
130
}
120
- // Everyone group is always implied.
131
+ // Everyone group is always implied, so include it .
121
132
expectedGroups = append (expectedGroups , ExpectedGroup {
122
133
OrganizationID : orgID ,
123
134
GroupID : & orgID ,
@@ -134,6 +145,7 @@ func (s AGPLIDPSync) SyncGroups(ctx context.Context, db database.Store, user dat
134
145
GroupName : & f .Group .Name ,
135
146
}
136
147
})
148
+
137
149
add , remove := slice .SymmetricDifferenceFunc (existingGroupsTyped , expectedGroups , func (a , b ExpectedGroup ) bool {
138
150
// Must match
139
151
if a .OrganizationID != b .OrganizationID {
@@ -150,10 +162,10 @@ func (s AGPLIDPSync) SyncGroups(ctx context.Context, db database.Store, user dat
150
162
})
151
163
152
164
for _ , r := range remove {
153
- // This should never happen. All group removals come from the
154
- // existing set, which come from the db. All groups from the
155
- // database have IDs. This code is purely defensive.
156
165
if r .GroupID == nil {
166
+ // This should never happen. All group removals come from the
167
+ // existing set, which come from the db. All groups from the
168
+ // database have IDs. This code is purely defensive.
157
169
detail := "user:" + user .Username
158
170
if r .GroupName != nil {
159
171
detail += fmt .Sprintf (" from group %s" , * r .GroupName )
@@ -166,6 +178,11 @@ func (s AGPLIDPSync) SyncGroups(ctx context.Context, db database.Store, user dat
166
178
// HandleMissingGroups will add the new groups to the org if
167
179
// the settings specify. It will convert all group names into uuids
168
180
// for easier assignment.
181
+ // TODO: This code should be batched at the end of the for loop.
182
+ // Optimizing this is being pushed because if AutoCreate is disabled,
183
+ // this code will only add cost on the first login for each user.
184
+ // AutoCreate is usually disabled for large deployments.
185
+ // For small deployments, this is less of a problem.
169
186
assignGroups , err := settings .HandleMissingGroups (ctx , tx , orgID , add )
170
187
if err != nil {
171
188
return xerrors .Errorf ("handle missing groups: %w" , err )
@@ -174,6 +191,8 @@ func (s AGPLIDPSync) SyncGroups(ctx context.Context, db database.Store, user dat
174
191
groupIDsToAdd = append (groupIDsToAdd , assignGroups ... )
175
192
}
176
193
194
+ // ApplyGroupDifference will take the total adds and removes, and apply
195
+ // them.
177
196
err = s .ApplyGroupDifference (ctx , tx , user , groupIDsToAdd , groupIDsToRemove )
178
197
if err != nil {
179
198
return xerrors .Errorf ("apply group difference: %w" , err )
@@ -190,8 +209,6 @@ func (s AGPLIDPSync) SyncGroups(ctx context.Context, db database.Store, user dat
190
209
191
210
// ApplyGroupDifference will add and remove the user from the specified groups.
192
211
func (s AGPLIDPSync ) ApplyGroupDifference (ctx context.Context , tx database.Store , user database.User , add []uuid.UUID , removeIDs []uuid.UUID ) error {
193
- // Always do group removal before group add. This way if there is an error,
194
- // we error on the underprivileged side.
195
212
if len (removeIDs ) > 0 {
196
213
removedGroupIDs , err := tx .RemoveUserFromGroups (ctx , database.RemoveUserFromGroupsParams {
197
214
UserID : user .ID ,
0 commit comments