[MM-60083] export: do not export DMs/GMs if users are deleted (#28854)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
09add85a51
Коммит
e76786278b
@@ -121,7 +121,11 @@ func (a *App) BulkExport(ctx request.CTX, writer io.Writer, outPath string, job
|
||||
}
|
||||
}
|
||||
|
||||
if job != nil && job.Data == nil {
|
||||
if job == nil {
|
||||
job = &model.Job{
|
||||
Data: make(model.StringMap),
|
||||
}
|
||||
} else if job.Data == nil {
|
||||
job.Data = make(model.StringMap)
|
||||
}
|
||||
|
||||
@@ -944,14 +948,38 @@ func (a *App) exportAllDirectChannels(ctx request.CTX, job *model.Job, writer io
|
||||
for _, channel := range channels {
|
||||
afterId = channel.Id
|
||||
|
||||
// Skip deleted.
|
||||
if channel.DeleteAt != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip if there are no active members in the channel
|
||||
if len(channel.Members) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip deleted.
|
||||
if channel.DeleteAt != 0 {
|
||||
continue
|
||||
// Skip if the channel member structure is not intact
|
||||
switch channel.Type {
|
||||
case model.ChannelTypeGroup:
|
||||
groupMembers := make([]string, len(channel.Members))
|
||||
for i, m := range channel.Members {
|
||||
groupMembers[i] = m.UserId
|
||||
}
|
||||
if channel.Name != model.GetGroupNameFromUserIds(groupMembers) {
|
||||
// this is the case of a group channel when other user is permanently deleted
|
||||
// we skip this channel and inform by logging it.
|
||||
job.Data["skipped_direct_channels"] = job.Data["skipped_direct_channels"] + "," + channel.Id
|
||||
ctx.Logger().Warn("Skipping group channels with partially deleted members", mlog.String("channel_id", channel.Id))
|
||||
continue
|
||||
}
|
||||
case model.ChannelTypeDirect:
|
||||
if _, u2 := channel.GetBothUsersForDM(); u2 != "" && len(channel.Members) == 1 {
|
||||
// this is the case of a direct channel when other user is permanently deleted
|
||||
// we skip this channel and inform by logging it.
|
||||
job.Data["skipped_direct_channels"] = job.Data["skipped_direct_channels"] + "," + channel.Id
|
||||
ctx.Logger().Info("Skipping direct channel with one active member", mlog.String("channel_id", channel.Id), mlog.String("user_id", channel.Members[0].UserId))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
favoritedBy, err := a.buildFavoritedByList(channel.Id)
|
||||
@@ -1070,6 +1098,7 @@ func (a *App) exportAllDirectPosts(ctx request.CTX, job *model.Job, writer io.Wr
|
||||
cnt += len(posts)
|
||||
updateJobProgress(ctx.Logger(), a.Srv().Store(), job, "direct_posts_exported", cnt)
|
||||
|
||||
channelsToSkip := model.SliceToMapKey(strings.Split(job.Data["skipped_direct_channels"], ",")...)
|
||||
for _, post := range posts {
|
||||
afterId = post.Id
|
||||
postProcessCount++
|
||||
@@ -1079,6 +1108,10 @@ func (a *App) exportAllDirectPosts(ctx request.CTX, job *model.Job, writer io.Wr
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := channelsToSkip[post.ChannelId]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle attachments.
|
||||
var postAttachments []imports.AttachmentImportData
|
||||
var err *model.AppError
|
||||
|
||||
@@ -355,6 +355,50 @@ func TestExportDMChannel(t *testing.T) {
|
||||
require.NoError(t, nErr)
|
||||
assert.Empty(t, channels)
|
||||
})
|
||||
|
||||
t.Run("Should not export DM channel if other user is permanently deleted", func(t *testing.T) {
|
||||
th1 := Setup(t).InitBasic()
|
||||
defer th1.TearDown()
|
||||
|
||||
// Create a DM Channel with another user
|
||||
dmc1 := th1.CreateDmChannel(th1.BasicUser2)
|
||||
th1.CreatePost(dmc1)
|
||||
|
||||
// Create a DM Channel with self
|
||||
dmc2 := th1.CreateDmChannel(th1.BasicUser)
|
||||
th1.CreatePost(dmc2)
|
||||
|
||||
channels, nErr := th1.App.Srv().Store().Channel().GetAllDirectChannelsForExportAfter(1000, "00000000", false)
|
||||
require.NoError(t, nErr)
|
||||
assert.Equal(t, 2, len(channels))
|
||||
|
||||
// Permanentley delete other user
|
||||
th1.App.PermanentDeleteUser(th1.Context, th1.BasicUser2)
|
||||
|
||||
var b bytes.Buffer
|
||||
err := th1.App.BulkExport(th1.Context, &b, "somePath", nil, model.BulkExportOpts{})
|
||||
require.Nil(t, err)
|
||||
|
||||
th2 := Setup(t).InitBasic()
|
||||
defer th2.TearDown()
|
||||
|
||||
// import the exported channel
|
||||
err, _ = th2.App.BulkImport(th2.Context, &b, nil, false, 5)
|
||||
require.Nil(t, err)
|
||||
|
||||
channels, nErr = th2.App.Srv().Store().Channel().GetAllDirectChannelsForExportAfter(1000, "00000000", false)
|
||||
require.NoError(t, nErr)
|
||||
assert.Equal(t, 1, len(channels))
|
||||
|
||||
// Ensure the posts of the deleted DM channel do not leak to the self-DM channel
|
||||
posts, nErr := th2.App.Srv().Store().Post().GetPosts(model.GetPostsOptions{
|
||||
ChannelId: channels[0].Id,
|
||||
PerPage: 1000,
|
||||
IncludeDeleted: true,
|
||||
}, false, nil)
|
||||
require.NoError(t, nErr)
|
||||
assert.Equal(t, 1, len(posts.Posts))
|
||||
})
|
||||
}
|
||||
|
||||
func TestExportDMChannelToSelf(t *testing.T) {
|
||||
|
||||
@@ -2327,12 +2327,15 @@ func (a *App) importMultipleDirectPostLines(rctx request.CTX, lines []imports.Li
|
||||
return line.LineNumber, model.NewAppError("BulkImport", "app.import.import_direct_post.create_direct_channel.error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
channel = ch
|
||||
} else {
|
||||
} else if len(userIDs) > 2 {
|
||||
ch, err = a.createGroupChannel(rctx, userIDs)
|
||||
if err != nil && err.Id != store.ChannelExistsError {
|
||||
return line.LineNumber, model.NewAppError("BulkImport", "app.import.import_direct_post.create_group_channel.error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
channel = ch
|
||||
} else {
|
||||
rctx.Logger().Warn("Not enough users to create a direct channel", mlog.Int("line_number", line.LineNumber))
|
||||
continue
|
||||
}
|
||||
|
||||
user := users[strings.ToLower(*line.DirectPost.User)]
|
||||
|
||||
@@ -40,11 +40,11 @@ const (
|
||||
|
||||
var (
|
||||
performanceReportVersion = semver.MustParse("0.1.0")
|
||||
acceptedPlatforms = sliceToMapKey("linux", "macos", "ios", "android", "windows", "other")
|
||||
acceptedAgents = sliceToMapKey("desktop", "firefox", "chrome", "safari", "edge", "other")
|
||||
acceptedPlatforms = SliceToMapKey("linux", "macos", "ios", "android", "windows", "other")
|
||||
acceptedAgents = SliceToMapKey("desktop", "firefox", "chrome", "safari", "edge", "other")
|
||||
|
||||
AcceptedInteractions = sliceToMapKey("keyboard", "pointer", "other")
|
||||
AcceptedLCPRegions = sliceToMapKey(
|
||||
AcceptedInteractions = SliceToMapKey("keyboard", "pointer", "other")
|
||||
AcceptedLCPRegions = SliceToMapKey(
|
||||
"post",
|
||||
"post_textbox",
|
||||
"channel_sidebar",
|
||||
@@ -56,8 +56,8 @@ var (
|
||||
"modal_content",
|
||||
"other",
|
||||
)
|
||||
AcceptedTrueFalseLabels = sliceToMapKey("true", "false")
|
||||
AcceptedSplashScreenOrigins = sliceToMapKey("root", "team_controller")
|
||||
AcceptedTrueFalseLabels = SliceToMapKey("true", "false")
|
||||
AcceptedSplashScreenOrigins = SliceToMapKey("root", "team_controller")
|
||||
)
|
||||
|
||||
type MetricSample struct {
|
||||
|
||||
@@ -860,7 +860,7 @@ func IsCloud() bool {
|
||||
return os.Getenv("MM_CLOUD_INSTALLATION_ID") != ""
|
||||
}
|
||||
|
||||
func sliceToMapKey(s ...string) map[string]any {
|
||||
func SliceToMapKey(s ...string) map[string]any {
|
||||
m := make(map[string]any)
|
||||
for i := range s {
|
||||
m[s[i]] = struct{}{}
|
||||
|
||||
Ссылка в новой задаче
Block a user