mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
[MM-51554] Make LDAP sync job more resilient against errors (#25496)
This commit is contained in:
parent
9ecb3e20c8
commit
bac05a273d
@ -97,7 +97,7 @@ type AppIface interface {
|
|||||||
// are configured to sync with teams and channels for group members on or after the given timestamp.
|
// are configured to sync with teams and channels for group members on or after the given timestamp.
|
||||||
// If includeRemovedMembers is true, then members who left or were removed from a team/channel will
|
// 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.
|
// be re-added; otherwise, they will not be re-added.
|
||||||
CreateDefaultMemberships(c request.CTX, params model.CreateDefaultMembershipParams) error
|
CreateDefaultMemberships(rctx request.CTX, params model.CreateDefaultMembershipParams) error
|
||||||
// CreateGuest creates a guest and sets several fields of the returned User struct to
|
// CreateGuest creates a guest and sets several fields of the returned User struct to
|
||||||
// their zero values.
|
// their zero values.
|
||||||
CreateGuest(c request.CTX, user *model.User) (*model.User, *model.AppError)
|
CreateGuest(c request.CTX, user *model.User) (*model.User, *model.AppError)
|
||||||
@ -121,7 +121,7 @@ type AppIface interface {
|
|||||||
DeleteChannelScheme(c request.CTX, channel *model.Channel) (*model.Channel, *model.AppError)
|
DeleteChannelScheme(c request.CTX, channel *model.Channel) (*model.Channel, *model.AppError)
|
||||||
// DeleteGroupConstrainedMemberships deletes team and channel memberships of users who aren't members of the allowed
|
// DeleteGroupConstrainedMemberships deletes team and channel memberships of users who aren't members of the allowed
|
||||||
// groups of all group-constrained teams and channels.
|
// groups of all group-constrained teams and channels.
|
||||||
DeleteGroupConstrainedMemberships(c request.CTX) error
|
DeleteGroupConstrainedMemberships(rctx request.CTX) error
|
||||||
// DeletePersistentNotification stops the persistent notifications.
|
// DeletePersistentNotification stops the persistent notifications.
|
||||||
DeletePersistentNotification(c request.CTX, post *model.Post) *model.AppError
|
DeletePersistentNotification(c request.CTX, post *model.Post) *model.AppError
|
||||||
// DeletePublicKey will delete plugin public key from the config.
|
// DeletePublicKey will delete plugin public key from the config.
|
||||||
@ -355,13 +355,13 @@ type AppIface interface {
|
|||||||
SyncPlugins() *model.AppError
|
SyncPlugins() *model.AppError
|
||||||
// SyncRolesAndMembership updates the SchemeAdmin status and membership of all of the members of the given
|
// SyncRolesAndMembership updates the SchemeAdmin status and membership of all of the members of the given
|
||||||
// syncable.
|
// syncable.
|
||||||
SyncRolesAndMembership(c request.CTX, syncableID string, syncableType model.GroupSyncableType, includeRemovedMembers bool)
|
SyncRolesAndMembership(rctx request.CTX, syncableID string, syncableType model.GroupSyncableType, includeRemovedMembers bool)
|
||||||
// SyncSharedChannel forces a shared channel to send any changed content to all remote clusters.
|
// SyncSharedChannel forces a shared channel to send any changed content to all remote clusters.
|
||||||
SyncSharedChannel(channelID string) error
|
SyncSharedChannel(channelID string) error
|
||||||
// SyncSyncableRoles updates the SchemeAdmin field value of the given syncable's members based on the configuration of
|
// 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
|
// 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.
|
// be invoked on group-synced (aka group-constrained) syncables.
|
||||||
SyncSyncableRoles(syncableID string, syncableType model.GroupSyncableType) *model.AppError
|
SyncSyncableRoles(rctx request.CTX, syncableID string, syncableType model.GroupSyncableType) *model.AppError
|
||||||
// TeamMembersMinusGroupMembers returns the set of users on the given team minus the set of users in the given
|
// TeamMembersMinusGroupMembers returns the set of users on the given team minus the set of users in the given
|
||||||
// groups.
|
// groups.
|
||||||
//
|
//
|
||||||
|
@ -2192,7 +2192,7 @@ func (a *OpenTracingAppLayer) CreateCommandWebhook(commandID string, args *model
|
|||||||
return resultVar0, resultVar1
|
return resultVar0, resultVar1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *OpenTracingAppLayer) CreateDefaultMemberships(c request.CTX, params model.CreateDefaultMembershipParams) error {
|
func (a *OpenTracingAppLayer) CreateDefaultMemberships(rctx request.CTX, params model.CreateDefaultMembershipParams) error {
|
||||||
origCtx := a.ctx
|
origCtx := a.ctx
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateDefaultMemberships")
|
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateDefaultMemberships")
|
||||||
|
|
||||||
@ -2204,7 +2204,7 @@ func (a *OpenTracingAppLayer) CreateDefaultMemberships(c request.CTX, params mod
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
resultVar0 := a.app.CreateDefaultMemberships(c, params)
|
resultVar0 := a.app.CreateDefaultMemberships(rctx, params)
|
||||||
|
|
||||||
if resultVar0 != nil {
|
if resultVar0 != nil {
|
||||||
span.LogFields(spanlog.Error(resultVar0))
|
span.LogFields(spanlog.Error(resultVar0))
|
||||||
@ -3346,7 +3346,7 @@ func (a *OpenTracingAppLayer) DeleteGroup(groupID string) (*model.Group, *model.
|
|||||||
return resultVar0, resultVar1
|
return resultVar0, resultVar1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *OpenTracingAppLayer) DeleteGroupConstrainedMemberships(c request.CTX) error {
|
func (a *OpenTracingAppLayer) DeleteGroupConstrainedMemberships(rctx request.CTX) error {
|
||||||
origCtx := a.ctx
|
origCtx := a.ctx
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.DeleteGroupConstrainedMemberships")
|
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.DeleteGroupConstrainedMemberships")
|
||||||
|
|
||||||
@ -3358,7 +3358,7 @@ func (a *OpenTracingAppLayer) DeleteGroupConstrainedMemberships(c request.CTX) e
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
resultVar0 := a.app.DeleteGroupConstrainedMemberships(c)
|
resultVar0 := a.app.DeleteGroupConstrainedMemberships(rctx)
|
||||||
|
|
||||||
if resultVar0 != nil {
|
if resultVar0 != nil {
|
||||||
span.LogFields(spanlog.Error(resultVar0))
|
span.LogFields(spanlog.Error(resultVar0))
|
||||||
@ -17217,7 +17217,7 @@ func (a *OpenTracingAppLayer) SyncPlugins() *model.AppError {
|
|||||||
return resultVar0
|
return resultVar0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *OpenTracingAppLayer) SyncRolesAndMembership(c request.CTX, syncableID string, syncableType model.GroupSyncableType, includeRemovedMembers bool) {
|
func (a *OpenTracingAppLayer) SyncRolesAndMembership(rctx request.CTX, syncableID string, syncableType model.GroupSyncableType, includeRemovedMembers bool) {
|
||||||
origCtx := a.ctx
|
origCtx := a.ctx
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SyncRolesAndMembership")
|
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SyncRolesAndMembership")
|
||||||
|
|
||||||
@ -17229,7 +17229,7 @@ func (a *OpenTracingAppLayer) SyncRolesAndMembership(c request.CTX, syncableID s
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
a.app.SyncRolesAndMembership(c, syncableID, syncableType, includeRemovedMembers)
|
a.app.SyncRolesAndMembership(rctx, syncableID, syncableType, includeRemovedMembers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *OpenTracingAppLayer) SyncSharedChannel(channelID string) error {
|
func (a *OpenTracingAppLayer) SyncSharedChannel(channelID string) error {
|
||||||
@ -17254,7 +17254,7 @@ func (a *OpenTracingAppLayer) SyncSharedChannel(channelID string) error {
|
|||||||
return resultVar0
|
return resultVar0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *OpenTracingAppLayer) SyncSyncableRoles(syncableID string, syncableType model.GroupSyncableType) *model.AppError {
|
func (a *OpenTracingAppLayer) SyncSyncableRoles(rctx request.CTX, syncableID string, syncableType model.GroupSyncableType) *model.AppError {
|
||||||
origCtx := a.ctx
|
origCtx := a.ctx
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SyncSyncableRoles")
|
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SyncSyncableRoles")
|
||||||
|
|
||||||
@ -17266,7 +17266,7 @@ func (a *OpenTracingAppLayer) SyncSyncableRoles(syncableID string, syncableType
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
resultVar0 := a.app.SyncSyncableRoles(syncableID, syncableType)
|
resultVar0 := a.app.SyncSyncableRoles(rctx, syncableID, syncableType)
|
||||||
|
|
||||||
if resultVar0 != nil {
|
if resultVar0 != nil {
|
||||||
span.LogFields(spanlog.Error(resultVar0))
|
span.LogFields(spanlog.Error(resultVar0))
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/mattermost/mattermost/server/public/model"
|
"github.com/mattermost/mattermost/server/public/model"
|
||||||
"github.com/mattermost/mattermost/server/public/shared/mlog"
|
"github.com/mattermost/mattermost/server/public/shared/mlog"
|
||||||
"github.com/mattermost/mattermost/server/public/shared/request"
|
"github.com/mattermost/mattermost/server/public/shared/request"
|
||||||
@ -18,67 +19,68 @@ import (
|
|||||||
// only that channel's members are created. If channelID is nil all channel memberships are created.
|
// only that channel's members are created. If channelID is nil all channel memberships are created.
|
||||||
// If includeRemovedMembers is true, then channel members who left or were removed from the channel will
|
// 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.
|
// be re-added; otherwise, they will not be re-added.
|
||||||
func (a *App) createDefaultChannelMemberships(c request.CTX, params model.CreateDefaultMembershipParams) error {
|
func (a *App) createDefaultChannelMemberships(rctx request.CTX, params model.CreateDefaultMembershipParams) error {
|
||||||
channelMembers, appErr := a.ChannelMembersToAdd(params.Since, params.ScopedChannelID, params.ReAddRemovedMembers)
|
channelMembers, appErr := a.ChannelMembersToAdd(params.Since, params.ScopedChannelID, params.ReAddRemovedMembers)
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
return appErr
|
return appErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var multiErr *multierror.Error
|
||||||
for _, userChannel := range channelMembers {
|
for _, userChannel := range channelMembers {
|
||||||
if params.ScopedUserID != nil && *params.ScopedUserID != userChannel.UserID {
|
if params.ScopedUserID != nil && *params.ScopedUserID != userChannel.UserID {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
channel, err := a.GetChannel(c, userChannel.ChannelID)
|
|
||||||
|
logger := rctx.Logger().With(
|
||||||
|
mlog.String("user_id", userChannel.UserID),
|
||||||
|
mlog.String("channel_id", userChannel.ChannelID),
|
||||||
|
)
|
||||||
|
|
||||||
|
channel, err := a.GetChannel(rctx, userChannel.ChannelID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
multiErr = multierror.Append(multiErr, fmt.Errorf("failed to get channel for default channel membership: %w", err))
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
tmem, err := a.GetTeamMember(c, channel.TeamId, userChannel.UserID)
|
tmem, err := a.GetTeamMember(rctx, channel.TeamId, userChannel.UserID)
|
||||||
if err != nil && err.Id != "app.team.get_member.missing.app_error" {
|
if err != nil && err.Id != "app.team.get_member.missing.app_error" {
|
||||||
return err
|
multiErr = multierror.Append(multiErr, fmt.Errorf("failed to get member for default channel membership: %w", err))
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// First add user to team
|
// First add user to team
|
||||||
if tmem == nil {
|
if tmem == nil {
|
||||||
_, err = a.AddTeamMember(c, channel.TeamId, userChannel.UserID)
|
_, err = a.AddTeamMember(rctx, channel.TeamId, userChannel.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Id == "api.team.join_user_to_team.allowed_domains.app_error" {
|
if err.Id == "api.team.join_user_to_team.allowed_domains.app_error" {
|
||||||
c.Logger().Info("User not added to channel - the domain associated with the user is not in the list of allowed team domains",
|
logger.Info(
|
||||||
mlog.String("user_id", userChannel.UserID),
|
"User not added to channel - the domain associated with the user is not in the list of allowed team domains",
|
||||||
mlog.String("channel_id", userChannel.ChannelID),
|
|
||||||
mlog.String("team_id", channel.TeamId),
|
mlog.String("team_id", channel.TeamId),
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
multiErr = multierror.Append(multiErr, fmt.Errorf("failed to add team member for default channel membership: %w", err))
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return err
|
logger.Info("Added channel member for default channel membership")
|
||||||
}
|
|
||||||
c.Logger().Info("added teammember",
|
|
||||||
mlog.String("user_id", userChannel.UserID),
|
|
||||||
mlog.String("team_id", channel.TeamId),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = a.AddChannelMember(c, userChannel.UserID, channel, ChannelMemberOpts{
|
_, err = a.AddChannelMember(rctx, userChannel.UserID, channel, ChannelMemberOpts{
|
||||||
SkipTeamMemberIntegrityCheck: true,
|
SkipTeamMemberIntegrityCheck: true,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Id == "api.channel.add_user.to.channel.failed.deleted.app_error" {
|
if err.Id == "api.channel.add_user.to.channel.failed.deleted.app_error" {
|
||||||
c.Logger().Info("Not adding user to channel because they have already left the team",
|
logger.Info("Not adding user to channel because they have already left the team")
|
||||||
mlog.String("user_id", userChannel.UserID),
|
|
||||||
mlog.String("channel_id", userChannel.ChannelID),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
return err
|
multiErr = multierror.Append(multiErr, fmt.Errorf("failed to add channel member for default channel membership: %w", err))
|
||||||
}
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Logger().Info("added channelmember",
|
logger.Info("Added channel member for default channel membership")
|
||||||
mlog.String("user_id", userChannel.UserID),
|
|
||||||
mlog.String("channel_id", userChannel.ChannelID),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return multiErr.ErrorOrNil()
|
||||||
}
|
}
|
||||||
|
|
||||||
// createDefaultTeamMemberships adds users to teams based on their group memberships and how those groups are
|
// createDefaultTeamMemberships adds users to teams based on their group memberships and how those groups are
|
||||||
@ -86,48 +88,50 @@ func (a *App) createDefaultChannelMemberships(c request.CTX, params model.Create
|
|||||||
// only that team's members are created. If teamID is nil all team memberships are created.
|
// only that team's members are created. If teamID is nil all team memberships are created.
|
||||||
// If includeRemovedMembers is true, then team members who left or were removed from the team will
|
// 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.
|
// be re-added; otherwise, they will not be re-added.
|
||||||
func (a *App) createDefaultTeamMemberships(c request.CTX, params model.CreateDefaultMembershipParams) error {
|
func (a *App) createDefaultTeamMemberships(rctx request.CTX, params model.CreateDefaultMembershipParams) error {
|
||||||
teamMembers, appErr := a.TeamMembersToAdd(params.Since, params.ScopedTeamID, params.ReAddRemovedMembers)
|
teamMembers, appErr := a.TeamMembersToAdd(params.Since, params.ScopedTeamID, params.ReAddRemovedMembers)
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
return appErr
|
return appErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var multiErr *multierror.Error
|
||||||
for _, userTeam := range teamMembers {
|
for _, userTeam := range teamMembers {
|
||||||
if params.ScopedUserID != nil && *params.ScopedUserID != userTeam.UserID {
|
if params.ScopedUserID != nil && *params.ScopedUserID != userTeam.UserID {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
_, err := a.AddTeamMember(c, userTeam.TeamID, userTeam.UserID)
|
|
||||||
|
logger := rctx.Logger().With(
|
||||||
|
mlog.String("user_id", userTeam.UserID),
|
||||||
|
mlog.String("team_id", userTeam.TeamID),
|
||||||
|
)
|
||||||
|
|
||||||
|
_, err := a.AddTeamMember(rctx, userTeam.TeamID, userTeam.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Id == "api.team.join_user_to_team.allowed_domains.app_error" {
|
if err.Id == "api.team.join_user_to_team.allowed_domains.app_error" {
|
||||||
c.Logger().Info("User not added to team - the domain associated with the user is not in the list of allowed team domains",
|
logger.Info("User not added to team - the domain associated with the user is not in the list of allowed team domains")
|
||||||
mlog.String("user_id", userTeam.UserID),
|
} else {
|
||||||
mlog.String("team_id", userTeam.TeamID),
|
multiErr = multierror.Append(multiErr, fmt.Errorf("failed to add team member for default team membership: %w", err))
|
||||||
)
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return err
|
|
||||||
|
logger.Info("Added team member for default team membership")
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Logger().Info("added teammember",
|
return multiErr.ErrorOrNil()
|
||||||
mlog.String("user_id", userTeam.UserID),
|
|
||||||
mlog.String("team_id", userTeam.TeamID),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateDefaultMemberships adds users to teams and channels based on their group memberships and how those groups
|
// 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.
|
// are configured to sync with teams and channels for group members on or after the given timestamp.
|
||||||
// If includeRemovedMembers is true, then members who left or were removed from a team/channel will
|
// 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.
|
// be re-added; otherwise, they will not be re-added.
|
||||||
func (a *App) CreateDefaultMemberships(c request.CTX, params model.CreateDefaultMembershipParams) error {
|
func (a *App) CreateDefaultMemberships(rctx request.CTX, params model.CreateDefaultMembershipParams) error {
|
||||||
err := a.createDefaultTeamMemberships(c, params)
|
err := a.createDefaultTeamMemberships(rctx, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.createDefaultChannelMemberships(c, params)
|
err = a.createDefaultChannelMemberships(rctx, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -137,13 +141,13 @@ func (a *App) CreateDefaultMemberships(c request.CTX, params model.CreateDefault
|
|||||||
|
|
||||||
// DeleteGroupConstrainedMemberships deletes team and channel memberships of users who aren't members of the allowed
|
// DeleteGroupConstrainedMemberships deletes team and channel memberships of users who aren't members of the allowed
|
||||||
// groups of all group-constrained teams and channels.
|
// groups of all group-constrained teams and channels.
|
||||||
func (a *App) DeleteGroupConstrainedMemberships(c request.CTX) error {
|
func (a *App) DeleteGroupConstrainedMemberships(rctx request.CTX) error {
|
||||||
err := a.deleteGroupConstrainedChannelMemberships(c, nil)
|
err := a.deleteGroupConstrainedChannelMemberships(rctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.deleteGroupConstrainedTeamMemberships(c, nil)
|
err = a.deleteGroupConstrainedTeamMemberships(rctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -154,66 +158,75 @@ func (a *App) DeleteGroupConstrainedMemberships(c request.CTX) error {
|
|||||||
// deleteGroupConstrainedTeamMemberships deletes team memberships of users who aren't members of the allowed
|
// deleteGroupConstrainedTeamMemberships deletes team memberships of users who aren't members of the allowed
|
||||||
// groups of the given group-constrained team. If a teamID is given then the procedure is scoped to the given team,
|
// groups of the given group-constrained team. If a teamID is given then the procedure is scoped to the given team,
|
||||||
// if teamID is nil then the procedure affects all teams.
|
// if teamID is nil then the procedure affects all teams.
|
||||||
func (a *App) deleteGroupConstrainedTeamMemberships(c request.CTX, teamID *string) error {
|
func (a *App) deleteGroupConstrainedTeamMemberships(rctx request.CTX, teamID *string) error {
|
||||||
teamMembers, appErr := a.TeamMembersToRemove(teamID)
|
teamMembers, appErr := a.TeamMembersToRemove(teamID)
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
return appErr
|
return appErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var multiErr *multierror.Error
|
||||||
for _, userTeam := range teamMembers {
|
for _, userTeam := range teamMembers {
|
||||||
err := a.RemoveUserFromTeam(c, userTeam.TeamId, userTeam.UserId, "")
|
logger := rctx.Logger().With(
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Logger().Info("removed teammember",
|
|
||||||
mlog.String("user_id", userTeam.UserId),
|
mlog.String("user_id", userTeam.UserId),
|
||||||
mlog.String("team_id", userTeam.TeamId),
|
mlog.String("team_id", userTeam.TeamId),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
err := a.RemoveUserFromTeam(rctx, userTeam.TeamId, userTeam.UserId, "")
|
||||||
|
if err != nil {
|
||||||
|
multiErr = multierror.Append(multiErr, fmt.Errorf("failed to remove team member for default team membership: %w", err))
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
logger.Info("Removed team member for group constrained team membership")
|
||||||
|
}
|
||||||
|
|
||||||
|
return multiErr.ErrorOrNil()
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteGroupConstrainedChannelMemberships deletes channel memberships of users who aren't members of the allowed
|
// deleteGroupConstrainedChannelMemberships deletes channel memberships of users who aren't members of the allowed
|
||||||
// groups of the given group-constrained channel. If a channelID is given then the procedure is scoped to the given team,
|
// groups of the given group-constrained channel. If a channelID is given then the procedure is scoped to the given team,
|
||||||
// if channelID is nil then the procedure affects all teams.
|
// if channelID is nil then the procedure affects all teams.
|
||||||
func (a *App) deleteGroupConstrainedChannelMemberships(c request.CTX, channelID *string) error {
|
func (a *App) deleteGroupConstrainedChannelMemberships(rctx request.CTX, channelID *string) error {
|
||||||
channelMembers, appErr := a.ChannelMembersToRemove(channelID)
|
channelMembers, appErr := a.ChannelMembersToRemove(channelID)
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
return appErr
|
return appErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var multiErr *multierror.Error
|
||||||
for _, userChannel := range channelMembers {
|
for _, userChannel := range channelMembers {
|
||||||
channel, err := a.GetChannel(c, userChannel.ChannelId)
|
logger := rctx.Logger().With(
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.RemoveUserFromChannel(c, userChannel.UserId, "", channel)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
a.Log().Info("removed channelmember",
|
|
||||||
mlog.String("user_id", userChannel.UserId),
|
mlog.String("user_id", userChannel.UserId),
|
||||||
mlog.String("channel_id", channel.Id),
|
mlog.String("channel_id", userChannel.ChannelId),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
channel, err := a.GetChannel(rctx, userChannel.ChannelId)
|
||||||
|
if err != nil {
|
||||||
|
multiErr = multierror.Append(multiErr, fmt.Errorf("failed to get channel for group constrained channel membership: %w", err))
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
err = a.RemoveUserFromChannel(rctx, userChannel.UserId, "", channel)
|
||||||
|
if err != nil {
|
||||||
|
multiErr = multierror.Append(multiErr, fmt.Errorf("failed to remove channel member for group constrained channel membership: %w", err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("Removed channel member for group constrained channel membership")
|
||||||
|
}
|
||||||
|
|
||||||
|
return multiErr.ErrorOrNil()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SyncSyncableRoles updates the SchemeAdmin field value of the given syncable's members based on the configuration of
|
// 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
|
// 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.
|
// be invoked on group-synced (aka group-constrained) syncables.
|
||||||
func (a *App) SyncSyncableRoles(syncableID string, syncableType model.GroupSyncableType) *model.AppError {
|
func (a *App) SyncSyncableRoles(rctx request.CTX, syncableID string, syncableType model.GroupSyncableType) *model.AppError {
|
||||||
permittedAdmins, err := a.Srv().Store().Group().PermittedSyncableAdmins(syncableID, syncableType)
|
permittedAdmins, err := a.Srv().Store().Group().PermittedSyncableAdmins(syncableID, syncableType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model.NewAppError("SyncSyncableRoles", "app.select_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
return model.NewAppError("SyncSyncableRoles", "app.select_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
a.Log().Info(
|
rctx.Logger().Info(
|
||||||
fmt.Sprintf("Permitted admins for %s", syncableType),
|
fmt.Sprintf("Permitted admins for %s", syncableType),
|
||||||
mlog.String(strings.ToLower(fmt.Sprintf("%s_id", syncableType)), syncableID),
|
mlog.String(strings.ToLower(fmt.Sprintf("%s_id", syncableType)), syncableID),
|
||||||
mlog.Array("permitted_admins", permittedAdmins),
|
mlog.Array("permitted_admins", permittedAdmins),
|
||||||
@ -239,8 +252,8 @@ func (a *App) SyncSyncableRoles(syncableID string, syncableType model.GroupSynca
|
|||||||
|
|
||||||
// SyncRolesAndMembership updates the SchemeAdmin status and membership of all of the members of the given
|
// SyncRolesAndMembership updates the SchemeAdmin status and membership of all of the members of the given
|
||||||
// syncable.
|
// syncable.
|
||||||
func (a *App) SyncRolesAndMembership(c request.CTX, syncableID string, syncableType model.GroupSyncableType, includeRemovedMembers bool) {
|
func (a *App) SyncRolesAndMembership(rctx request.CTX, syncableID string, syncableType model.GroupSyncableType, includeRemovedMembers bool) {
|
||||||
a.SyncSyncableRoles(syncableID, syncableType)
|
a.SyncSyncableRoles(rctx, syncableID, syncableType)
|
||||||
|
|
||||||
lastJob, _ := a.Srv().Store().Job().GetNewestJobByStatusAndType(model.JobStatusSuccess, model.JobTypeLdapSync)
|
lastJob, _ := a.Srv().Store().Job().GetNewestJobByStatusAndType(model.JobStatusSuccess, model.JobTypeLdapSync)
|
||||||
var since int64
|
var since int64
|
||||||
@ -253,17 +266,25 @@ func (a *App) SyncRolesAndMembership(c request.CTX, syncableID string, syncableT
|
|||||||
switch syncableType {
|
switch syncableType {
|
||||||
case model.GroupSyncableTypeTeam:
|
case model.GroupSyncableTypeTeam:
|
||||||
params.ScopedTeamID = &syncableID
|
params.ScopedTeamID = &syncableID
|
||||||
a.createDefaultTeamMemberships(c, params)
|
if err := a.createDefaultTeamMemberships(rctx, params); err != nil {
|
||||||
a.deleteGroupConstrainedTeamMemberships(c, &syncableID)
|
rctx.Logger().Warn("Error creating default team memberships", mlog.Err(err))
|
||||||
|
}
|
||||||
|
if err := a.deleteGroupConstrainedTeamMemberships(rctx, &syncableID); err != nil {
|
||||||
|
rctx.Logger().Warn("Error deleting group constrained team memberships", mlog.Err(err))
|
||||||
|
}
|
||||||
if err := a.ClearTeamMembersCache(syncableID); err != nil {
|
if err := a.ClearTeamMembersCache(syncableID); err != nil {
|
||||||
c.Logger().Warn("Error clearing team members cache", mlog.Err(err))
|
rctx.Logger().Warn("Error clearing team members cache", mlog.Err(err))
|
||||||
}
|
}
|
||||||
case model.GroupSyncableTypeChannel:
|
case model.GroupSyncableTypeChannel:
|
||||||
params.ScopedChannelID = &syncableID
|
params.ScopedChannelID = &syncableID
|
||||||
a.createDefaultChannelMemberships(c, params)
|
if err := a.createDefaultChannelMemberships(rctx, params); err != nil {
|
||||||
a.deleteGroupConstrainedChannelMemberships(c, &syncableID)
|
rctx.Logger().Warn("Error creating default channel memberships", mlog.Err(err))
|
||||||
if err := a.ClearChannelMembersCache(c, syncableID); err != nil {
|
}
|
||||||
c.Logger().Warn("Error clearing channel members cache", mlog.Err(err))
|
if err := a.deleteGroupConstrainedChannelMemberships(rctx, &syncableID); err != nil {
|
||||||
|
rctx.Logger().Warn("Error deleting group constrained team memberships", mlog.Err(err))
|
||||||
|
}
|
||||||
|
if err := a.ClearChannelMembersCache(rctx, syncableID); err != nil {
|
||||||
|
rctx.Logger().Warn("Error clearing channel members cache", mlog.Err(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,18 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost/server/public/model"
|
"github.com/mattermost/mattermost/server/public/model"
|
||||||
|
"github.com/mattermost/mattermost/server/v8/channels/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//nolint:govet // The setup code leads to a lot of variable shadowing.
|
||||||
func TestCreateDefaultMemberships(t *testing.T) {
|
func TestCreateDefaultMemberships(t *testing.T) {
|
||||||
th := Setup(t).InitBasic()
|
th := Setup(t).InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
@ -105,7 +110,7 @@ func TestCreateDefaultMemberships(t *testing.T) {
|
|||||||
|
|
||||||
pErr := th.App.CreateDefaultMemberships(th.Context, model.CreateDefaultMembershipParams{Since: 0, ReAddRemovedMembers: false})
|
pErr := th.App.CreateDefaultMemberships(th.Context, model.CreateDefaultMembershipParams{Since: 0, ReAddRemovedMembers: false})
|
||||||
if pErr != nil {
|
if pErr != nil {
|
||||||
t.Errorf("faild to populate syncables: %s", pErr.Error())
|
t.Errorf("failed to populate syncables: %s", pErr.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Singer should be in team and channel
|
// Singer should be in team and channel
|
||||||
@ -175,7 +180,7 @@ func TestCreateDefaultMemberships(t *testing.T) {
|
|||||||
// Sync everything after syncable was created (proving that team updates trigger re-sync)
|
// Sync everything after syncable was created (proving that team updates trigger re-sync)
|
||||||
pErr = th.App.CreateDefaultMemberships(th.Context, model.CreateDefaultMembershipParams{Since: scientistGroupMember.CreateAt + 1, ReAddRemovedMembers: false})
|
pErr = th.App.CreateDefaultMemberships(th.Context, model.CreateDefaultMembershipParams{Since: scientistGroupMember.CreateAt + 1, ReAddRemovedMembers: false})
|
||||||
if pErr != nil {
|
if pErr != nil {
|
||||||
t.Errorf("faild to populate syncables: %s", pErr.Error())
|
t.Errorf("failed to populate syncables: %s", pErr.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scientist should be in team but not the channel
|
// Scientist should be in team but not the channel
|
||||||
@ -218,7 +223,7 @@ func TestCreateDefaultMemberships(t *testing.T) {
|
|||||||
// Sync everything after syncable was created (proving that channel updates trigger re-sync)
|
// Sync everything after syncable was created (proving that channel updates trigger re-sync)
|
||||||
pErr = th.App.CreateDefaultMemberships(th.Context, model.CreateDefaultMembershipParams{Since: scientistGroupMember.CreateAt + 1, ReAddRemovedMembers: false})
|
pErr = th.App.CreateDefaultMemberships(th.Context, model.CreateDefaultMembershipParams{Since: scientistGroupMember.CreateAt + 1, ReAddRemovedMembers: false})
|
||||||
if pErr != nil {
|
if pErr != nil {
|
||||||
t.Errorf("faild to populate syncables: %s", pErr.Error())
|
t.Errorf("failed to populate syncables: %s", pErr.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
expected = 1
|
expected = 1
|
||||||
@ -243,7 +248,7 @@ func TestCreateDefaultMemberships(t *testing.T) {
|
|||||||
// Even re-syncing from the beginning doesn't re-add to channel or team
|
// Even re-syncing from the beginning doesn't re-add to channel or team
|
||||||
pErr = th.App.CreateDefaultMemberships(th.Context, model.CreateDefaultMembershipParams{Since: 0, ReAddRemovedMembers: false})
|
pErr = th.App.CreateDefaultMemberships(th.Context, model.CreateDefaultMembershipParams{Since: 0, ReAddRemovedMembers: false})
|
||||||
if pErr != nil {
|
if pErr != nil {
|
||||||
t.Errorf("faild to populate syncables: %s", pErr.Error())
|
t.Errorf("failed to populate syncables: %s", pErr.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Singer should not be in team or channel
|
// Singer should not be in team or channel
|
||||||
@ -284,7 +289,7 @@ func TestCreateDefaultMemberships(t *testing.T) {
|
|||||||
|
|
||||||
pErr = th.App.CreateDefaultMemberships(th.Context, model.CreateDefaultMembershipParams{Since: 0, ReAddRemovedMembers: false})
|
pErr = th.App.CreateDefaultMemberships(th.Context, model.CreateDefaultMembershipParams{Since: 0, ReAddRemovedMembers: false})
|
||||||
if pErr != nil {
|
if pErr != nil {
|
||||||
t.Errorf("faild to populate syncables: %s", pErr.Error())
|
t.Errorf("failed to populate syncables: %s", pErr.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
timeBeforeLeaving := model.GetMillis()
|
timeBeforeLeaving := model.GetMillis()
|
||||||
@ -482,6 +487,46 @@ func TestCreateDefaultMemberships(t *testing.T) {
|
|||||||
t.Errorf("expected 2 channel member on team1Channel1, got %d", len(team1Channel1Members))
|
t.Errorf("expected 2 channel member on team1Channel1, got %d", len(team1Channel1Members))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("error should contain a information about all users that failed", func(t *testing.T) {
|
||||||
|
user1 := th.CreateUser()
|
||||||
|
_, err = th.App.UpsertGroupMember(scienceGroup.Id, user1.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
user2 := th.CreateUser()
|
||||||
|
_, err = th.App.UpsertGroupMember(scienceGroup.Id, user2.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
store := &mockStore{
|
||||||
|
Store: th.App.Srv().Store(),
|
||||||
|
us: &mokeUserStore{
|
||||||
|
UserStore: th.App.Srv().Store().User(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
require.Nil(t, err)
|
||||||
|
th.App.Srv().platform.Store = store
|
||||||
|
|
||||||
|
nErr = th.App.CreateDefaultMemberships(th.Context, model.CreateDefaultMembershipParams{Since: 0, ReAddRemovedMembers: false})
|
||||||
|
require.Error(t, nErr)
|
||||||
|
assert.ErrorContains(t, nErr, "failed to add team member for default team membership")
|
||||||
|
assert.ErrorContains(t, nErr, user1.Id)
|
||||||
|
assert.ErrorContains(t, nErr, user2.Id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockStore struct {
|
||||||
|
store.Store
|
||||||
|
us store.UserStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fk *mockStore) User() store.UserStore { return fk.us }
|
||||||
|
|
||||||
|
type mokeUserStore struct {
|
||||||
|
store.UserStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func (us *mokeUserStore) Get(_ context.Context, id string) (*model.User, error) {
|
||||||
|
return nil, fmt.Errorf("some error for %s", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteGroupMemberships(t *testing.T) {
|
func TestDeleteGroupMemberships(t *testing.T) {
|
||||||
@ -601,10 +646,10 @@ func TestSyncSyncableRoles(t *testing.T) {
|
|||||||
_, err = th.App.UpdateGroupSyncable(channelSyncable)
|
_, err = th.App.UpdateGroupSyncable(channelSyncable)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
err = th.App.SyncSyncableRoles(channel.Id, model.GroupSyncableTypeChannel)
|
err = th.App.SyncSyncableRoles(th.Context, channel.Id, model.GroupSyncableTypeChannel)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
err = th.App.SyncSyncableRoles(team.Id, model.GroupSyncableTypeTeam)
|
err = th.App.SyncSyncableRoles(th.Context, team.Id, model.GroupSyncableTypeTeam)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
for _, user := range []*model.User{user1, user2} {
|
for _, user := range []*model.User{user1, user2} {
|
||||||
|
@ -7774,10 +7774,6 @@
|
|||||||
"id": "ent.ldap.save_user.username_exists.ldap_app_error",
|
"id": "ent.ldap.save_user.username_exists.ldap_app_error",
|
||||||
"translation": "An account with that username already exists. Please contact your Administrator."
|
"translation": "An account with that username already exists. Please contact your Administrator."
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"id": "ent.ldap.syncronize.delete_group_constained_memberships",
|
|
||||||
"translation": "error deleting team or channel memberships"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "ent.ldap.syncronize.get_all.app_error",
|
"id": "ent.ldap.syncronize.get_all.app_error",
|
||||||
"translation": "Unable to get all users using AD/LDAP."
|
"translation": "Unable to get all users using AD/LDAP."
|
||||||
|
Loading…
Reference in New Issue
Block a user