Skip to content

Commit a839e44

Browse files
committed
Don't remove system admins from channels even if they "shouldn't" be in them
1 parent 5ec58f4 commit a839e44

File tree

3 files changed

+67
-2
lines changed

3 files changed

+67
-2
lines changed

server/syncablesync/mattermostservice.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,24 @@ func (m *Mattermost) FetchGroupsAndSyncables(ctx context.Context) ([]Group, erro
420420
return groups, nil
421421
}
422422

423+
func (m *Mattermost) UnremovableUserIDs(ctx context.Context) ([]string, error) {
424+
// For Mattermost, just use 'is system admin' as a proxy for 'should not be automatically removed from channels they shouldn't be in'.
425+
// Note that if someone is promoted to a system admin at the same time as they would be removed from channels, then they will be removed in _that_ sync run.
426+
srh := &mattermostSystemRoleHandler{api: m.API}
427+
rms, err := srh.FetchRoster(ctx, SyncableTarget{
428+
Type: mattermostSyncableTypeSystemRole,
429+
ID: "system_admin",
430+
})
431+
if err != nil {
432+
return nil, err
433+
}
434+
out := make([]string, len(rms))
435+
for n, rm := range rms {
436+
out[n] = rm.UserID
437+
}
438+
return out, nil
439+
}
440+
423441
func (m *Mattermost) getSystemBot(ctx context.Context) (*model.User, error) {
424442
l := ctxlog.FromContext(ctx)
425443
u, appErr := m.API.GetUserByUsername(model.BotSystemBotUsername)

server/syncablesync/syncablesync.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ import (
1313
"slices"
1414

1515
"github.com/lukegb/mattermost-plugin-uffd/server/ctxlog"
16+
"github.com/lukegb/mattermost-plugin-uffd/server/stringset"
1617
)
1718

1819
type ServiceAPI interface {
1920
SortSyncableTargets([]SyncableTarget)
2021
SyncableHandlers() map[SyncableType]SyncableHandler
2122
FetchGroupsAndSyncables(context.Context) ([]Group, error)
23+
UnremovableUserIDs(context.Context) ([]string, error)
2224
}
2325

2426
type Engine struct {
@@ -89,7 +91,7 @@ func (e *Engine) FullSync(ctx context.Context) error {
8991

9092
groups, err := e.API.FetchGroupsAndSyncables(ctx)
9193
if err != nil {
92-
return fmt.Errorf("fetching list of groups to sync: %v", err)
94+
return fmt.Errorf("fetching list of groups to sync: %w", err)
9395
}
9496
l.WithField("groups", groups).Debug("found groups")
9597

@@ -102,6 +104,13 @@ func (e *Engine) FullSync(ctx context.Context) error {
102104
}
103105
}
104106

107+
// Users which should not be removed from channels that they 'shouldn't be in'.
108+
unremovableUsersList, err := e.API.UnremovableUserIDs(ctx)
109+
if err != nil {
110+
return fmt.Errorf("fetching list of 'unremovable' user IDs: %w", err)
111+
}
112+
unremovableUsers := stringset.FromSlice(unremovableUsersList)
113+
105114
sortedSyncableTarget := slices.Collect(maps.Keys(syncablesToGroups))
106115
e.API.SortSyncableTargets(sortedSyncableTarget)
107116

@@ -152,7 +161,12 @@ func (e *Engine) FullSync(ctx context.Context) error {
152161
if !addOnly {
153162
for uid, gotRM := range gotRosterMap {
154163
if _, ok := wantRosterMap[uid]; !ok {
155-
membersToDelete = append(membersToDelete, gotRM)
164+
if unremovableUsers.Contains(uid) {
165+
gotRM.IsAdmin = false
166+
membersToUpdate = append(membersToUpdate, gotRM)
167+
} else {
168+
membersToDelete = append(membersToDelete, gotRM)
169+
}
156170
}
157171
}
158172
} else {

server/syncablesync/syncablesync_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const dummySyncableType = SyncableType("flarp")
1515
type dummyService struct {
1616
groups []Group
1717
syncableMembers map[SyncableTarget][]RosterMember
18+
unremovable []string
1819
}
1920

2021
var _ ServiceAPI = ((*dummyService)(nil))
@@ -31,6 +32,10 @@ func (s *dummyService) FetchGroupsAndSyncables(context.Context) ([]Group, error)
3132
return s.groups, nil
3233
}
3334

35+
func (s *dummyService) UnremovableUserIDs(context.Context) ([]string, error) {
36+
return s.unremovable, nil
37+
}
38+
3439
type dummyServiceHandler struct {
3540
s *dummyService
3641
}
@@ -219,6 +224,34 @@ func TestFullSync(t *testing.T) {
219224
UserID: "user2",
220225
}},
221226
},
227+
}, {
228+
name: "doesn't remove 'unremovable' members (but does demote them)",
229+
s: &dummyService{
230+
unremovable: []string{"user1", "user2"},
231+
groups: []Group{{
232+
ID: "group",
233+
Name: "group name",
234+
// no Members
235+
Syncables: []Syncable{{
236+
Target: dummyST,
237+
}},
238+
}},
239+
syncableMembers: map[SyncableTarget][]RosterMember{
240+
dummyST: {{
241+
UserID: "user1",
242+
IsAdmin: true,
243+
}, {
244+
UserID: "user2",
245+
}},
246+
},
247+
},
248+
wantSyncableMembers: map[SyncableTarget][]RosterMember{
249+
dummyST: {{
250+
UserID: "user1",
251+
}, {
252+
UserID: "user2",
253+
}},
254+
},
222255
}}
223256
for _, tc := range tcs {
224257
t.Run(tc.name, func(t *testing.T) {

0 commit comments

Comments
 (0)