[MM-60083] export: do not export DMs/GMs if users are deleted (#28854)

Этот коммит содержится в:
Ibrahim Serdar Acikgoz
2024-12-19 16:47:52 +01:00
коммит произвёл GitHub
родитель 09add85a51
Коммит e76786278b
5 изменённых файлов: 92 добавлений и 12 удалений

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

@@ -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{}{}