[MM-33908] Resync LDAP Groups to Teams and Channels (#17372)

* add includeRemovedMembers flag

* fix API call in client4.go

* remove check for 'since'

* add comments

* run make app-layers

* re-run CI tests

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
Max Erenberg
2021-05-04 12:37:36 -04:00
коммит произвёл GitHub
родитель 3e5b0a7d7f
Коммит 9f4902e188
20 изменённых файлов: 262 добавлений и 143 удалений

Просмотреть файл

@@ -60,6 +60,13 @@ type AppIface interface {
// The result can be used, for example, to determine the set of users who would be removed from a channel if the
// channel were group-constrained with the given groups.
ChannelMembersMinusGroupMembers(channelID string, groupIDs []string, page, perPage int) ([]*model.UserWithGroups, int64, *model.AppError)
// ChannelMembersToAdd returns a slice of UserChannelIDPair that need newly created memberships
// based on the groups configurations. The returned list can be optionally scoped to a single given channel.
//
// Typically since will be the last successful group sync time.
// If includeRemovedMembers is true, then channel members who left or were removed from the channel will
// be included; otherwise, they will be excluded.
ChannelMembersToAdd(since int64, channelID *string, includeRemovedMembers bool) ([]*model.UserChannelIDPair, *model.AppError)
// CheckProviderAttributes returns the empty string if the patch can be applied without
// overriding attributes set by the user's login provider; otherwise, the name of the offending
// field is returned.
@@ -79,7 +86,9 @@ type AppIface interface {
CreateDefaultChannels(teamID string) ([]*model.Channel, *model.AppError)
// CreateDefaultMemberships adds users to teams and channels based on their group memberships and how those groups
// are configured to sync with teams and channels for group members on or after the given timestamp.
CreateDefaultMemberships(since int64) error
// If includeRemovedMembers is true, then members who left or were removed from a team/channel will
// be re-added; otherwise, they will not be re-added.
CreateDefaultMemberships(since int64, includeRemovedMembers bool) error
// CreateGuest creates a guest and sets several fields of the returned User struct to
// their zero values.
CreateGuest(user *model.User) (*model.User, *model.AppError)
@@ -305,12 +314,16 @@ type AppIface interface {
// status to away if needed. Used by the WS to set status to away if an 'online' device disconnects
// while an 'away' device is still connected
SetStatusLastActivityAt(userID string, activityAt int64)
// SyncLdap starts an LDAP sync job.
// If includeRemovedMembers is true, then members who left or were removed from a team/channel will
// be re-added; otherwise, they will not be re-added.
SyncLdap(includeRemovedMembers bool)
// SyncPlugins synchronizes the plugins installed locally
// with the plugin bundles available in the file store.
SyncPlugins() *model.AppError
// SyncRolesAndMembership updates the SchemeAdmin status and membership of all of the members of the given
// syncable.
SyncRolesAndMembership(syncableID string, syncableType model.GroupSyncableType)
SyncRolesAndMembership(syncableID string, syncableType model.GroupSyncableType, includeRemovedMembers bool)
// SyncSyncableRoles updates the SchemeAdmin field value of the given syncable's members based on the configuration of
// the member's group memberships and the configuration of those groups to the syncable. This method should only
// be invoked on group-synced (aka group-constrained) syncables.
@@ -321,6 +334,13 @@ type AppIface interface {
// The result can be used, for example, to determine the set of users who would be removed from a team if the team
// were group-constrained with the given groups.
TeamMembersMinusGroupMembers(teamID string, groupIDs []string, page, perPage int) ([]*model.UserWithGroups, int64, *model.AppError)
// TeamMembersToAdd returns a slice of UserTeamIDPair that need newly created memberships
// based on the groups configurations. The returned list can be optionally scoped to a single given team.
//
// Typically since will be the last successful group sync time.
// If includeRemovedMembers is true, then team members who left or were removed from the team will
// be included; otherwise, they will be excluded.
TeamMembersToAdd(since int64, teamID *string, includeRemovedMembers bool) ([]*model.UserTeamIDPair, *model.AppError)
// This function migrates the default built in roles from code/config to the database.
DoAdvancedPermissionsMigration()
// This function zip's up all the files in fileDatas array and then saves it to the directory specified with the specified zip file name
@@ -408,7 +428,6 @@ type AppIface interface {
BulkImport(fileReader io.Reader, dryRun bool, workers int) (*model.AppError, int)
BulkImportWithPath(fileReader io.Reader, dryRun bool, workers int, importPath string) (*model.AppError, int)
CancelJob(jobId string) *model.AppError
ChannelMembersToAdd(since int64, channelID *string) ([]*model.UserChannelIDPair, *model.AppError)
ChannelMembersToRemove(teamID *string) ([]*model.ChannelMember, *model.AppError)
CheckAndSendUserLimitWarningEmails() *model.AppError
CheckCanInviteToSharedChannel(channelId string) error
@@ -1014,10 +1033,8 @@ type AppIface interface {
SwitchEmailToOAuth(w http.ResponseWriter, r *http.Request, email, password, code, service string) (string, *model.AppError)
SwitchLdapToEmail(ldapPassword, code, email, newPassword string) (string, *model.AppError)
SwitchOAuthToEmail(email, password, requesterId string) (string, *model.AppError)
SyncLdap()
SyncPluginsActiveState()
T(translationID string, args ...interface{}) string
TeamMembersToAdd(since int64, teamID *string) ([]*model.UserTeamIDPair, *model.AppError)
TeamMembersToRemove(teamID *string) ([]*model.TeamMember, *model.AppError)
TelemetryId() string
TestElasticsearch(cfg *model.Config) *model.AppError

Просмотреть файл

@@ -399,8 +399,14 @@ func (a *App) DeleteGroupSyncable(groupID string, syncableID string, syncableTyp
return gs, nil
}
func (a *App) TeamMembersToAdd(since int64, teamID *string) ([]*model.UserTeamIDPair, *model.AppError) {
userTeams, err := a.Srv().Store.Group().TeamMembersToAdd(since, teamID)
// TeamMembersToAdd returns a slice of UserTeamIDPair that need newly created memberships
// based on the groups configurations. The returned list can be optionally scoped to a single given team.
//
// Typically since will be the last successful group sync time.
// If includeRemovedMembers is true, then team members who left or were removed from the team will
// be included; otherwise, they will be excluded.
func (a *App) TeamMembersToAdd(since int64, teamID *string, includeRemovedMembers bool) ([]*model.UserTeamIDPair, *model.AppError) {
userTeams, err := a.Srv().Store.Group().TeamMembersToAdd(since, teamID, includeRemovedMembers)
if err != nil {
return nil, model.NewAppError("TeamMembersToAdd", "app.select_error", nil, err.Error(), http.StatusInternalServerError)
}
@@ -408,8 +414,14 @@ func (a *App) TeamMembersToAdd(since int64, teamID *string) ([]*model.UserTeamID
return userTeams, nil
}
func (a *App) ChannelMembersToAdd(since int64, channelID *string) ([]*model.UserChannelIDPair, *model.AppError) {
userChannels, err := a.Srv().Store.Group().ChannelMembersToAdd(since, channelID)
// ChannelMembersToAdd returns a slice of UserChannelIDPair that need newly created memberships
// based on the groups configurations. The returned list can be optionally scoped to a single given channel.
//
// Typically since will be the last successful group sync time.
// If includeRemovedMembers is true, then channel members who left or were removed from the channel will
// be included; otherwise, they will be excluded.
func (a *App) ChannelMembersToAdd(since int64, channelID *string, includeRemovedMembers bool) ([]*model.UserChannelIDPair, *model.AppError) {
userChannels, err := a.Srv().Store.Group().ChannelMembersToAdd(since, channelID, includeRemovedMembers)
if err != nil {
return nil, model.NewAppError("ChannelMembersToAdd", "app.select_error", nil, err.Error(), http.StatusInternalServerError)
}

Просмотреть файл

@@ -13,12 +13,15 @@ import (
"github.com/mattermost/mattermost-server/v5/shared/mlog"
)
func (a *App) SyncLdap() {
// SyncLdap starts an LDAP sync job.
// If includeRemovedMembers is true, then members who left or were removed from a team/channel will
// be re-added; otherwise, they will not be re-added.
func (a *App) SyncLdap(includeRemovedMembers bool) {
a.Srv().Go(func() {
if license := a.Srv().License(); license != nil && *license.Features.LDAP && *a.Config().LdapSettings.EnableSync {
if ldapI := a.Ldap(); ldapI != nil {
ldapI.StartSynchronizeJob(false)
ldapI.StartSynchronizeJob(false, includeRemovedMembers)
} else {
mlog.Error("Not executing ldap sync because ldap is not available")
}

Просмотреть файл

@@ -1054,7 +1054,7 @@ func (a *OpenTracingAppLayer) ChannelMembersMinusGroupMembers(channelID string,
return resultVar0, resultVar1, resultVar2
}
func (a *OpenTracingAppLayer) ChannelMembersToAdd(since int64, channelID *string) ([]*model.UserChannelIDPair, *model.AppError) {
func (a *OpenTracingAppLayer) ChannelMembersToAdd(since int64, channelID *string, includeRemovedMembers bool) ([]*model.UserChannelIDPair, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.ChannelMembersToAdd")
@@ -1066,7 +1066,7 @@ func (a *OpenTracingAppLayer) ChannelMembersToAdd(since int64, channelID *string
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.ChannelMembersToAdd(since, channelID)
resultVar0, resultVar1 := a.app.ChannelMembersToAdd(since, channelID, includeRemovedMembers)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
@@ -1878,7 +1878,7 @@ func (a *OpenTracingAppLayer) CreateDefaultChannels(teamID string) ([]*model.Cha
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) CreateDefaultMemberships(since int64) error {
func (a *OpenTracingAppLayer) CreateDefaultMemberships(since int64, includeRemovedMembers bool) error {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateDefaultMemberships")
@@ -1890,7 +1890,7 @@ func (a *OpenTracingAppLayer) CreateDefaultMemberships(since int64) error {
}()
defer span.Finish()
resultVar0 := a.app.CreateDefaultMemberships(since)
resultVar0 := a.app.CreateDefaultMemberships(since, includeRemovedMembers)
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))
@@ -15239,7 +15239,7 @@ func (a *OpenTracingAppLayer) SwitchOAuthToEmail(email string, password string,
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) SyncLdap() {
func (a *OpenTracingAppLayer) SyncLdap(includeRemovedMembers bool) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SyncLdap")
@@ -15251,7 +15251,7 @@ func (a *OpenTracingAppLayer) SyncLdap() {
}()
defer span.Finish()
a.app.SyncLdap()
a.app.SyncLdap(includeRemovedMembers)
}
func (a *OpenTracingAppLayer) SyncPlugins() *model.AppError {
@@ -15291,7 +15291,7 @@ func (a *OpenTracingAppLayer) SyncPluginsActiveState() {
a.app.SyncPluginsActiveState()
}
func (a *OpenTracingAppLayer) SyncRolesAndMembership(syncableID string, syncableType model.GroupSyncableType) {
func (a *OpenTracingAppLayer) SyncRolesAndMembership(syncableID string, syncableType model.GroupSyncableType, includeRemovedMembers bool) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SyncRolesAndMembership")
@@ -15303,7 +15303,7 @@ func (a *OpenTracingAppLayer) SyncRolesAndMembership(syncableID string, syncable
}()
defer span.Finish()
a.app.SyncRolesAndMembership(syncableID, syncableType)
a.app.SyncRolesAndMembership(syncableID, syncableType, includeRemovedMembers)
}
func (a *OpenTracingAppLayer) SyncSyncableRoles(syncableID string, syncableType model.GroupSyncableType) *model.AppError {
@@ -15350,7 +15350,7 @@ func (a *OpenTracingAppLayer) TeamMembersMinusGroupMembers(teamID string, groupI
return resultVar0, resultVar1, resultVar2
}
func (a *OpenTracingAppLayer) TeamMembersToAdd(since int64, teamID *string) ([]*model.UserTeamIDPair, *model.AppError) {
func (a *OpenTracingAppLayer) TeamMembersToAdd(since int64, teamID *string, includeRemovedMembers bool) ([]*model.UserTeamIDPair, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.TeamMembersToAdd")
@@ -15362,7 +15362,7 @@ func (a *OpenTracingAppLayer) TeamMembersToAdd(since int64, teamID *string) ([]*
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.TeamMembersToAdd(since, teamID)
resultVar0, resultVar1 := a.app.TeamMembersToAdd(since, teamID, includeRemovedMembers)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))

Просмотреть файл

@@ -15,8 +15,10 @@ import (
// createDefaultChannelMemberships adds users to channels based on their group memberships and how those groups are
// configured to sync with channels for group members on or after the given timestamp. If a channelID is given
// only that channel's members are created. If channelID is nil all channel memberships are created.
func (a *App) createDefaultChannelMemberships(since int64, channelID *string) error {
channelMembers, appErr := a.ChannelMembersToAdd(since, channelID)
// If includeRemovedMembers is true, then channel members who left or were removed from the channel will
// be re-added; otherwise, they will not be re-added.
func (a *App) createDefaultChannelMemberships(since int64, channelID *string, includeRemovedMembers bool) error {
channelMembers, appErr := a.ChannelMembersToAdd(since, channelID, includeRemovedMembers)
if appErr != nil {
return appErr
}
@@ -78,8 +80,10 @@ func (a *App) createDefaultChannelMemberships(since int64, channelID *string) er
// createDefaultTeamMemberships adds users to teams based on their group memberships and how those groups are
// configured to sync with teams for group members on or after the given timestamp. If a teamID is given
// only that team's members are created. If teamID is nil all team memberships are created.
func (a *App) createDefaultTeamMemberships(since int64, teamID *string) error {
teamMembers, appErr := a.TeamMembersToAdd(since, teamID)
// If includeRemovedMembers is true, then team members who left or were removed from the team will
// be re-added; otherwise, they will not be re-added.
func (a *App) createDefaultTeamMemberships(since int64, teamID *string, includeRemovedMembers bool) error {
teamMembers, appErr := a.TeamMembersToAdd(since, teamID, includeRemovedMembers)
if appErr != nil {
return appErr
}
@@ -108,13 +112,15 @@ func (a *App) createDefaultTeamMemberships(since int64, teamID *string) error {
// CreateDefaultMemberships adds users to teams and channels based on their group memberships and how those groups
// are configured to sync with teams and channels for group members on or after the given timestamp.
func (a *App) CreateDefaultMemberships(since int64) error {
err := a.createDefaultTeamMemberships(since, nil)
// If includeRemovedMembers is true, then members who left or were removed from a team/channel will
// be re-added; otherwise, they will not be re-added.
func (a *App) CreateDefaultMemberships(since int64, includeRemovedMembers bool) error {
err := a.createDefaultTeamMemberships(since, nil, includeRemovedMembers)
if err != nil {
return err
}
err = a.createDefaultChannelMemberships(since, nil)
err = a.createDefaultChannelMemberships(since, nil, includeRemovedMembers)
if err != nil {
return err
}
@@ -226,7 +232,7 @@ func (a *App) SyncSyncableRoles(syncableID string, syncableType model.GroupSynca
// SyncRolesAndMembership updates the SchemeAdmin status and membership of all of the members of the given
// syncable.
func (a *App) SyncRolesAndMembership(syncableID string, syncableType model.GroupSyncableType) {
func (a *App) SyncRolesAndMembership(syncableID string, syncableType model.GroupSyncableType, includeRemovedMembers bool) {
a.SyncSyncableRoles(syncableID, syncableType)
lastJob, _ := a.Srv().Store.Job().GetNewestJobByStatusAndType(model.JOB_STATUS_SUCCESS, model.JOB_TYPE_LDAP_SYNC)
@@ -237,11 +243,11 @@ func (a *App) SyncRolesAndMembership(syncableID string, syncableType model.Group
switch syncableType {
case model.GroupSyncableTypeTeam:
a.createDefaultTeamMemberships(since, &syncableID)
a.createDefaultTeamMemberships(since, &syncableID, includeRemovedMembers)
a.deleteGroupConstrainedTeamMemberships(&syncableID)
a.ClearTeamMembersCache(syncableID)
case model.GroupSyncableTypeChannel:
a.createDefaultChannelMemberships(since, &syncableID)
a.createDefaultChannelMemberships(since, &syncableID, includeRemovedMembers)
a.deleteGroupConstrainedChannelMemberships(&syncableID)
a.ClearChannelMembersCache(syncableID)
}

Просмотреть файл

@@ -104,7 +104,7 @@ func TestCreateDefaultMemberships(t *testing.T) {
t.Errorf("test groupmember not created: %s", err.Error())
}
pErr := th.App.CreateDefaultMemberships(0)
pErr := th.App.CreateDefaultMemberships(0, false)
if pErr != nil {
t.Errorf("faild to populate syncables: %s", pErr.Error())
}
@@ -174,7 +174,7 @@ func TestCreateDefaultMemberships(t *testing.T) {
}
// Sync everything after syncable was created (proving that team updates trigger re-sync)
pErr = th.App.CreateDefaultMemberships(scientistGroupMember.CreateAt + 1)
pErr = th.App.CreateDefaultMemberships(scientistGroupMember.CreateAt+1, false)
if pErr != nil {
t.Errorf("faild to populate syncables: %s", pErr.Error())
}
@@ -217,7 +217,7 @@ func TestCreateDefaultMemberships(t *testing.T) {
}
// Sync everything after syncable was created (proving that channel updates trigger re-sync)
pErr = th.App.CreateDefaultMemberships(scientistGroupMember.CreateAt + 1)
pErr = th.App.CreateDefaultMemberships(scientistGroupMember.CreateAt+1, false)
if pErr != nil {
t.Errorf("faild to populate syncables: %s", pErr.Error())
}
@@ -242,7 +242,7 @@ func TestCreateDefaultMemberships(t *testing.T) {
}
// Even re-syncing from the beginning doesn't re-add to channel or team
pErr = th.App.CreateDefaultMemberships(0)
pErr = th.App.CreateDefaultMemberships(0, false)
if pErr != nil {
t.Errorf("faild to populate syncables: %s", pErr.Error())
}
@@ -283,7 +283,7 @@ func TestCreateDefaultMemberships(t *testing.T) {
t.Errorf("error updating group syncable: %s", err.Error())
}
pErr = th.App.CreateDefaultMemberships(0)
pErr = th.App.CreateDefaultMemberships(0, false)
if pErr != nil {
t.Errorf("faild to populate syncables: %s", pErr.Error())
}
@@ -304,7 +304,7 @@ func TestCreateDefaultMemberships(t *testing.T) {
t.Errorf("error permanently deleting channelmemberhistory: %s", nErr.Error())
}
pErr = th.App.CreateDefaultMemberships(scienceChannelGroupSyncable.UpdateAt)
pErr = th.App.CreateDefaultMemberships(scienceChannelGroupSyncable.UpdateAt, false)
if pErr != nil {
t.Errorf("failed to populate syncables: %s", pErr.Error())
}
@@ -320,7 +320,7 @@ func TestCreateDefaultMemberships(t *testing.T) {
t.Errorf("error permanently deleting channelmemberhistory: %s", nErr.Error())
}
pErr = th.App.CreateDefaultMemberships(scienceChannelGroupSyncable.UpdateAt)
pErr = th.App.CreateDefaultMemberships(scienceChannelGroupSyncable.UpdateAt, false)
if pErr != nil {
t.Errorf("failed to populate syncables: %s", pErr.Error())
}
@@ -360,7 +360,7 @@ func TestCreateDefaultMemberships(t *testing.T) {
_, err = th.App.UpsertGroupSyncable(model.NewGroupChannel(scienceGroup.Id, restrictedChannel.Id, true))
require.Nil(t, err)
pErr = th.App.CreateDefaultMemberships(0)
pErr = th.App.CreateDefaultMemberships(0, false)
require.NoError(t, pErr)
// Ensure only the restricted user was added to both the team and channel